From: John Hodge Date: Tue, 7 Feb 2012 09:25:05 +0000 (+0800) Subject: Sorting source tree a bit X-Git-Tag: rel0.15~789^2~6 X-Git-Url: https://git.ucc.asn.au/?p=tpg%2Facess2.git;a=commitdiff_plain;h=48743e39650eb1ef988380e9d95f27fd40d3a9ce Sorting source tree a bit --- diff --git a/Design Notes/AcessFS.txt b/Design Notes/AcessFS.txt deleted file mode 100644 index 7c74bebd..00000000 --- a/Design Notes/AcessFS.txt +++ /dev/null @@ -1,54 +0,0 @@ -Acess File System -- Database Design - -== Data Strutures == -Blocks are of a size specified in the superblock -- Superblock - > Fixed offset: 1024 bytes -- Field Table - > Offset set in superblock -- Index Table -- Inode Table - -=== Superblock === -struct sSuperblock { - Uint8 Magic[4]; // == '\xACFS'+Version - Uint8 BlockSize; // TrueSize = 2^(7+BlockSize) -}; - -=== Field Table === -struct sFieldTableEntry { - Uint16 Ident; - Uint8 Type; - Uint8 Length; - char Text[]; -} FieldTable[SuperBlock.NFields]; - -=== Index Table == -struct sIndexTableEntry { - Uint16 Field; - Uint16 CheckSum; - Uint32 Block; -} IndexTable[SuperBlock.NFields]; - -=== Index Table entry == -struct { - Uint32 NumEntries; - Uint32 Links[]; -}; - -=== Inode Table === -struct sInodeTable { - -}; - -=== Inode === -struct sInodeEntry { - Uint16 Name; - Uint8 Size; - Uint8 Checksum; - Uint8 data[]; -}; - -Each `sInodeEntry` defines an entry in a "database" - diff --git a/Design Notes/Spinlocks.txt b/Design Notes/Spinlocks.txt deleted file mode 100644 index b4f6b84c..00000000 --- a/Design Notes/Spinlocks.txt +++ /dev/null @@ -1,26 +0,0 @@ -SHORTLOCK() - cli; lock cmpxchg -SHORTREL() - lock and ; sti - - -LONGLOCK() - mov eax, 1 - lock cmpxchg lock.lock, eax - if(eax) { - SHORTLOCK(lock.listLock) - // add to list (linked list, 4 static entries) - SHORTREL(lock.listLock) - for(;;) - { - check owner - mov eax, 1 - lock cmpxchg lock.lock, eax - if(!eax) break; // got lock - Threads_Sleep(); - } - } - -LONGREL() - lock and lock.lock, 0 - pop off front of list, free entry, wake thread diff --git a/Design Notes/VFS - Select.txt b/Design Notes/VFS - Select.txt deleted file mode 100644 index 70e1993c..00000000 --- a/Design Notes/VFS - Select.txt +++ /dev/null @@ -1,25 +0,0 @@ -select() -- Implemented using a wait queue for every file descriptor -- That requires waiting on sockets to be centeralised - -All wait tasks (reads on VTerm, Pipes, PTYs, network sockets) use the kernel -version of select with an inifinite timeout. -They then signal the VFS using their VFS node pointer as a reference - -The VFS function select() -- Maintains a list of processes on each node (if select has been called on that node) -- Each process maybe has a semaphore on it (to use the semaphore code to maintain the process list) - > Could maybe use a mutex instead - - -VFS_Select(int, fd_set* read, fd_set* write, fd_set* except) - -read is the set of sockets that we are waiting to read from -write " " " be able to write to -except " " " for possible errors on - - -Hence, each VFS_Node has three listener lists (or just pointers) -- One for when data is avaliable -- One for when space is avaliable for writing -- One for when an error occurs (closed pipe, etc ...) diff --git a/Design Notes/VTerm.txt b/Design Notes/VTerm.txt deleted file mode 100644 index 6173108b..00000000 --- a/Design Notes/VTerm.txt +++ /dev/null @@ -1,53 +0,0 @@ - -== Read == -- UTF-8 / UCS-4 Character Stream - > Selected with mode call - -== Write == -UTF-8 Emulation Text Mode: -- Emuates a character device - > VT-100/ANSI Control Codes - > Characters/Symbols are sent as UTF-8 - -/* -Native Text Mode: -- NxM 64-bit entries - > UCS-32 Codepoint (if a diacritic is encountered, the previous character is modified) - > 12-bit (16 encoded) Foreground - > 12-bit (16 encoded) Background -*/ - -Framebuffer Graphics: -- WxH 32-bit (3x 8-bit channels) framebuffer -- Write to entire framebuffer - -Accellerated Graphics: -- Command Stream - > Each Node.Write call is a single command - + NOP (-) - + Direct (Uint16 X, Y, W, H, Uint32 Data[]) - + Blit (Uint16 W, H, SrcX, SrxY, DstX, DstY, Uint32 Data[]) - + Fill (Uint16 X, Y, W, H) - + Rect (Uint16 X, Y, W, H) - + Line (Uint16 X, Y, W, H) - + Text (Uint16 X, Y, Size, Font) - + ShowTile (Uint16 ID, Uint16 X, Y) - + DelTile (Uint16 ID) -- Extra IOCtls - + int LoadFont(char *Path) - + UnloadFont(int *ID) - + int MakeTile(struct {Uint16 W, H, Uint32 Data[]} *Img) - + DelTile(int *ID) -- Allow fast switch between Accel/Framebuffer? -- Min Reqd Tile Size 32x32 - > Tiles should be driver emulated if unavaliable by hardware - -3D Graphics: (Can be emulated if not avaliable, or just denied) -- Command Stream - > - + NOP (-) - + FlipBuffer (-) - + LoadTexture(Uint16 ID, W, H, Uint32 Data[]) - + UnloadTexture(Uint16 ID) - + SetTexture(Uint16 ID) - + Triangle (Uint16 Texture, Uint32[3+3+2][3]) diff --git a/Kernel/.gitignore b/Kernel/.gitignore deleted file mode 100644 index 439ce3e7..00000000 --- a/Kernel/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.BuildNum.* diff --git a/Kernel/Doxyfile b/Kernel/Doxyfile deleted file mode 100644 index a474dd56..00000000 --- a/Kernel/Doxyfile +++ /dev/null @@ -1,1510 +0,0 @@ -# Doxyfile 1.5.8 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = Acess2 - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = ../SrcDoc/Kernel - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, -# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, -# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, -# Spanish, Swedish, and Ukrainian. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it parses. -# With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this tag. -# The format is ext=language, where ext is a file extension, and language is one of -# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, -# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = YES - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = YES - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by -# doxygen. The layout file controls the global structure of the generated output files -# in an output format independent way. The create the layout file that represents -# doxygen's defaults, run doxygen with the -l option. You can optionally specify a -# file name after the option, if omitted DoxygenLayout.xml will be used as the name -# of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = . - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 - -FILE_PATTERNS = *.c *.h - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = arch/archdoc.h - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER -# are set, an additional index file will be generated that can be used as input for -# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated -# HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. -# For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's -# filter section matches. -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to FRAME, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are -# probably better off using the HTML help feature. Other possible values -# for this tag are: HIERARCHIES, which will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list; -# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which -# disables this behavior completely. For backwards compatibility with previous -# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE -# respectively. - -GENERATE_TREEVIEW = NONE - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = YES - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. -# This is useful -# if you want to understand what is going on. -# On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = FreeSans - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Options related to the search engine -#--------------------------------------------------------------------------- - -# The SEARCHENGINE tag specifies whether or not a search engine should be -# used. If set to NO the values of all tags below this one will be ignored. - -SEARCHENGINE = NO diff --git a/Kernel/Doxyfile.api b/Kernel/Doxyfile.api deleted file mode 100644 index 6872e55f..00000000 --- a/Kernel/Doxyfile.api +++ /dev/null @@ -1,1775 +0,0 @@ -# Doxyfile 1.7.5.1 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" "). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or sequence of words) that should -# identify the project. Note that if you do not use Doxywizard you need -# to put quotes around the project name if it contains spaces. - -PROJECT_NAME = Acess2 - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer -# a quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = - -# With the PROJECT_LOGO tag one can specify an logo or icon that is -# included in the documentation. The maximum height of the logo should not -# exceed 55 pixels and the maximum width should not exceed 200 pixels. -# Doxygen will copy the logo to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = ../APIDoc - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful if your file system -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this -# tag. The format is ext=language, where ext is a file extension, and language -# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, -# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions -# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also makes the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and -# unions are shown inside the group in which they are included (e.g. using -# @ingroup) instead of on a separate page (for HTML and Man pages) or -# section (for LaTeX and RTF). - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and -# unions with only public data fields will be shown inline in the documentation -# of the scope in which they are defined (i.e. file, namespace, or group -# documentation), provided this scope is documented. If set to NO (the default), -# structs, classes, and unions are shown on a separate page (for HTML and Man -# pages) or section (for LaTeX and RTF). - -#INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = YES - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penalty. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will roughly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = NO - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespaces are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to -# do proper type resolution of all parameters of a function it will reject a -# match between the prototype and the implementation of a member function even -# if there is only one candidate or it is obvious which candidate to choose -# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen -# will still accept a match between prototype and implementation in such cases. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or macro consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and macros in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. The create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files -# containing the references data. This must be a list of .bib files. The -# .bib extension is automatically appended if omitted. Using this command -# requires the bibtex tool to be installed. See also -# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style -# of the bibliography can be controlled using LATEX_BIB_STYLE. - -#CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# The WARN_NO_PARAMDOC option can be enabled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = include/apidoc_mainpage.h \ - include/acess.h \ - include/hal_proc.h \ - include/binary.h \ - include/modules.h \ - include/vfs.h \ - include/vfs_ext.h \ - include/fs_devfs.h \ - include/fs_sysfs.h \ - include/iocache.h \ - arch/archdoc.h \ - include/apidoc/arch_x86.h \ - include/api_drv_common.h \ - include/api_drv_video.h \ - include/api_drv_terminal.h \ - include/api_drv_disk.h \ - include/api_drv_keyboard.h \ - include/api_drv_joystick.h \ - include/api_drv_network.h - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh -# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py -# *.f90 *.f *.for *.vhd *.vhdl - -FILE_PATTERNS = api_drv_*.h - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# Note that relative paths are relative to directory from which doxygen is run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty or if -# non of the patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) -# and it is also possible to disable source filtering for a specific pattern -# using *.ext= (so without naming a filter). This option only has effect when -# FILTER_SOURCE_FILES is enabled. - -FILTER_SOURCE_PATTERNS = - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. Note that when using a custom header you are responsible -# for the proper inclusion of any scripts and style sheets that doxygen -# needs, which is dependent on the configuration options used. -# It is adviced to generate a default header using "doxygen -w html -# header.html footer.html stylesheet.css YourConfigFile" and then modify -# that header. Note that the header is subject to change so you typically -# have to redo this when upgrading to a newer version of doxygen or when -# changing the value of configuration settings such as GENERATE_TREEVIEW! - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that -# the files will be copied as-is; there are no commands or markers available. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the stylesheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. - -HTML_TIMESTAMP = YES - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values -# (range [0,1..20]) that doxygen will group on one line in the generated HTML -# documentation. Note that a value of 0 will completely suppress the enum -# values from appearing in the overview section. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NONE - -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. - -USE_INLINE_TREES = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax -# (see http://www.mathjax.org) which uses client side Javascript for the -# rendering instead of using prerendered bitmaps. Use this if you do not -# have LaTeX installed or if you want to formulas look prettier in the HTML -# output. When enabled you also need to install MathJax separately and -# configure the path to it using the MATHJAX_RELPATH option. - -USE_MATHJAX = NO - -# When MathJax is enabled you need to specify the location relative to the -# HTML output directory using the MATHJAX_RELPATH option. The destination -# directory should contain the MathJax.js script. For instance, if the mathjax -# directory is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the -# mathjax.org site, so you can quickly see the result without installing -# MathJax, but it is strongly recommended to install a local copy of MathJax -# before deployment. - -MATHJAX_RELPATH = http://www.mathjax.org/mathjax - -# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension -# names that should be enabled during MathJax rendering. - -#MATHJAX_EXTENSIONS = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = NO - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a PHP enabled web server instead of at the web client -# using Javascript. Doxygen will generate the search PHP script and index -# file to put on the web server. The advantage of the server -# based approach is that it scales better to large projects and allows -# full text search. The disadvantages are that it is more difficult to setup -# and does not have live searching capabilities. - -SERVER_BASED_SEARCH = NO - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# Note that when enabling USE_PDFLATEX this option is only used for -# generating bitmaps for formulas in the HTML output, but not in the -# Makefile that is written to the output directory. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for -# the generated latex document. The footer should contain everything after -# the last chapter. If it is left blank doxygen will generate a -# standard footer. Notice: only use this tag if you know what you are doing! - -LATEX_FOOTER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include -# source code with syntax highlighting in the LaTeX output. -# Note that which sources are shown also depends on other settings -# such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -# The LATEX_BIB_STYLE tag can be used to specify the style to use for the -# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See -# http://en.wikipedia.org/wiki/BibTeX for more info. - -#LATEX_BIB_STYLE = plain - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. -# This is useful -# if you want to understand what is going on. -# On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# pointed to by INCLUDE_PATH will be searched when a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition that -# overrules the definition found in the source code. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all references to function-like macros -# that are alone on a line, have an all uppercase name, and do not end with a -# semicolon, because these will confuse the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option also works with HAVE_DOT disabled, but it is recommended to -# install and use dot, since it yields more powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is -# allowed to run in parallel. When set to 0 (the default) doxygen will -# base this on the number of processors available in the system. You can set it -# explicitly to a value larger than 0 to get control over the balance -# between CPU load and processing speed. - -DOT_NUM_THREADS = 0 - -# By default doxygen will use the Helvetica font for all dot files that -# doxygen generates. When you want a differently looking font you can specify -# the font name using DOT_FONTNAME. You need to make sure dot is able to find -# the font, which can be done by putting it in a standard location or by setting -# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the -# directory containing the font. - -DOT_FONTNAME = FreeSans - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the Helvetica font. -# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to -# set the path where dot can find it. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will generate a graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are svg, png, jpg, or gif. -# If left blank png will be used. If you choose svg you need to set -# HTML_FILE_EXTENSION to xhtml in order to make the SVG files -# visible in IE 9+ (other browsers do not have this requirement). - -DOT_IMAGE_FORMAT = png - -# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to -# enable generation of interactive SVG images that allow zooming and panning. -# Note that this requires a modern browser other than Internet Explorer. -# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you -# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files -# visible. Older versions of IE do not have SVG support. - -#INTERACTIVE_SVG = NO - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the -# \mscfile command). - -MSCFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/Kernel/DoxygenLayout.xml b/Kernel/DoxygenLayout.xml deleted file mode 100644 index e8faa6d8..00000000 --- a/Kernel/DoxygenLayout.xml +++ /dev/null @@ -1,185 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Kernel/GenSyscalls.php b/Kernel/GenSyscalls.php deleted file mode 100644 index d451d1dd..00000000 --- a/Kernel/GenSyscalls.php +++ /dev/null @@ -1,76 +0,0 @@ -$call) -{ - if($i != $num) { - $lHeader .= "\n"; - $lAsmInc .= "\n"; - } - - $lHeader .= "#define {$call[0]}\t{$num}"; - $lHeader .= "\t// {$num} - {$call[1]}\n"; - - $lAsmInc .= "%define {$call[0]}\t{$num}\t; {$call[1]}\n"; - - - if($i != $num) - $i = $num+1; - else - $i ++; -} -$lHeader .= "#define NUM_SYSCALLS\t$i\n"; -$lHeader .= "#define SYS_DEBUG\t0x100 // 0x100 - Print a debug string\n"; -$lHeader .= "\n"; -$lHeader .= "#ifdef __GNUC__\n"; -$lHeader .= "static const char *cSYSCALL_NAMES[] = {\n\t"; - -$j = 0; -for($i=0;$i<$lMax;$i++) -{ - if(!isset($lSyscalls[$i])) - $lHeader .= "\"\","; - else - $lHeader .= "\"".$lSyscalls[$i][0]."\","; - $j ++; - if($j == 6) { - $lHeader .= "\n\t"; - $j = 0; - } -} -$lHeader .= "\"\"\n};\n" -$lHeader .= "#endif\n"; -$lHeader .= "#endif\n"; - -$fp = fopen("include/syscalls.h", "w"); fwrite($fp, $lHeader); fclose($fp); -$fp = fopen("include/syscalls.inc.asm", "w"); fwrite($fp, $lAsmInc); fclose($fp); - -?> diff --git a/Kernel/GenSyscalls.pl b/Kernel/GenSyscalls.pl deleted file mode 100755 index 487ceca4..00000000 --- a/Kernel/GenSyscalls.pl +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/perl -# -# - -open(FILE, "syscalls.lst"); - -$num = 0; -@calls = (); -while($_ = ) -{ - if(/(\d+)/) - { - $num = $1; - } - elsif(/([A-Z_]+)\s+(.+)/) - { - push @calls, [$num, $1, $2]; - $num ++; - } -} - -close(FILE); - -# C header -open(HEADER, ">include/syscalls.h"); -print HEADER "/* - * Acess2 - * syscalls.h - * - System Call List - * - * NOTE: Generated from Kernel/syscalls.lst - */ -#ifndef _SYSCALLS_H -#define _SYSCALLS_H - -"; - -$lastid = -1; -$i = 0; -foreach my $call (@calls) -{ - print HEADER "#define ", $call->[1], "\t", $call->[0], "\t// ", $call->[2], "\n"; - $i = $call->[0] + 1; -} -print HEADER " -#define NUM_SYSCALLS ",$i," -#define SYS_DEBUG 0x100 - -#ifndef __ASSEMBLER__ -static const char *cSYSCALL_NAMES[] = { -"; - -$lastid = -1; -foreach $call (@calls) -{ - while( $lastid + 1 < $call->[0] ) - { - print HEADER "\t\"\",\n"; - $lastid = $lastid + 1; - } - print HEADER "\t\"", $call->[1], "\",\n"; - $lastid = $lastid + 1; -} -print HEADER " -\t\"\" -}; -#endif - -#endif -"; - -close(HEADER); - -# Assembly Header -open(ASM, ">include/syscalls.inc.asm"); -print ASM "; Acess2 -; System Calls List -; - -"; -foreach $call (@calls) -{ - print ASM "%define ", $call->[1], "\t", $call->[0], "\t ;", $call->[2], "\n"; -} -close(ASM); diff --git a/Kernel/Makefile b/Kernel/Makefile deleted file mode 100644 index d0c8dbf6..00000000 --- a/Kernel/Makefile +++ /dev/null @@ -1,138 +0,0 @@ -# Acess2 Kernel -# Root Makefile -# NOTE: Does some voodoo to allow differing architecture builds to co-exist -# - The built objects and dependency files are suffixed with the arch name -# - The final binary is Acess2..bin - --include ../Makefile.cfg - --include arch/$(ARCHDIR)/Makefile - --include Makefile.BuildNum.$(ARCH) - -ifeq ($(BUILD_NUM),) -BUILD_NUM = 0 -endif - -KERNEL_VERSION = $(ACESS_VERSION) -MAKEDEP = $(CC) -M - -ifeq ($(AS_SUFFIX),) - AS_SUFFIX = S -endif - -ASFLAGS += -D ARCHDIR_IS_$(ARCHDIR)=1 -D PLATFORM_is_$(PLATFORM)=1 -CPPFLAGS += -I./include -I./arch/$(ARCHDIR)/include -D_MODULE_NAME_=\"Kernel\" -CPPFLAGS += -D ARCH=$(ARCH) -D ARCHDIR=$(ARCHDIR) -D PLATFORM=\"$(PLATFORM)\" -D ARCHDIR_IS_$(ARCHDIR)=1 -D PLATFORM_is_$(PLATFORM)=1 -CPPFLAGS += -D KERNEL_VERSION=$(KERNEL_VERSION) -CFLAGS += -Wall -fno-stack-protector -Wstrict-prototypes -g -CFLAGS += -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wuninitialized -CFLAGS += -O3 -LDFLAGS += -T arch/$(ARCHDIR)/link.ld -g - -ifeq ($(PLATFORM),default) - OBJDIR := obj-$(ARCH)/ - #OBJSUFFIX := .$(ARCH) - BIN := ../Acess2.$(ARCH).bin - GZBIN := ../Acess2.$(ARCH).gz -else - OBJDIR := obj-$(ARCH)-$(PLATFORM)/ - #OBJSUFFIX := .$(ARCH)-$(PLATFORM) - BIN := ../Acess2.$(ARCH)-$(PLATFORM).bin - GZBIN := ../Acess2.$(ARCH)-$(PLATFORM).gz -endif - -ifeq ($(DEBUG_BUILD),yes) - LDFLAGS += -g - CFLAGS += -g -endif - -BUILDINFO_OBJ := $(OBJDIR)buildinfo.o$(OBJSUFFIX) -BUILDINFO_SRC := $(OBJDIR)buildinfo.c$(OBJSUFFIX) - -OBJ := $(addprefix arch/$(ARCHDIR)/,$(A_OBJ)) -OBJ += heap.o drvutil.o logging.o debug.o lib.o adt.o time.o -OBJ += messages.o modules.o syscalls.o system.o -OBJ += threads.o mutex.o semaphore.o workqueue.o events.o -OBJ += drv/proc.o drv/fifo.o drv/iocache.o drv/pci.o -OBJ += drv/vterm.o drv/vterm_font.o drv/vterm_vt100.o drv/vterm_output.o drv/vterm_input.o drv/vterm_termbuf.o -OBJ += binary.o bin/elf.o bin/pe.o -OBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/dir.o vfs/io.o vfs/mount.o -OBJ += vfs/memfile.o vfs/nodecache.o vfs/handle.o vfs/select.o vfs/mmap.o -OBJ += vfs/fs/root.o vfs/fs/devfs.o -OBJ += $(addprefix drv/, $(addsuffix .o,$(DRIVERS))) - -OBJ := $(addsuffix $(OBJSUFFIX), $(OBJ)) -OBJ := $(addprefix $(OBJDIR), $(OBJ)) - -MODS += $(addprefix ../Modules/, $(addsuffix .xo.$(ARCH),$(MODULES))) - -DEPFILES := $(OBJ:%$(OBJSUFFIX)=%.dep$(OBJSUFFIX)) - -SRCFILES = $(OBJ:$(OBJDIR)%.o$(OBJSUFFIX)=%.c) -SRCFILES := $(SRCFILES:$(OBJDIR)%.ao$(OBJSUFFIX)=%.$(AS_SUFFIX)) - -OBJ += $(BUILDINFO_OBJ) - -.PHONY: all clean install apidoc - -all: $(BIN) - -clean: - @$(RM) $(BIN) ../Acess2.$(ARCH).gz $(BIN).dsm ../Map.$(ARCH).txt LineCounts.$(ARCH).txt - @$(RM) -r $(OBJDIR) $(OBJ) $(DEPFILES) $(BUILDINFO_SRC) - -install: $(BIN) - @cp $(BIN) $(BIN)_ - @$(STRIP) $(BIN)_ - @gzip -c $(BIN)_ > $(GZBIN) - @$(RM) $(BIN)_ - $(xCP) $(GZBIN) $(DISTROOT) - -apidoc: - doxygen Doxyfile.api - -$(BIN): $(OBJ) $(MODS) arch/$(ARCHDIR)/link.ld Makefile ../BuildConf/$(ARCH)/Makefile.cfg ../BuildConf/$(ARCH)/$(PLATFORM).mk - @echo --- LD -o $(BIN) - @$(LD) $(LDFLAGS) -o $(BIN) $(OBJ) $(MODS) --defsym __buildnum=$$(( $(BUILD_NUM) + 1 )) -Map ../Map.$(ARCH).txt - @$(DISASM) -S $(BIN) > $(BIN).dsm - @wc -l $(SRCFILES) include/*.h > LineCounts.$(ARCH).txt - @echo BUILD_NUM = $$(( $(BUILD_NUM) + 1 )) > Makefile.BuildNum.$(ARCH) - $(POSTBUILD) - -$(OBJDIR)%.ao$(OBJSUFFIX): %.$(AS_SUFFIX) Makefile - @echo --- AS -o $@ - @mkdir -p $(dir $@) - @$(AS) $(ASFLAGS) $< -o $@ -ifeq ($(AS_SUFFIX),S) - @$(MAKEDEP) $(CPPFLAGS) -MT $@ -o $(OBJDIR)$*.ao.dep$(OBJSUFFIX) $< -endif - -$(OBJDIR)%.o$(OBJSUFFIX): %.c Makefile -# if exists %*/Makefile -# @make -C %*/ all -# else - @echo --- CC -o $@ - @mkdir -p $(dir $@) - @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< - @$(MAKEDEP) $(CPPFLAGS) -MT $@ -o $(OBJDIR)$*.o.dep$(OBJSUFFIX) $< -# endif - -%.xo.$(ARCH): - @BUILDTYPE=static make -C $* all - -include/syscalls.h include/syscalls.inc.asm: syscalls.lst Makefile GenSyscalls.pl - perl GenSyscalls.pl - -Makefile: ../Makefile.cfg arch/$(ARCHDIR)/Makefile - -$(BUILDINFO_SRC): $(filter-out $(BUILDINFO_OBJ), $(OBJ)) $(MODS) arch/$(ARCHDIR)/link.ld Makefile - @echo "#include " > $@ - @echo "const char gsGitHash[] = \""`git log -n 1 | head -n 1 | awk '{print $$2}'`"\";" >> $@ - @echo "const int giBuildNumber = $(BUILD_NUM);" >> $@ -$(BUILDINFO_OBJ): $(BUILDINFO_SRC) - @echo --- CC -o $@ - @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< - -# Dependency Files --include $(DEPFILES) diff --git a/Kernel/adt.c b/Kernel/adt.c deleted file mode 100644 index ef2ae058..00000000 --- a/Kernel/adt.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Acess2 Kernel - * - * adt.c - * - Complex data type code - */ -#include -#include - - -// === CODE === -// --- Ring Buffers --- -tRingBuffer *RingBuffer_Create(size_t Space) -{ - tRingBuffer *ret = malloc(sizeof(tRingBuffer)+Space); - ret->Start = 0; - ret->Length = 0; - ret->Space = Space; - return ret; -} - -size_t RingBuffer_Read(void *Dest, tRingBuffer *Buffer, size_t Length) -{ - size_t tmpLen; - - tmpLen = Buffer->Length; // Changed in Write, so cache it for our read - - if(Length > tmpLen) Length = tmpLen; - - if( Buffer->Start + Length > Buffer->Space ) - { - int endData = Buffer->Space - Buffer->Start; - memcpy(Dest, &Buffer->Data[Buffer->Start], endData); - memcpy((Uint8*)Dest + endData, Buffer->Data, Length - endData); - } - else - { - memcpy(Dest, &Buffer->Data[Buffer->Start], Length); - } - - // Lock then modify - SHORTLOCK( &Buffer->Lock ); - Buffer->Start += Length; - if( Buffer->Start > Buffer->Space ) - Buffer->Start -= Buffer->Space; - Buffer->Length -= Length; - SHORTREL( &Buffer->Lock ); - - return Length; -} - -size_t RingBuffer_Write(tRingBuffer *Buffer, const void *Source, size_t Length) -{ - size_t bufEnd, endSpace; - size_t tmpLen, tmpStart; - - // Cache Start and Length because _Read can change these - SHORTLOCK( &Buffer->Lock ); - tmpStart = Buffer->Start; - tmpLen = Buffer->Length; - SHORTREL( &Buffer->Lock ); - - bufEnd = (tmpStart + Buffer->Length) % Buffer->Space; - endSpace = Buffer->Space - bufEnd; - - // Force to bounds - if(Length > Buffer->Space - tmpLen) Length = Buffer->Space - tmpLen; - - if(endSpace < Length) - { - memcpy( &Buffer->Data[bufEnd], Source, endSpace ); - memcpy( Buffer->Data, (Uint8*)Source + endSpace, Length - endSpace ); - } - else - { - memcpy( &Buffer->Data[bufEnd], Source, Length ); - } - - // Lock then modify - SHORTLOCK( &Buffer->Lock ); - Buffer->Length += Length; - SHORTREL( &Buffer->Lock ); - - return Length; -} - diff --git a/Kernel/arch/archdoc.h b/Kernel/arch/archdoc.h deleted file mode 100644 index ef3331f7..00000000 --- a/Kernel/arch/archdoc.h +++ /dev/null @@ -1,124 +0,0 @@ -/** - * \file archdoc.h - * \brief Documentation Definitions for the Acess 2 Architecture Interface - * \author John Hodge (thePowersGang) - * - * Acess 2 allows different architecture builds to be made off almost - * the same source tree. The difference between the trees is the inclusion - * of a different directory from the "Kernel/arch/" directory that - * contains the architecture specific intialisation and management code. - * Each achitecture tree must provide all the definitions from this - * document in some form or another (usually in the most efficient way - * avaliable) - * The values and types used in this documentation are a guide only and - * will most probably be incorrect for most architectures. - */ -#ifndef _ARCHDOC_H_ -#define _ARCHDOC_H_ - -/** - * \brief Maximum number of CPUs supported by this architecture driver - * (in the current build) - */ -#define MAX_CPUS 1 -/** - * \brief Number of bits in a machine word (Uint) - */ -#define BITS 32 -/** - * \brief Number of valid bits in a \a tPAddr - */ -#define PHYS_BITS 32 - -/** - * \name Syncronisation Primitives - * \{ - */ -/** - * \brief Spinlock type - */ -typedef volatile int tSpinlock; -/** - * \brief Acquire a spinlock - */ -#define LOCK(lockptr) do{while(*(tSpinlock*)lockptr)Threads_Yield();*(tSpinlock*)lockptr=1;}while(0) -/** - * \brief Release a held spinlock - */ -#define RELEASE(lockptr) do{*(tSpinlock*)lockptr=0;}while(0) -/** - * \} - */ -//#define HALT() __asm__ __volatile__ ("hlt") - - -/** - * \name Atomic Types - * \{ - */ -typedef unsigned int Uint; //!< Unsigned machine native integer -typedef unsigned char Uint8; //!< Unsigned 8-bit integer -typedef unsigned short Uint16; //!< Unsigned 16-bit integer -typedef unsigned long Uint32; //!< Unsigned 32-bit integer -typedef unsigned long long Uint64; //!< Unsigned 64-bit integer -typedef signed int Sint; //!< Signed Machine Native integer -typedef signed char Sint8; //!< Signed 8-bit integer -typedef signed short Sint16; //!< Signed 16-bit integer -typedef signed long Sint32; //!< Signed 32-bit integer -typedef signed long long Sint64; //!< Signed 16-bit integer -/** - * \} - */ - -typedef Uint size_t; //!< Counter/Byte count type -typedef Uint32 tVAddr; //!< Virtual address type -typedef Uint32 tPAddr; //!< Physical Address type - -/** - * \brief Register state passed to the syscall handler - * - * The \a tSyscallRegs structure allows the system call handler to read - * the user state to get the arguments for the call. It also allows the - * handler to alter specific parts of the user state to reflect the - * result of the call. - * \note The fields shown here are need only be present in the actual - * structure, in any order. The implementation may also add more - * fields for padding purposes. - */ -typedef struct { - Uint Arg4; //!< Fourth argument - Uint Arg3; //!< Third argument - Uint Arg2; //!< Second argument - union { - Uint Arg1; //!< First arugment - Uint RetHi; //!< High part of the return - }; - union { - Uint Num; //!< Call Number - Uint Return; //!< Low return value - }; - - Uint StackPointer; //!< User stack pointer -} tSyscallRegs; - -/** - * \brief Opaque structure definining the MMU state for a task - */ -typedef struct sMemoryState tMemoryState; - -/** - * \brief Opque structure defining the CPU state for a task - */ -typedef struct sTaskState tTaskState; - - -/** - * \name Memory Management - * \{ - */ -/** - * \} - */ - - -#endif diff --git a/Kernel/arch/armv7/Makefile b/Kernel/arch/armv7/Makefile deleted file mode 100644 index fad1b55d..00000000 --- a/Kernel/arch/armv7/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# -# Acess2 Kernel -# arm7 Architecture Makefile -# arch/arm7/Makefile - -CPPFLAGS = -CFLAGS = -ASFLAGS = - -CPPFLAGS += -DMMU_PRESENT=1 -LDFLAGS += `$(CC) --print-libgcc-file-name` - -A_OBJ = start.ao main.o lib.o lib.ao time.o pci.o debug.o -A_OBJ += mm_phys.o mm_virt.o proc.o proc.ao - -#main.c: Makefile.BuildNum.$(ARCH) - -ifeq ($(PLATFORM),tegra2) - POSTBUILD = arm-elf-objcopy $(BIN) -O binary $(BIN) -endif diff --git a/Kernel/arch/armv7/debug.c b/Kernel/arch/armv7/debug.c deleted file mode 100644 index 7b9e55dd..00000000 --- a/Kernel/arch/armv7/debug.c +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Acess2 - * - By John Hodge (thePowersGang) - * - * arch/arm7/debug.c - * - ARM7 Debug output - * NOTE: Currently designed for the realview-pb-a8 emulated by Qemu - */ -#include - -// === CONSTANTS === -//#define UART0_BASE 0x10009000 -#define UART0_BASE 0xF1000000 // Boot time mapped - -// === PROTOTYPES === -void KernelPanic_SetMode(void); -void KernelPanic_PutChar(char Ch); -void StartupPrint(const char *str); - -// === GLOBALS === - int giDebug_SerialInitialised = 0; - -// === CODE === -void Debug_PutCharDebug(char ch) -{ - if(ch == '\n') - Debug_PutCharDebug('\r'); - - #if PLATFORM_is_tegra2 - // Tegra2 - while( !(*(volatile Uint32*)(UART0_BASE + 0x14) & (1 << 5)) ) - ; - #endif - -// *(volatile Uint32*)(SERIAL_BASE + SERIAL_REG_DATA) = ch; - *(volatile Uint32*)(UART0_BASE) = ch; -} - -void Debug_PutStringDebug(const char *str) -{ - for( ; *str; str++ ) - Debug_PutCharDebug( *str ); -} - -void KernelPanic_SetMode(void) -{ -} - -void KernelPanic_PutChar(char ch) -{ -// Debug_PutCharDebug(ch); -} - -void StartupPrint(const char *str) -{ -} - diff --git a/Kernel/arch/armv7/include/arch.h b/Kernel/arch/armv7/include/arch.h deleted file mode 100644 index 837a5e10..00000000 --- a/Kernel/arch/armv7/include/arch.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Acess2 - * ARM7 Architecture Header - */ -#ifndef _ARCH_H_ -#define _ARCH_H_ - -// === CONSTANTS === -#define INVLPTR ((void*)-1) -#define BITS 32 -#define PAGE_SIZE 0x1000 -#define KERNEL_BASE 0x80000000 // 2GiB - -// === TYPES === -typedef unsigned int Uint; -typedef unsigned char Uint8; -typedef unsigned short Uint16; -typedef unsigned long Uint32; -typedef unsigned long long Uint64; -typedef signed int Sint; -typedef signed char Sint8; -typedef signed short Sint16; -typedef signed long Sint32; -typedef signed long long Sint64; - -typedef int size_t; -typedef char BOOL; - -typedef Uint32 tVAddr; -typedef Uint32 tPAddr; - -#include "lock.h" - -// --- Debug -extern void Debug_PutCharDebug(char Ch); -extern void Debug_PutStringDebug(const char *String); - -// This should be elsewhere, but CBF -extern void MM_SetupPhys(void); -extern int MM_InitialiseVirtual(void); - -#define NO_IO_BUS 1 - -#endif diff --git a/Kernel/arch/armv7/include/assembly.h b/Kernel/arch/armv7/include/assembly.h deleted file mode 100644 index 0c5c57fb..00000000 --- a/Kernel/arch/armv7/include/assembly.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Acess2 ARMv7 - * - By John Hodge (thePowersGang) - * - * arch/arm7/include/assembly.h - * - Assembly specific macros - */ -#ifndef _ASSEMBLY_H_ -#define _ASSEMBLY_H_ - -#define PUSH_GPRS \ - str r0, [sp,#-1*4];\ - str r1, [sp,#-2*4];\ - str r2, [sp,#-3*4];\ - str r3, [sp,#-4*4];\ - str r4, [sp,#-5*4];\ - str r5, [sp,#-6*4];\ - str r6, [sp,#-7*4];\ - str r7, [sp,#-8*4];\ - str r8, [sp,#-9*4];\ - str r9, [sp,#-10*4];\ - str r10, [sp,#-11*4];\ - str r11, [sp,#-12*4];\ - str r12, [sp,#-13*4];\ - str sp, [sp,#-14*4];\ - str lr, [sp,#-15*4];\ - sub sp, #16*4 - -#define POP_GPRS add sp, #16*4; \ - ldr r0, [sp,#-1*4]; \ - ldr r1, [sp,#-2*4]; \ - ldr r2, [sp,#-3*4]; \ - ldr r3, [sp,#-4*4]; \ - ldr r4, [sp,#-5*4]; \ - ldr r5, [sp,#-6*4]; \ - ldr r6, [sp,#-7*4]; \ - ldr r7, [sp,#-8*4]; \ - ldr r8, [sp,#-9*4]; \ - ldr r9, [sp,#-10*4]; \ - ldr r10, [sp,#-11*4]; \ - ldr r11, [sp,#-12*4]; \ - ldr r12, [sp,#-13*4]; \ - ldr lr, [sp,#-15*4]; - -#endif - diff --git a/Kernel/arch/armv7/include/lock.h b/Kernel/arch/armv7/include/lock.h deleted file mode 100644 index 6688af48..00000000 --- a/Kernel/arch/armv7/include/lock.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Acess2 - * ARM7 Architecture - * - * lock.h - Hardware level spinlocks - */ -#ifndef _LOCK_H_ -#define _LOCK_H_ - -// === CODE === -struct sShortSpinlock { - int Lock; -}; - -// --- Spinlocks --- -static inline int IS_LOCKED(struct sShortSpinlock *Lock) -{ - return !!Lock->Lock; -} - -static inline int CPU_HAS_LOCK(struct sShortSpinlock *Lock) -{ - // TODO: Handle multiple CPUs - return !!Lock->Lock; -} - -static inline int SHORTLOCK(struct sShortSpinlock *Lock) -{ - #if 0 - // Coped from linux, yes, but I know what it does now :) - Uint tmp; - __asm__ __volatile__ ( - "1: ldrex %0, [%1]\n" // Exclusive LOAD - " teq %0, #0\n" // Check if zero - " strexeq %0, %2, [%1]\n" // Set to one if it is zero (releasing lock on the memory) - " teqeq %0, #0\n" // If the lock was avaliable, check if the write succeeded - " bne 1b" // If the lock was unavaliable, or the write failed, loop - : "=&r" (tmp) // Temp - : "r" (&Lock->Lock), "r" (1) - : "cc" // Condition codes clobbered - ); - #elif 1 - while( *(volatile int*)&Lock->Lock ) ; - Lock->Lock = 1; - #else - int v = 1; - while( v ) - __asm__ __volatile__ ( - "swp %0, %0, [%1]" - : "=r" (v) : "r" (&Lock->Lock) - : "cc" - ); - #endif - return 1; -} - -static inline void SHORTREL(struct sShortSpinlock *Lock) -{ - Lock->Lock = 0; -} - -#endif - diff --git a/Kernel/arch/armv7/include/mm_virt.h b/Kernel/arch/armv7/include/mm_virt.h deleted file mode 100644 index c1f10deb..00000000 --- a/Kernel/arch/armv7/include/mm_virt.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Acess2 - * ARM7 Virtual Memory Manager Header - */ -#ifndef _MM_VIRT_H_ -#define _MM_VIRT_H_ - -#include "options.h" - -#define USER_STACK_COMM 0x04000 // Pages to allocate up front -#define USER_STACK_SIZE 0x10000 // Stack space -#define USER_STACK_TOP 0x78000000 - -#define MM_USER_MIN 0x00001000 -#define USER_LIB_MAX 0x70000000 -#define MM_PPD_HANDLES 0x7F800000 -#define MM_TABLE1USER 0x7FC00000 // 2 GiB - 4 MiB -#define MM_TABLE0USER 0x7FE00000 // 2 GiB - 2 MiB -#define MM_KSTACK_BASE 0x7FE00000 -#define MM_KSTACK_END 0x80000000 - -// Page Blocks are 12-bits wide (12 address bits used) -// Hence, the table is 16KiB large (and must be so aligned) -// and each block addresses 1MiB of data - -// First level table is aligned to 16KiB (restriction of TTBR reg) -// - VMSAv6 uses two TTBR regs, determined by bit 31 - -//#define KERNEL_BASE 0x80000000 // 2GiB - -#define MM_KHEAP_BASE 0x80800000 // 8MiB of kernel code -#define MM_KHEAP_MAX 0xC0000000 // ~1GiB of kernel heap - -#define MM_MODULE_MIN 0xC0000000 // - 0xD0000000 -#define MM_MODULE_MAX 0xCF000000 - -#define MM_GLOBALSTACKS 0xCF000000 // Global stacks -#define MM_GLOBALSTACKS_END 0xD0000000 - -// PMM Data, giving it 256MiB is overkill, but it's unused atm -#define MM_MAXPHYSPAGE (1024*1024) -// 2^(32-12) max pages -// 8.125 bytes per page (for bitmap allocation) -// = 8.125 MiB -#define MM_PMM_BASE 0xE0000000 -#define MM_PMM_END 0xF0000000 - -#define MM_HWMAP_BASE 0xF0000000 // Ent 0xF00 -#define MM_HWMAP_END 0xFE000000 -#define MM_TMPMAP_BASE 0xFE000000 -#define MM_TMPMAP_END 0xFF000000 - -#define MM_KERNEL_VFS 0xFF000000 // -#define MM_TABLE1KERN 0xFF800000 // - 0x???????? 4MiB -//#define MM_TABLE0KERN 0xFFC00000 // - 0xFFE04000 16KiB - -#endif diff --git a/Kernel/arch/armv7/include/options.h b/Kernel/arch/armv7/include/options.h deleted file mode 100644 index 95345ed6..00000000 --- a/Kernel/arch/armv7/include/options.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Acess2 ARMv7 Port - * - By John Hodge (thePowersGang) - * - * options.h - * - C/ASM Shared constants - */ -#ifndef _ARMV7_OPTIONS_H_ -#define _ARMV7_OPTIONS_H_ - -#define KERNEL_BASE 0x80000000 - -//#define PCI_PADDR 0x60000000 // Realview (Non-PB) - -#if PLATFORM_is_realview_pb -# define UART0_PADDR 0x10009000 // Realview -# define GICI_PADDR 0x1e000000 -# define GICD_PADDR 0x1e001000 -# define PL110_BASE 0x10020000 // Integrator - -#endif - -#if PLATFORM_is_tegra2 // Tegra2 -# define UART0_PADDR 0x70006000 -# define GICD_PADDR 0x50041000 -# define GICI_PADDR 0x50040100 -//# define PL110_BASE 0x10020000 // Integrator -#endif - -#define MM_KSTACK_SIZE 0x2000 // 2 Pages - -#endif - diff --git a/Kernel/arch/armv7/include/proc.h b/Kernel/arch/armv7/include/proc.h deleted file mode 100644 index d6ef3d55..00000000 --- a/Kernel/arch/armv7/include/proc.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Acess2 - * ARM7 Architecture - * - * proc.h - Arch-Dependent Process Management - */ -#ifndef _PROC_H_ -#define _PROC_H_ - -#define MAX_CPUS 4 -#define USER_MAX 0x80000000 - -// === STRUCTURES === -typedef struct { - Uint32 IP, SP; - Uint32 UserIP, UserSP; -} tTaskState; - -typedef struct { - Uint32 Base; -} tMemoryState; - -typedef struct { - union { - Uint32 Num; - Uint32 Error; - }; - union { - Uint32 Arg1; - Uint32 Return; - }; - union { - Uint32 Arg2; - Uint32 RetHi; - }; - Uint32 Arg3; - Uint32 Arg4; - Uint32 Arg5; - Uint32 Arg6; // R6 -} tSyscallRegs; - -// === MACROS === -#define HALT() do{}while(0) - -// === PROTOTYPES === - -#endif - diff --git a/Kernel/arch/armv7/lib.S b/Kernel/arch/armv7/lib.S deleted file mode 100644 index e2f06130..00000000 --- a/Kernel/arch/armv7/lib.S +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Acess2 ARM - * - By John Hodge (thePowersGang) - * - * arch/arm7/lib.S - * - Assembly editions of library functions - */ -#include "include/assembly.h" - -.globl __memcpy_byte -__memcpy_byte: -1: - tst r2, r2 @ Check counter - moveq pc, lr @ Return if zero - ldrb r3, [r1],#1 @ Read - strb r3, [r0],#1 @ Write - sub r2, #1 - b 1b - -@ -@ Pre-aligned memcpy (32-bit blocks) -@ -.globl __memcpy_align4 -__memcpy_align4: - push {r4} - mvn r3, #3 @ Mask for checking length - - @ 4 byte chunk copies -1: tst r2, r3 - ldrne r4, [r1],#4 - strne r4, [r0],#4 - subne r2, #4 - bne 1b - - @ single byte copies to finish off -2: tst r2, #3 - beq 3f - ldrb r4, [r1],#1 - strb r4, [r0],#1 - sub r2, #1 - b 2b - -3: pop {r4} - mov pc, lr - -@ -@ Division -@ -.globl __divmod32_asm -__divmod32_asm: - push {r4} - mov r4, #0 @ Return value - mov r3, #1 @ add value - - @ Scan up for first larger multiple of 2 -1: cmp r0, r1 @ N < D - bmi 2f @ ^^ - lsl r1, r1, #1 @ D <<= 1 - lsls r3, r3, #1 @ add <<= 1 - beq .err @ result is zero - b 1b - - @ Go back down -2: lsrs r3, r3, #1 @ add >>= 1 - beq 3f @ Done (value is zero) - lsr r1, r1, #1 @ D >>= 1 - cmp r0, r1 @ N < D - bmi 2b - sub r0, r1 @ N -= D - add r4, r3 @ ret += add - b 2b -3: - tst r2, r2 @ Remainder (if wanted) - strne r0,[r2] - mov r0, r4 @ Return value - pop {r4} - mov pc, lr -.err: - mov r0, #0 - tst r2, r2 - strne r0, [r2] - pop {r4} - mov pc, lr - diff --git a/Kernel/arch/armv7/lib.c b/Kernel/arch/armv7/lib.c deleted file mode 100644 index 5a6928e7..00000000 --- a/Kernel/arch/armv7/lib.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Acess2 ARM7 Port - * - * lib.c - Library Functions - */ -#include -#include "../helpers.h" - -// === IMPORTS === -extern void __memcpy_align4(void *_dest, const void *_src, size_t _length); -extern void __memcpy_byte(void *_dest, const void *_src, size_t _length); -extern Uint32 __divmod32_asm(Uint32 Num, Uint32 Den, Uint32 *Rem); - -// === PROTOTYPES === -Uint64 __divmod64(Uint64 Num, Uint64 Den, Uint64 *Rem); -Uint32 __divmod32(Uint32 Num, Uint32 Den, Uint32 *Rem); -Uint64 __udivdi3(Uint64 Num, Uint64 Den); -Uint64 __umoddi3(Uint64 Num, Uint64 Den); -Uint32 __udivsi3(Uint32 Num, Uint32 Den); -Uint32 __umodsi3(Uint32 Num, Uint32 Den); -Sint32 __divsi3(Sint32 Num, Sint32 Den); -Sint32 __modsi3(Sint32 Num, Sint32 Den); - -// === CODE === -void *memcpy(void *_dest, const void *_src, size_t _length) -{ - Uint8 *dst8 = _dest; - const Uint8 *src8 = _src; - - if( ((tVAddr)_dest & 3) == 0 && ((tVAddr)_src & 3) == 0 ) - { - __memcpy_align4(_dest, _src, _length); - return _dest; - } - - // Handle small copies / Non-aligned - if( _length < 4 || ((tVAddr)_dest & 3) != ((tVAddr)_src & 3) ) - { - __memcpy_byte(_dest, _src, _length); - return _dest; - } - - // Force alignment - while( (tVAddr)dst8 & 3 ) *dst8 ++ = *src8++, _length --; - - __memcpy_align4(dst8, src8, _length); - - return _dest; -} - -int memcmp(const void *_m1, const void *_m2, size_t _length) -{ - const Uint32 *m1, *m2; - const Uint8 *m1_8 = _m1, *m2_8 = _m2; - - // Handle small copies / Non-aligned - if( _length < 4 || ((tVAddr)_m1 & 3) != ((tVAddr)_m1 & 3) ) - { - for( ; _length--; m1_8++,m2_8++ ) { - if(*m1_8 != *m2_8) return *m1_8 - *m2_8; - } - return 0; - } - - // Force alignment - for( ; (tVAddr)m1_8 & 3; m1_8 ++, m2_8 ++) { - if(*m1_8 != *m2_8) return *m1_8 - *m2_8; - } - m1 = (void *)m1_8; m2 = (void *)m2_8; - - // DWORD copies - for( ; _length > 3; _length -= 4, m1++, m2++) - if(*m1 != *m2) return *m1 - *m2; - - // Trailing bytes - m1_8 = (void*)m1; m2_8 = (void*)m2; - for( ; _length; _length --, m1_8++, m2_8++ ) - if(*m1_8 != *m2_8) return *m1_8 - *m2_8; - - return 0; -} - -void *memset(void *_dest, int _value, size_t _length) -{ - Uint32 *dst, val32; - Uint8 *dst8 = _dest; - - _value = (Uint8)_value; - - // Handle small copies - if( _length < 4 ) - { - for( ; _length--; dst8++ ) - *dst8 = _value; - return _dest; - } - - val32 = _value; - val32 |= val32 << 8; - val32 |= val32 << 16; - - // Force alignment - while( (tVAddr)dst8 & 3 ) *dst8 ++ = _value; - dst = (void *)dst8; - - // DWORD copies - for( ; _length > 3; _length -= 4) - *dst++ = val32; - - // Trailing bytes - dst8 = (void*)dst; - for( ; _length; _length -- ) - *dst8 ++ = _value; - - return _dest; -} - -DEF_DIVMOD(64) -DEF_DIVMOD(32) - -Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem) -{ - Uint64 ret; - if(Den == 0) return 0; // TODO: #div0 - if(Num < Den) { - if(Rem) *Rem = Num; - return 0; - } - if(Num == 0) { - if(Rem) *Rem = 0; - return 0; - } - if(Den == 1) { - if(Rem) *Rem = 0; - return Num; - } - if(Den == 2) { - if(Rem) *Rem = Num & 1; - return Num >> 1; - } - if(Den == 16) { - if(Rem) *Rem = Num & 0xF; - return Num >> 4; - } - if(Den == 32) { - if(Rem) *Rem = Num & 0x1F; - return Num >> 5; - } - if(Den == 0x1000) { - if(Rem) *Rem = Num & 0xFFF; - return Num >> 12; - } - - if( !(Den >> 32) && !(Num >> 32) ) { - if(Rem) *Rem = 0; // Clear high bits - return __divmod32_asm(Num, Den, (Uint32*)Rem); - } - - ret = __divmod64(Num, Den, Rem); - return ret; -} - -// Unsigned Divide 64-bit Integer -Uint64 __udivdi3(Uint64 Num, Uint64 Den) -{ - return DivMod64U(Num, Den, NULL); -} - -// Unsigned Modulus 64-bit Integer -Uint64 __umoddi3(Uint64 Num, Uint64 Den) -{ - Uint64 ret = 0; - DivMod64U(Num, Den, &ret); - return ret; -} - -Uint32 __udivsi3(Uint32 Num, Uint32 Den) -{ - return __divmod32_asm(Num, Den, NULL); -} - -Uint32 __umodsi3(Uint32 Num, Uint32 Den) -{ - Uint32 rem; - __divmod32_asm(Num, Den, &rem); - return rem; -} - -static inline Sint32 DivMod32S(Sint32 Num, Sint32 Den, Sint32 *Rem) -{ - Sint32 ret = 1; - if( Num < 0 ) { - ret = -ret; - Num = -Num; - } - if( Den < 0 ) { - ret = -ret; - Den = -Den; - } - if(ret < 0) - ret = -__divmod32(Num, Den, (Uint32*)Rem); - else - ret = __divmod32(Num, Den, (Uint32*)Rem); - return ret; -} - -Sint32 __divsi3(Sint32 Num, Sint32 Den) -{ - return DivMod32S(Num, Den, NULL); -} - -Sint32 __modsi3(Sint32 Num, Sint32 Den) -{ - Sint32 rem; - DivMod32S(Num, Den, &rem); - return rem; -} diff --git a/Kernel/arch/armv7/link.ld b/Kernel/arch/armv7/link.ld deleted file mode 100644 index d10dcc46..00000000 --- a/Kernel/arch/armv7/link.ld +++ /dev/null @@ -1,55 +0,0 @@ -ENTRY (_start) - -_kernel_base = 0x80000000; -_usertext_vbase = 0xFFFFE000; - -SECTIONS -{ - . = 0; - .init : - { - *(.init) - } - . += _kernel_base; - .text : AT( ADDR(.text) - _kernel_base ) - { - *(.text*) - *(.rodata*) - } - - - /* HACKS: User accesible .text section */ - . = ALIGN(0x1000); - gUsertextPhysStart = . - _kernel_base; - . = _usertext_vbase; - .usertext : AT( gUsertextPhysStart ) - { - *(.usertext) - } - . += gUsertextPhysStart + _kernel_base - _usertext_vbase; - - /* 0x4000 (4 pages) alignment needed for root table */ - .data ALIGN(0x4000) : AT( ADDR(.data) - _kernel_base ) - { - *(.padata) - *(.data*) - - gKernelSymbols = .; - *(KEXPORT) - gKernelSymbolsEnd = .; - - gKernelModules = .; - *(KMODULES) - gKernelModulesEnd = .; - } - .bss : AT( ADDR(.bss) - _kernel_base ) - { - bss_start = .; - *(.bss*) - *(COMMON*) - . = ALIGN(0x1000); - *(.pabss) - bss_end = .; - } - gKernelEnd = .; -} diff --git a/Kernel/arch/armv7/main.c b/Kernel/arch/armv7/main.c deleted file mode 100644 index 248c17c5..00000000 --- a/Kernel/arch/armv7/main.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Acess2 - * - * ARM7 Entrypoint - * arch/arm7/main.c - */ -#define DEBUG 0 - -#include -#include - -// === IMPORTS === -extern void Interrupts_Setup(void); -extern void Arch_LoadBootModules(void); -extern void Heap_Install(void); -extern void Threads_Init(void); -extern void System_Init(const char *Commandline); - -// === PROTOTYPES === - int kmain(void); -Uint32 ARMv7_int_HandleSyscalls(Uint32 Num, Uint32 *Args); - -// === CODE === -int kmain(void) -{ - LogF("Acess2 ARMv7 v"EXPAND_STR(KERNEL_VERSION)"\n"); - LogF(" Git Hash %s\n", gsGitHash); - LogF(" Build %i\n", BUILD_NUM); - - MM_SetupPhys(); - - LogF("Heap Setup...\n"); - Heap_Install(); - - LogF("Threads Init...\n"); - Threads_Init(); - - LogF("VFS Init...\n"); - VFS_Init(); - - // Boot modules? - Module_EnsureLoaded("armv7_GIC"); - - // - LogF("Moving to arch-independent init\n"); - #if PLATFORM_is_tegra2 - System_Init("Acess2.armv7.bin /Acess=initrd: -VTerm:Video=Tegra2Vid"); - #else - System_Init("Acess2.armv7.bin /Acess=initrd: -VTerm:Video=PL110"); - #endif -// System_Init("Acess2.armv7.bin /Acess=initrd:"); - //TODO: - LogF("End of kmain(), for(;;) Threads_Sleep();\n"); - for(;;) - Threads_Sleep(); -} - -void Arch_LoadBootModules(void) -{ -} - -Uint32 ARMv7_int_HandleSyscalls(Uint32 Num, Uint32 *Args) -{ - Uint32 ret = -1, err = 0; - Uint32 addr; - ENTER("iNum xArgs[0] xArgs[1] xArgs[2] xArgs[3]", - Num, Args[0], Args[1], Args[2], Args[3] - ); - switch(Num) - { - case 1: -// Log_Debug("ARMv7", "__clear_cache(%p, %p)", Args[0], Args[1]); - // Align - Args[0] &= ~0xFFF; - Args[1] += 0xFFF; Args[1] &= ~0xFFF; - // Invalidate! - for( addr = Args[0]; addr < Args[1]; addr += 0x1000 ) - { - LOG("addr = %p", addr); - __asm__ __volatile__ ( - "mcrlt p15, 0, %0, c7, c5, 1;\n\t" - "mcrlt p15, 0, %0, c7, c6, 1;\n\t" - : - : "r" (addr) - ); - } - ret = 0; - break; - } - Args[0] = ret; // RetLow - Args[1] = 0; // RetHi - Args[2] = err; // Errno - LEAVE('x', ret); - return ret; -} - diff --git a/Kernel/arch/armv7/mm_phys.c b/Kernel/arch/armv7/mm_phys.c deleted file mode 100644 index 5e4a2428..00000000 --- a/Kernel/arch/armv7/mm_phys.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Acess2 - * - * ARM7 Physical Memory Manager - * arch/arm7/mm_phys.c - */ -#define DEBUG 0 - -#include -#include - -#define MM_NUM_RANGES 1 // Single range -#define MM_RANGE_MAX 0 -#define TRACE_ALLOCS 0 - -#define NUM_STATIC_ALLOC 4 - -char gStaticAllocPages[NUM_STATIC_ALLOC][PAGE_SIZE] __attribute__ ((section(".padata"))); -tPAddr gaiStaticAllocPages[NUM_STATIC_ALLOC] = { - (tPAddr)(&gStaticAllocPages[0]) - KERNEL_BASE, - (tPAddr)(&gStaticAllocPages[1]) - KERNEL_BASE, - (tPAddr)(&gStaticAllocPages[2]) - KERNEL_BASE, - (tPAddr)(&gStaticAllocPages[3]) - KERNEL_BASE -}; -extern char gKernelEnd[]; - - -#include - -//#define REALVIEW_LOWRAM_SIZE 0x10000000 -#define REALVIEW_LOWRAM_SIZE (32*1024*1024) - -void MM_SetupPhys(void) -{ - LogF("MM_SetupPhys: ()\n"); - MM_Tpl_InitPhys( REALVIEW_LOWRAM_SIZE/0x1000, NULL ); -} - -int MM_int_GetMapEntry( void *Data, int Index, tPAddr *Start, tPAddr *Length ) -{ - switch(Index) - { - case 0: - *Start = ((tVAddr)&gKernelEnd - KERNEL_BASE + 0xFFF) & ~0xFFF; - *Length = REALVIEW_LOWRAM_SIZE - *Start; - return 1; - default: - return 0; - } -} - -/** - * \brief Takes a physical address and returns the ID of its range - * \param Addr Physical address of page - * \return Range ID from eMMPhys_Ranges - */ -int MM_int_GetRangeID( tPAddr Addr ) -{ - return MM_RANGE_MAX; // ARM doesn't need ranges -} diff --git a/Kernel/arch/armv7/mm_virt.c b/Kernel/arch/armv7/mm_virt.c deleted file mode 100644 index 460b334f..00000000 --- a/Kernel/arch/armv7/mm_virt.c +++ /dev/null @@ -1,1078 +0,0 @@ -/* - * Acess2 - * - * ARM7 Virtual Memory Manager - * - arch/arm7/mm_virt.c - */ -#define DEBUG 0 -#include -#include -#include - -#define TRACE_MAPS 0 - -#define AP_KRW_ONLY 1 // Kernel page -#define AP_KRO_ONLY 5 // Kernel RO page -#define AP_RW_BOTH 3 // Standard RW -#define AP_RO_BOTH 7 // COW Page -#define AP_RO_USER 2 // User RO Page -#define PADDR_MASK_LVL1 0xFFFFFC00 - -// === IMPORTS === -extern Uint32 kernel_table0[]; - -// === TYPES === -typedef struct -{ - tPAddr PhysAddr; - Uint8 Size; - Uint8 Domain; - BOOL bExecutable; - BOOL bGlobal; - BOOL bShared; - int AP; -} tMM_PageInfo; - -//#define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>20)]) -#define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>22)]) -#define USRFRACTAL(addr) (*((Uint32*)(0x7FDFF000) + ((addr)>>22))) -#define TLBIALL() __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0)) -#define TLBIMVA(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 1;dsb;isb" : : "r" ((addr)&~0xFFF):"memory") -#define DCCMVAC(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 1" : : "r" ((addr)&~0xFFF)) - -// === PROTOTYPES === -void MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1); - int MM_int_AllocateCoarse(tVAddr VAddr, int Domain); - int MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi); - int MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi); -tVAddr MM_NewUserStack(void); -tPAddr MM_AllocateZero(tVAddr VAddr); -tPAddr MM_AllocateRootTable(void); -void MM_int_CloneTable(Uint32 *DestEnt, int Table); -tPAddr MM_Clone(void); -tVAddr MM_NewKStack(int bGlobal); -void MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info); -//void MM_DumpTables(tVAddr Start, tVAddr End); -void MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch); - -// === GLOBALS === -tPAddr giMM_ZeroPage; - -// === CODE === -int MM_InitialiseVirtual(void) -{ - return 0; -} - -void MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1) -{ - if(VAddr & 0x80000000) { - *Table0 = (void*)&kernel_table0; // Level 0 - *Table1 = (void*)MM_TABLE1KERN; // Level 1 - } - else { - *Table0 = (void*)MM_TABLE0USER; - *Table1 = (void*)MM_TABLE1USER; - } -} - -int MM_int_AllocateCoarse(tVAddr VAddr, int Domain) -{ - Uint32 *table0, *table1; - Uint32 *desc; - tPAddr paddr; - - ENTER("xVAddr iDomain", VAddr, Domain); - - MM_int_GetTables(VAddr, &table0, &table1); - - VAddr &= ~(0x400000-1); // 4MiB per "block", 1 Page - - desc = &table0[ VAddr>>20]; - LOG("desc = %p", desc); - - // table0: 4 bytes = 1 MiB - - LOG("desc[0] = %x", desc[0]); - LOG("desc[1] = %x", desc[1]); - LOG("desc[2] = %x", desc[2]); - LOG("desc[3] = %x", desc[3]); - - if( (desc[0] & 3) != 0 || (desc[1] & 3) != 0 - || (desc[2] & 3) != 0 || (desc[3] & 3) != 0 ) - { - // Error? - LEAVE('i', 1); - return 1; - } - - paddr = MM_AllocPhys(); - if( !paddr ) - { - // Error - LEAVE('i', 2); - return 2; - } - - *desc = paddr | (Domain << 5) | 1; - desc[1] = desc[0] + 0x400; - desc[2] = desc[0] + 0x800; - desc[3] = desc[0] + 0xC00; - - if( VAddr < 0x80000000 ) { - USRFRACTAL(VAddr) = paddr | 0x13; - } - else { - FRACTAL(table1, VAddr) = paddr | 0x13; - } - - // TLBIALL - TLBIALL(); - - memset( (void*)&table1[ (VAddr >> 12) & ~(1024-1) ], 0, 0x1000 ); - - LEAVE('i', 0); - return 0; -} - -int MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi) -{ - Uint32 *table0, *table1; - Uint32 *desc; - - ENTER("pVAddr ppi", VAddr, pi); - - MM_int_GetTables(VAddr, &table0, &table1); - - desc = &table0[ VAddr >> 20 ]; - LOG("desc = %p", desc); - - switch(pi->Size) - { - case 12: // Small Page - case 16: // Large Page - LOG("Page"); - if( (*desc & 3) == 0 ) { - MM_int_AllocateCoarse( VAddr, pi->Domain ); - } - desc = &table1[ VAddr >> 12 ]; - LOG("desc (2) = %p", desc); - if( pi->Size == 12 ) - { - // Small page - // - Error if overwriting a large page - if( (*desc & 3) == 1 ) LEAVE_RET('i', 1); - if( pi->PhysAddr == 0 ) { - *desc = 0; - TLBIMVA( VAddr ); - DCCMVAC( (tVAddr) desc ); - #warning "HACK: TLBIALL" - TLBIALL(); - LEAVE('i', 0); - return 0; - } - - *desc = (pi->PhysAddr & 0xFFFFF000) | 2; - if(!pi->bExecutable) *desc |= 1; // XN - if(!pi->bGlobal) *desc |= 1 << 11; // nG - if( pi->bShared) *desc |= 1 << 10; // S - *desc |= (pi->AP & 3) << 4; // AP - *desc |= ((pi->AP >> 2) & 1) << 9; // APX - TLBIMVA( VAddr ); - #warning "HACK: TLBIALL" - TLBIALL(); - DCCMVAC( (tVAddr) desc ); - LEAVE('i', 0); - return 0; - } - else - { - // Large page - Log_Warning("MMVirt", "TODO: Implement large pages in MM_int_SetPageInfo"); - } - break; - case 20: // Section or unmapped - Log_Warning("MMVirt", "TODO: Implement sections in MM_int_SetPageInfo"); - break; - case 24: // Supersection - // Error if not aligned - if( VAddr & 0xFFFFFF ) { - LEAVE('i', 1); - return 1; - } - if( (*desc & 3) == 0 || ((*desc & 3) == 2 && (*desc & (1 << 18))) ) - { - if( pi->PhysAddr == 0 ) { - *desc = 0; - } - else { - // Apply - *desc = pi->PhysAddr & 0xFF000000; -// *desc |= ((pi->PhysAddr >> 32) & 0xF) << 20; -// *desc |= ((pi->PhysAddr >> 36) & 0x7) << 5; - *desc |= 2 | (1 << 18); - } - // TODO: Apply to all entries - Log_Warning("MMVirt", "TODO: Apply changes to all entries of supersections"); - LEAVE('i', 0); - return 0; - } - // TODO: What here? - Log_Warning("MMVirt", "TODO: 24-bit not on supersection?"); - LEAVE('i', 1); - return 1; - } - - LEAVE('i', 1); - return 1; -} - -int MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi) -{ - Uint32 *table0, *table1; - Uint32 desc; - -// LogF("MM_int_GetPageInfo: VAddr=%p, pi=%p\n", VAddr, pi); - - MM_int_GetTables(VAddr, &table0, &table1); - - desc = table0[ VAddr >> 20 ]; - -// if( VAddr > 0x90000000) -// LOG("table0 desc(%p) = %x", &table0[ VAddr >> 20 ], desc); - - pi->bExecutable = 1; - pi->bGlobal = 0; - pi->bShared = 0; - pi->AP = 0; - - switch( (desc & 3) ) - { - // 0: Unmapped - case 0: - pi->PhysAddr = 0; - pi->Size = 20; - pi->Domain = 0; - return 1; - - // 1: Coarse page table - case 1: - // Domain from top level table - pi->Domain = (desc >> 5) & 7; - // Get next level - desc = table1[ VAddr >> 12 ]; -// LOG("table1 desc(%p) = %x", &table1[ VAddr >> 12 ], desc); - switch( desc & 3 ) - { - // 0: Unmapped - case 0: - pi->Size = 12; - return 1; - // 1: Large Page (64KiB) - case 1: - pi->Size = 16; - pi->PhysAddr = desc & 0xFFFF0000; - pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2); - pi->bExecutable = !(desc & 0x8000); - pi->bShared = (desc >> 10) & 1; - return 0; - // 2/3: Small page - case 2: - case 3: - pi->Size = 12; - pi->PhysAddr = desc & 0xFFFFF000; - pi->bExecutable = !(desc & 1); - pi->bGlobal = !(desc >> 11); - pi->bShared = (desc >> 10) & 1; - pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2); - return 0; - } - return 1; - - // 2: Section (or Supersection) - case 2: - if( desc & (1 << 18) ) { - // Supersection - pi->PhysAddr = desc & 0xFF000000; - pi->PhysAddr |= (Uint64)((desc >> 20) & 0xF) << 32; - pi->PhysAddr |= (Uint64)((desc >> 5) & 0x7) << 36; - pi->Size = 24; - pi->Domain = 0; // Supersections default to zero - pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2); - return 0; - } - - // Section - pi->PhysAddr = desc & 0xFFF80000; - pi->Size = 20; - pi->Domain = (desc >> 5) & 7; - pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2); - return 0; - - // 3: Reserved (invalid) - case 3: - pi->PhysAddr = 0; - pi->Size = 20; - pi->Domain = 0; - return 2; - } - return 2; -} - -// --- Exports --- -tPAddr MM_GetPhysAddr(tVAddr VAddr) -{ - tMM_PageInfo pi; - if( MM_int_GetPageInfo(VAddr, &pi) ) - return 0; - return pi.PhysAddr | (VAddr & ((1 << pi.Size)-1)); -} - -Uint MM_GetFlags(tVAddr VAddr) -{ - tMM_PageInfo pi; - int ret; - - if( MM_int_GetPageInfo(VAddr, &pi) ) - return 0; - - ret = 0; - - switch(pi.AP) - { - case 0: - break; - case AP_KRW_ONLY: - ret |= MM_PFLAG_KERNEL; - break; - case AP_KRO_ONLY: - ret |= MM_PFLAG_KERNEL|MM_PFLAG_RO; - break; - case AP_RW_BOTH: - break; - case AP_RO_BOTH: - ret |= MM_PFLAG_COW; - break; - case AP_RO_USER: - ret |= MM_PFLAG_RO; - break; - } - - if( pi.bExecutable ) ret |= MM_PFLAG_EXEC; - return ret; -} - -void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask) -{ - tMM_PageInfo pi; - Uint curFlags; - - if( MM_int_GetPageInfo(VAddr, &pi) ) - return ; - - curFlags = MM_GetFlags(VAddr); - if( (curFlags & Mask) == Flags ) - return ; - curFlags &= ~Mask; - curFlags |= Flags; - - if( curFlags & MM_PFLAG_COW ) - pi.AP = AP_RO_BOTH; - else - { - switch(curFlags & (MM_PFLAG_KERNEL|MM_PFLAG_RO) ) - { - case 0: - pi.AP = AP_RW_BOTH; break; - case MM_PFLAG_KERNEL: - pi.AP = AP_KRW_ONLY; break; - case MM_PFLAG_RO: - pi.AP = AP_RO_USER; break; - case MM_PFLAG_KERNEL|MM_PFLAG_RO: - pi.AP = AP_KRO_ONLY; break; - } - } - - pi.bExecutable = !!(curFlags & MM_PFLAG_EXEC); - - MM_int_SetPageInfo(VAddr, &pi); -} - -int MM_IsValidBuffer(tVAddr Addr, size_t Size) -{ - tMM_PageInfo pi; - int bUser = 0; - - Size += Addr & (PAGE_SIZE-1); - Addr &= ~(PAGE_SIZE-1); - - if( MM_int_GetPageInfo(Addr, &pi) ) return 0; - Addr += PAGE_SIZE; - - if(pi.AP != AP_KRW_ONLY && pi.AP != AP_KRO_ONLY) - bUser = 1; - - while( Size >= PAGE_SIZE ) - { - if( MM_int_GetPageInfo(Addr, &pi) ) - return 0; - if(bUser && (pi.AP == AP_KRW_ONLY || pi.AP == AP_KRO_ONLY)) - return 0; - Addr += PAGE_SIZE; - Size -= PAGE_SIZE; - } - - return 1; -} - -int MM_Map(tVAddr VAddr, tPAddr PAddr) -{ - tMM_PageInfo pi = {0}; - #if TRACE_MAPS - Log("MM_Map %P=>%p", PAddr, VAddr); - #endif - - pi.PhysAddr = PAddr; - pi.Size = 12; - if(VAddr < USER_STACK_TOP) - pi.AP = AP_RW_BOTH; - else - pi.AP = AP_KRW_ONLY; // Kernel Read/Write - pi.bExecutable = 1; - if( MM_int_SetPageInfo(VAddr, &pi) ) { -// MM_DerefPhys(pi.PhysAddr); - return 0; - } - return pi.PhysAddr; -} - -tPAddr MM_Allocate(tVAddr VAddr) -{ - tMM_PageInfo pi = {0}; - - ENTER("pVAddr", VAddr); - - pi.PhysAddr = MM_AllocPhys(); - if( pi.PhysAddr == 0 ) LEAVE_RET('i', 0); - pi.Size = 12; - if(VAddr < USER_STACK_TOP) - pi.AP = AP_RW_BOTH; - else - pi.AP = AP_KRW_ONLY; - pi.bExecutable = 0; - if( MM_int_SetPageInfo(VAddr, &pi) ) { - MM_DerefPhys(pi.PhysAddr); - LEAVE('i', 0); - return 0; - } - LEAVE('x', pi.PhysAddr); - return pi.PhysAddr; -} - -tPAddr MM_AllocateZero(tVAddr VAddr) -{ - if( !giMM_ZeroPage ) { - giMM_ZeroPage = MM_Allocate(VAddr); - MM_RefPhys(giMM_ZeroPage); - memset((void*)VAddr, 0, PAGE_SIZE); - } - else { - MM_RefPhys(giMM_ZeroPage); - MM_Map(VAddr, giMM_ZeroPage); - } - MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW); - return giMM_ZeroPage; -} - -void MM_Deallocate(tVAddr VAddr) -{ - tMM_PageInfo pi; - - if( MM_int_GetPageInfo(VAddr, &pi) ) return ; - if( pi.PhysAddr == 0 ) return; - MM_DerefPhys(pi.PhysAddr); - - pi.PhysAddr = 0; - pi.AP = 0; - pi.bExecutable = 0; - MM_int_SetPageInfo(VAddr, &pi); -} - -tPAddr MM_AllocateRootTable(void) -{ - tPAddr ret; - - ret = MM_AllocPhysRange(2, -1); - if( ret & 0x1000 ) { - MM_DerefPhys(ret); - MM_DerefPhys(ret+0x1000); - ret = MM_AllocPhysRange(3, -1); - if( ret & 0x1000 ) { - MM_DerefPhys(ret); - ret += 0x1000; -// Log("MM_AllocateRootTable: Second try not aligned, %P", ret); - } - else { - MM_DerefPhys(ret + 0x2000); -// Log("MM_AllocateRootTable: Second try aligned, %P", ret); - } - } -// else -// Log("MM_AllocateRootTable: Got it in one, %P", ret); - return ret; -} - -void MM_int_CloneTable(Uint32 *DestEnt, int Table) -{ - tPAddr table; - Uint32 *tmp_map; - Uint32 *cur = (void*)MM_TABLE1USER; -// Uint32 *cur = &FRACTAL(MM_TABLE1USER,0); - int i; - - table = MM_AllocPhys(); - if(!table) return ; - - cur += 256*Table; - - tmp_map = (void*)MM_MapTemp(table); - - for( i = 0; i < 1024; i ++ ) - { -// Log_Debug("MMVirt", "cur[%i] (%p) = %x", Table*256+i, &cur[Table*256+i], cur[Table*256+i]); - switch(cur[i] & 3) - { - case 0: tmp_map[i] = 0; break; - case 1: - tmp_map[i] = 0; - Log_Error("MMVirt", "TODO: Support large pages in MM_int_CloneTable (%p)", (Table*256+i)*0x1000); - // Large page? - break; - case 2: - case 3: - // Small page - // - If full RW -// Debug("%p cur[%i] & 0x230 = 0x%x", Table*256*0x1000, i, cur[i] & 0x230); - if( (cur[i] & 0x230) == 0x010 ) - { - void *dst, *src; - tPAddr newpage; - newpage = MM_AllocPhys(); - src = (void*)( (Table*256+i)*0x1000 ); - dst = (void*)MM_MapTemp(newpage); -// Debug("Taking a copy of kernel page %p (%P)", src, cur[i] & ~0xFFF); - memcpy(dst, src, PAGE_SIZE); - MM_FreeTemp( (tVAddr)dst ); - tmp_map[i] = newpage | (cur[i] & 0xFFF); - } - else - { - if( (cur[i] & 0x230) == 0x030 ) - cur[i] |= 0x200; // Set to full RO (Full RO=COW, User RO = RO) - tmp_map[i] = cur[i]; - MM_RefPhys( tmp_map[i] & ~0xFFF ); - } - break; - } - } - MM_FreeTemp( (tVAddr) tmp_map ); - - DestEnt[0] = table + 0*0x400 + 1; - DestEnt[1] = table + 1*0x400 + 1; - DestEnt[2] = table + 2*0x400 + 1; - DestEnt[3] = table + 3*0x400 + 1; -} - -tPAddr MM_Clone(void) -{ - tPAddr ret; - Uint32 *new_lvl1_1, *new_lvl1_2, *cur; - Uint32 *tmp_map; - int i; - -// MM_DumpTables(0, KERNEL_BASE); - - ret = MM_AllocateRootTable(); - - cur = (void*)MM_TABLE0USER; - new_lvl1_1 = (void*)MM_MapTemp(ret); - new_lvl1_2 = (void*)MM_MapTemp(ret+0x1000); - tmp_map = new_lvl1_1; - for( i = 0; i < 0x800-4; i ++ ) - { - // HACK! Ignore the original identity mapping - if( i == 0 && Threads_GetTID() == 0 ) { - tmp_map[0] = 0; - continue; - } - if( i == 0x400 ) - tmp_map = &new_lvl1_2[-0x400]; - switch( cur[i] & 3 ) - { - case 0: tmp_map[i] = 0; break; - case 1: - MM_int_CloneTable(&tmp_map[i], i); - i += 3; // Tables are alocated in blocks of 4 - break; - case 2: - case 3: - Log_Error("MMVirt", "TODO: Support Sections/Supersections in MM_Clone (i=%i)", i); - tmp_map[i] = 0; - break; - } - } - - // Allocate Fractal table - { - int j, num; - tPAddr tmp = MM_AllocPhys(); - Uint32 *table = (void*)MM_MapTemp(tmp); - Uint32 sp; - register Uint32 __SP asm("sp"); - - // Map table to last 4MiB of user space - new_lvl1_2[0x3FC] = tmp + 0*0x400 + 1; - new_lvl1_2[0x3FD] = tmp + 1*0x400 + 1; - new_lvl1_2[0x3FE] = tmp + 2*0x400 + 1; - new_lvl1_2[0x3FF] = tmp + 3*0x400 + 1; - - tmp_map = new_lvl1_1; - for( j = 0; j < 512; j ++ ) - { - if( j == 256 ) - tmp_map = &new_lvl1_2[-0x400]; - if( (tmp_map[j*4] & 3) == 1 ) - { - table[j] = tmp_map[j*4] & PADDR_MASK_LVL1;// 0xFFFFFC00; - table[j] |= 0x813; // nG, Kernel Only, Small page, XN - } - else - table[j] = 0; - } - // Fractal - table[j++] = (ret + 0x0000) | 0x813; - table[j++] = (ret + 0x1000) | 0x813; - // Nuke the rest - for( ; j < 1024; j ++ ) - table[j] = 0; - - // Get kernel stack bottom - sp = __SP & ~(MM_KSTACK_SIZE-1); - j = (sp / 0x1000) % 1024; - num = MM_KSTACK_SIZE/0x1000; - -// Log("num = %i, sp = %p, j = %i", num, sp, j); - - // Copy stack pages - for(; num--; j ++, sp += 0x1000) - { - tVAddr page; - void *tmp_page; - - page = MM_AllocPhys(); -// Log("page = %P", page); - table[j] = page | 0x813; - - tmp_page = (void*)MM_MapTemp(page); - memcpy(tmp_page, (void*)sp, 0x1000); - MM_FreeTemp( (tVAddr) tmp_page ); - } - - MM_FreeTemp( (tVAddr)table ); - } - - MM_FreeTemp( (tVAddr)new_lvl1_1 ); - MM_FreeTemp( (tVAddr)new_lvl1_2 ); - -// Log("MM_Clone: ret = %P", ret); - - return ret; -} - -void MM_ClearUser(void) -{ - int i, j; - const int user_table_count = USER_STACK_TOP / (256*0x1000); - Uint32 *cur = (void*)MM_TABLE0USER; - Uint32 *tab; - -// MM_DumpTables(0, 0x80000000); - -// Log("user_table_count = %i (as opposed to %i)", user_table_count, 0x800-4); - - for( i = 0; i < user_table_count; i ++ ) - { - switch( cur[i] & 3 ) - { - case 0: break; // Already unmapped - case 1: // Sub pages - tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32)); - for( j = 0; j < 1024; j ++ ) - { - switch( tab[j] & 3 ) - { - case 0: break; // Unmapped - case 1: - Log_Error("MMVirt", "TODO: Support large pages in MM_ClearUser"); - break; - case 2: - case 3: - MM_DerefPhys( tab[j] & ~(PAGE_SIZE-1) ); - break; - } - } - MM_DerefPhys( cur[i] & ~(PAGE_SIZE-1) ); - cur[i+0] = 0; - cur[i+1] = 0; - cur[i+2] = 0; - i += 3; - break; - case 2: - case 3: - Log_Error("MMVirt", "TODO: Implement sections/supersections in MM_ClearUser"); - break; - } - cur[i] = 0; - } - - // Final block of 4 tables are KStack - i = 0x800 - 4; - - // Clear out unused stacks - { - register Uint32 __SP asm("sp"); - int cur_stack_base = ((__SP & ~(MM_KSTACK_SIZE-1)) / PAGE_SIZE) % 1024; - - tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32)); - - // First 512 is the Table1 mapping + 2 for Table0 mapping - for( j = 512+2; j < 1024; j ++ ) - { - // Skip current stack - if( j == cur_stack_base ) { - j += (MM_KSTACK_SIZE / PAGE_SIZE) - 1; - continue ; - } - if( !(tab[j] & 3) ) continue; - ASSERT( (tab[j] & 3) == 2 ); - MM_DerefPhys( tab[j] & ~(PAGE_SIZE) ); - tab[j] = 0; - } - } - - -// MM_DumpTables(0, 0x80000000); -} - -tVAddr MM_MapTemp(tPAddr PAddr) -{ - tVAddr ret; - tMM_PageInfo pi; - - for( ret = MM_TMPMAP_BASE; ret < MM_TMPMAP_END - PAGE_SIZE; ret += PAGE_SIZE ) - { - if( MM_int_GetPageInfo(ret, &pi) == 0 ) - continue; - -// Log("MapTemp %P at %p by %p", PAddr, ret, __builtin_return_address(0)); - MM_RefPhys(PAddr); // Counter the MM_Deallocate in FreeTemp - MM_Map(ret, PAddr); - - return ret; - } - Log_Warning("MMVirt", "MM_MapTemp: All slots taken"); - return 0; -} - -void MM_FreeTemp(tVAddr VAddr) -{ - if( VAddr < MM_TMPMAP_BASE || VAddr >= MM_TMPMAP_END ) { - Log_Warning("MMVirt", "MM_FreeTemp: Passed an addr not from MM_MapTemp (%p)", VAddr); - return ; - } - - MM_Deallocate(VAddr); -} - -tVAddr MM_MapHWPages(tPAddr PAddr, Uint NPages) -{ - tVAddr ret; - int i; - tMM_PageInfo pi; - - ENTER("xPAddr iNPages", PAddr, NPages); - - // Scan for a location - for( ret = MM_HWMAP_BASE; ret < MM_HWMAP_END - NPages * PAGE_SIZE; ret += PAGE_SIZE ) - { -// LOG("checking %p", ret); - // Check if there is `NPages` free pages - for( i = 0; i < NPages; i ++ ) - { - if( MM_int_GetPageInfo(ret + i*PAGE_SIZE, &pi) == 0 ) - break; - } - // Nope, jump to after the used page found and try again -// LOG("i = %i, ==? %i", i, NPages); - if( i != NPages ) { - ret += i * PAGE_SIZE; - continue ; - } - - // Map the pages - for( i = 0; i < NPages; i ++ ) - MM_Map(ret+i*PAGE_SIZE, PAddr+i*PAGE_SIZE); - // and return - LEAVE('p', ret); - return ret; - } - Log_Warning("MMVirt", "MM_MapHWPages: No space for a %i page block", NPages); - LEAVE('p', 0); - return 0; -} - -tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PAddr) -{ - tPAddr phys; - tVAddr ret; - - phys = MM_AllocPhysRange(Pages, MaxBits); - if(!phys) { - Log_Warning("MMVirt", "No space left for a %i page block (MM_AllocDMA)", Pages); - return 0; - } - - ret = MM_MapHWPages(phys, Pages); - *PAddr = phys; - - return ret; -} - -void MM_UnmapHWPages(tVAddr Vaddr, Uint Number) -{ - Log_Error("MMVirt", "TODO: Implement MM_UnmapHWPages"); -} - -tVAddr MM_NewKStack(int bShared) -{ - tVAddr min_addr, max_addr; - tVAddr addr, ofs; - - if( bShared ) { - min_addr = MM_GLOBALSTACKS; - max_addr = MM_GLOBALSTACKS_END; - } - else { - min_addr = MM_KSTACK_BASE; - max_addr = MM_KSTACK_END; - } - - // Locate a free slot - for( addr = min_addr; addr < max_addr; addr += MM_KSTACK_SIZE ) - { - tMM_PageInfo pi; - if( MM_int_GetPageInfo(addr+MM_KSTACK_SIZE-PAGE_SIZE, &pi) ) break; - } - - // Check for an error - if(addr >= max_addr) { - return 0; - } - - // 1 guard page - for( ofs = PAGE_SIZE; ofs < MM_KSTACK_SIZE; ofs += PAGE_SIZE ) - { - if( MM_Allocate(addr + ofs) == 0 ) - { - while(ofs) - { - ofs -= PAGE_SIZE; - MM_Deallocate(addr + ofs); - } - Log_Warning("MMVirt", "MM_NewKStack: Unable to allocate"); - return 0; - } - } - return addr + ofs; -} - -tVAddr MM_NewUserStack(void) -{ - tVAddr addr, ofs; - - addr = USER_STACK_TOP - USER_STACK_SIZE; - if( MM_GetPhysAddr(addr + PAGE_SIZE) ) { - Log_Error("MMVirt", "Unable to create initial user stack, addr %p taken", - addr + PAGE_SIZE - ); - return 0; - } - - // 1 guard page - for( ofs = PAGE_SIZE; ofs < USER_STACK_SIZE; ofs += PAGE_SIZE ) - { - tPAddr rv; - if(ofs >= USER_STACK_SIZE - USER_STACK_COMM) - rv = MM_Allocate(addr + ofs); - else - rv = MM_AllocateZero(addr + ofs); - if(rv == 0) - { - while(ofs) - { - ofs -= PAGE_SIZE; - MM_Deallocate(addr + ofs); - } - Log_Warning("MMVirt", "MM_NewUserStack: Unable to allocate"); - return 0; - } - MM_SetFlags(addr+ofs, 0, MM_PFLAG_KERNEL); - } -// Log("Return %p", addr + ofs); -// MM_DumpTables(0, 0x80000000); - return addr + ofs; -} - -void MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info) -{ - if( giMM_ZeroPage && Info->PhysAddr == giMM_ZeroPage ) - { - Debug("%p => %8s - 0x%7x %i %x %s", - Start, "ZERO", Len, - Info->Domain, Info->AP, - Info->bGlobal ? "G" : "nG" - ); - } - else - { - Debug("%p => %8x - 0x%7x %i %x %s", - Start, Info->PhysAddr-Len, Len, - Info->Domain, Info->AP, - Info->bGlobal ? "G" : "nG" - ); - } -} - -void MM_DumpTables(tVAddr Start, tVAddr End) -{ - tVAddr range_start = 0, addr; - tMM_PageInfo pi, pi_old; - int i = 0, inRange=0; - - memset(&pi_old, 0, sizeof(pi_old)); - - Debug("Page Table Dump (%p to %p):", Start, End); - range_start = Start; - for( addr = Start; i == 0 || (addr && addr < End); i = 1 ) - { - int rv; -// Log("addr = %p", addr); - rv = MM_int_GetPageInfo(addr, &pi); - if( rv - || pi.Size != pi_old.Size - || pi.Domain != pi_old.Domain - || pi.AP != pi_old.AP - || pi.bGlobal != pi_old.bGlobal - || pi_old.PhysAddr != pi.PhysAddr ) - { - if(inRange) { - MM_int_DumpTableEnt(range_start, addr - range_start, &pi_old); - } - addr &= ~((1 << pi.Size)-1); - range_start = addr; - } - - pi_old = pi; - // Handle the zero page - if( !giMM_ZeroPage || pi_old.Size != 12 || pi_old.PhysAddr != giMM_ZeroPage ) - pi_old.PhysAddr += 1 << pi_old.Size; - addr += 1 << pi_old.Size; - inRange = (rv == 0); - } - if(inRange) - MM_int_DumpTableEnt(range_start, addr - range_start, &pi); - Debug("Done"); -} - -// NOTE: Runs in abort context, not much difference, just a smaller stack -void MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch) -{ - int rv; - tMM_PageInfo pi; - - rv = MM_int_GetPageInfo(Addr, &pi); - - // Check for COW - if( rv == 0 && pi.AP == AP_RO_BOTH ) - { - pi.AP = AP_RW_BOTH; - if( giMM_ZeroPage && pi.PhysAddr == giMM_ZeroPage ) - { - tPAddr newpage; - newpage = MM_AllocPhys(); - if( !newpage ) { - Log_Error("MMVirt", "Unable to allocate new page for COW of ZERO"); - for(;;); - } - - #if TRACE_COW - Log_Notice("MMVirt", "COW %p caused by %p, ZERO duped to %P (RefCnt(%i)--)", Addr, PC, - newpage, MM_GetRefCount(pi.PhysAddr)); - #endif - - MM_DerefPhys(pi.PhysAddr); - pi.PhysAddr = newpage; - pi.AP = AP_RW_BOTH; - MM_int_SetPageInfo(Addr, &pi); - - memset( (void*)(Addr & ~(PAGE_SIZE-1)), 0, PAGE_SIZE ); - - return ; - } - else if( MM_GetRefCount(pi.PhysAddr) > 1 ) - { - // Duplicate the page - tPAddr newpage; - void *dst, *src; - - newpage = MM_AllocPhys(); - if(!newpage) { - Log_Error("MMVirt", "Unable to allocate new page for COW"); - for(;;); - } - dst = (void*)MM_MapTemp(newpage); - src = (void*)(Addr & ~(PAGE_SIZE-1)); - memcpy( dst, src, PAGE_SIZE ); - MM_FreeTemp( (tVAddr)dst ); - - #if TRACE_COW - Log_Notice("MMVirt", "COW %p caused by %p, %P duped to %P (RefCnt(%i)--)", Addr, PC, - pi.PhysAddr, newpage, MM_GetRefCount(pi.PhysAddr)); - #endif - - MM_DerefPhys(pi.PhysAddr); - pi.PhysAddr = newpage; - } - #if TRACE_COW - else { - Log_Notice("MMVirt", "COW %p caused by %p, took last reference to %P", - Addr, PC, pi.PhysAddr); - } - #endif - // Unset COW - pi.AP = AP_RW_BOTH; - MM_int_SetPageInfo(Addr, &pi); - return ; - } - - - Log_Error("MMVirt", "Code at %p accessed %p (DFSR = 0x%x)%s", PC, Addr, DFSR, - (bPrefetch ? " - Prefetch" : "") - ); - if( Addr < 0x80000000 ) - MM_DumpTables(0, 0x80000000); - else - MM_DumpTables(0x80000000, -1); - for(;;); -} - diff --git a/Kernel/arch/armv7/pci.c b/Kernel/arch/armv7/pci.c deleted file mode 100644 index 2e674bbc..00000000 --- a/Kernel/arch/armv7/pci.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * - */ -#include -#include - -// Realview -//#define PCI_BASE 0x60000000 - -//#define PCI_BASE 0xF0400000 // VMM Mapping -#define PCI_BASE 0 - -// === CODE === -void PCI_CfgWriteDWord(Uint32 Addr, Uint32 Data) -{ - #if PCI_BASE - Uint32 address = PCI_BASE | Addr; - *(Uint32*)(address) = Data; - #else - #endif -} - -Uint32 PCI_CfgReadDWord(Uint32 Addr) -{ - #if PCI_BASE - Uint32 address = PCI_BASE | Addr; - return *(Uint32*)address; - #else - return 0xFFFFFFFF; - #endif -} - diff --git a/Kernel/arch/armv7/proc.S b/Kernel/arch/armv7/proc.S deleted file mode 100644 index 531de299..00000000 --- a/Kernel/arch/armv7/proc.S +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Acess2 ARM - * - By John Hodge (thePowersGang) - * - * arch/arm7/proc.S - * - Process management assembly - */ - -#include "include/assembly.h" - -.globl KernelThreadHeader -@ SP+12: Argument 1 -@ SP+8: Argument Count -@ SP+4: Function -@ SP+0: Thread Pointer -KernelThreadHeader: - ldr r0, [sp],#4 - @ TODO: Do something with the thread pointer - - ldr r4, [sp],#4 @ Function - @ Get argument - ldr r0, [sp],#4 - - blx r4 - - ldr r0, =0 - bl Threads_Exit - b . - -.globl SwitchTask -@ R0: New stack -@ R1: Pointer to where to save old stack -@ R2: New IP -@ R3: Pointer to save old IP -@ SP+0: New address space -SwitchTask: - push {r4-r12,lr} - - @ Save IP - ldr r4, =.return - str r4, [r3] - @ Save SP - str sp, [r1] - - @ Only update TTBR0 if the task has an explicit address space - ldr r1, [sp,#4*10] - tst r1, r1 - mcrne p15, 0, r1, c2, c0, 0 @ Set TTBR0 to r0 - mov r1, #0 - mcrne p15, 0, r1, c8, c7, 0 @ TLBIALL - Invalidate all - - @ Restore SP - mov sp, r0 - - bx r2 - -.return: - pop {r4-r12,pc} - -.extern MM_Clone -.extern MM_DumpTables -.globl Proc_CloneInt -Proc_CloneInt: - @ R0: SP Destination - @ R1: Mem Destination - push {r4-r12,lr} - mov r4, r1 @ Save mem destination - str sp, [r0] @ Save SP to SP dest - - bl MM_Clone - str r0, [r4] @ Save clone return to Mem Dest - - ldr r0, =Proc_CloneInt_new - pop {r4-r12,pc} -Proc_CloneInt_new: - mov r0, #0 - pop {r4-r12,pc} - -@ R0: New user SP -@ Return: Old user SP -.globl Proc_int_SwapUserSP -Proc_int_SwapUserSP: - cps #31 @ Go to system mode - mov r1, sp - tst r0, r0 @ Only update if non-zero - movne sp, r0 - mov r0, r1 - cps #19 - mov pc, lr - -.section .usertext, "ax" -.globl Proc_int_DropToUser -@ R0: User IP -@ R1: User SP -Proc_int_DropToUser: - cps #16 - mov sp, r1 - mov pc, r0 - -.section .rodata -csProc_CloneInt_NewTaskMessage: - .asciz "New task PC=%p, R4=%p, sp=%p" -csProc_CloneInt_OldTaskMessage: - .asciz "Parent task PC=%p, R4=%p, SP=%p" diff --git a/Kernel/arch/armv7/proc.c b/Kernel/arch/armv7/proc.c deleted file mode 100644 index cd998f2b..00000000 --- a/Kernel/arch/armv7/proc.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Acess2 - * - By John Hodge (thePowersGang) - * - * arch/arm7/proc.c - * - ARM7 Process Switching - */ -#include -#include -#include - -// === IMPORTS === -extern tThread gThreadZero; -extern tProcess gProcessZero; -extern void SwitchTask(Uint32 NewSP, Uint32 *OldSP, Uint32 NewIP, Uint32 *OldIP, Uint32 MemPtr); -extern void KernelThreadHeader(void); // Actually takes args on stack -extern void Proc_int_DropToUser(Uint32 IP, Uint32 SP) NORETURN __attribute__((long_call)); -extern Uint32 Proc_int_SwapUserSP(Uint32 NewSP); -extern Uint32 Proc_CloneInt(Uint32 *SP, Uint32 *MemPtr); -extern tVAddr MM_NewKStack(int bGlobal); // TODO: Move out into a header -extern tVAddr MM_NewUserStack(void); -extern char kernel_table0[]; - -// === PROTOTYPES === -void Proc_IdleThread(void *unused); - -// === GLOBALS === -tThread *gpCurrentThread = &gThreadZero; -tThread *gpIdleThread = NULL; - -// === CODE === -void ArchThreads_Init(void) -{ - gProcessZero.MemState.Base = (tPAddr)&kernel_table0 - KERNEL_BASE; -} - -void Proc_IdleThread(void *unused) -{ - Threads_SetPriority(gpIdleThread, -1); - for(;;) { - Proc_Reschedule(); - __asm__ __volatile__ ("wfi"); - } -} - -void Proc_Start(void) -{ - tTID tid; - - tid = Proc_NewKThread( Proc_IdleThread, NULL ); - gpIdleThread = Threads_GetThread(tid); - gpIdleThread->ThreadName = (char*)"Idle Thread"; -} - -int GetCPUNum(void) -{ - return 0; -} - -tThread *Proc_GetCurThread(void) -{ - return gpCurrentThread; -} - -void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) -{ - Uint32 *usr_sp; - int i; - const char **envp; - tVAddr delta; - -// Log_Debug("Proc", "Proc_StartUser: (Entrypoint=%p, Base=%p, ArgC=%i, ArgV=%p, DataSize=0x%x)", -// Entrypoint, Base, ArgC, ArgV, DataSize); - - // Write data to the user's stack - usr_sp = (void*)MM_NewUserStack(); - usr_sp -= (DataSize+3)/4; - memcpy(usr_sp, ArgV, DataSize); - free(ArgV); - - // Adjust user's copy of the arguments - delta = (tVAddr)usr_sp - (tVAddr)ArgV; - ArgV = (void*)usr_sp; - for(i = 0; ArgV[i]; i ++) ArgV[i] += delta; - envp = &ArgV[i+1]; - for(i = 0; envp[i]; i ++) envp[i] += delta; - - *--usr_sp = (Uint32)envp; - *--usr_sp = (Uint32)ArgV; - *--usr_sp = (Uint32)ArgC; - *--usr_sp = Base; - - // Drop to user code - Log_Debug("Proc", "Proc_int_DropToUser(%p, %p)", Entrypoint, usr_sp); - Proc_int_DropToUser(Entrypoint, (Uint32)usr_sp); -} - -void Proc_ClearProcess(tProcess *Process) -{ - Log_Warning("Proc", "TODO: Nuke address space etc"); -} - -void Proc_ClearThread(tThread *Thread) -{ -} - -tTID Proc_Clone(Uint Flags) -{ - tThread *new; - Uint32 pc, sp, mem; - - new = Threads_CloneTCB(Flags); - if(!new) return -1; - - // Actual clone magic - pc = Proc_CloneInt(&sp, &mem); - if(pc == 0) { - Log("Proc_Clone: In child"); - return 0; - } - - new->SavedState.IP = pc; - new->SavedState.SP = sp; - new->SavedState.UserSP = Proc_int_SwapUserSP(0); - new->SavedState.UserIP = Proc_GetCurThread()->SavedState.UserIP; - new->Process->MemState.Base = mem; - - Threads_AddActive(new); - - return new->TID; -} - -int Proc_SpawnWorker( void (*Fnc)(void*), void *Ptr ) -{ - tThread *new; - Uint32 sp; - - new = Threads_CloneThreadZero(); - if(!new) return -1; - if(new->ThreadName) free(new->ThreadName); - new->ThreadName = NULL; - - new->KernelStack = MM_NewKStack(1); - if(!new->KernelStack) { - // TODO: Delete thread - Log_Error("Proc", "Unable to allocate kernel stack"); - return -1; - } - - sp = new->KernelStack; - - *(Uint32*)(sp -= 4) = (Uint)Ptr; - *(Uint32*)(sp -= 4) = (Uint)Fnc; - *(Uint32*)(sp -= 4) = (Uint)new; - - new->SavedState.SP = sp; - new->SavedState.IP = (Uint)KernelThreadHeader; - - Threads_AddActive(new); - - return new->TID; -} - -tTID Proc_NewKThread( void (*Fnc)(void*), void *Ptr ) -{ - tThread *new; - Uint32 sp; - - new = Threads_CloneTCB(0); - if(!new) return -1; - free(new->ThreadName); - new->ThreadName = NULL; - - // TODO: Non-shared stack - new->KernelStack = MM_NewKStack(1); - if(!new->KernelStack) { - // TODO: Delete thread - Log_Error("Proc", "Unable to allocate kernel stack"); - return -1; - } - - sp = new->KernelStack; - - *(Uint32*)(sp -= 4) = (Uint)Ptr; - *(Uint32*)(sp -= 4) = (Uint)Fnc; - *(Uint32*)(sp -= 4) = (Uint)new; - - new->SavedState.SP = sp; - new->SavedState.IP = (Uint)KernelThreadHeader; - - Threads_AddActive(new); - - return new->TID; -} - -void Proc_CallFaultHandler(tThread *Thread) -{ - -} - -void Proc_Reschedule(void) -{ - tThread *cur, *next; - - cur = gpCurrentThread; - - next = Threads_GetNextToRun(0, cur); - if(!next) next = gpIdleThread; - if(!next || next == cur) return; - - Log("Switching to %p (%i %s) IP=%p SP=%p TTBR0=%p UsrSP=%p", - next, next->TID, next->ThreadName, - next->SavedState.IP, next->SavedState.SP, next->Process->MemState.Base, - next->SavedState.UserSP - ); - - Log("Requested by %p", __builtin_return_address(0)); - - gpCurrentThread = next; - - cur->SavedState.UserSP = Proc_int_SwapUserSP( next->SavedState.UserSP ); - - SwitchTask( - next->SavedState.SP, &cur->SavedState.SP, - next->SavedState.IP, &cur->SavedState.IP, - next->Process->MemState.Base - ); - -} - -void Proc_DumpThreadCPUState(tThread *Thread) -{ - -} - diff --git a/Kernel/arch/armv7/start.S b/Kernel/arch/armv7/start.S deleted file mode 100644 index 8d9f3e4d..00000000 --- a/Kernel/arch/armv7/start.S +++ /dev/null @@ -1,367 +0,0 @@ - -#include "include/assembly.h" -#include "include/options.h" - -@ -@ Exception defs taken from ARM DDI 0406B -@ -.section .init -interrupt_vector_table: -ivt_reset: b _start @ 0x00 Reset -ivt_undef: b Undef_Handler @ 0x04 #UD -ivt_svc: b SVC_Handler @ 0x08 SVC (used to be called SWI) -ivt_prefetch: b PrefetchAbort @ 0x0C Prefetch abort -ivt_data: b DataAbort @ 0x10 Data abort -ivt_unused: b . @ 0x14 Not Used -ivt_irq: b IRQHandler @ 0x18 IRQ -ivt_fiq: b . @ 0x1C FIQ (Fast interrupt) - -.globl _start -_start: - ldr r2, =UART0_PADDR - mov r1, #'A' - str r1, [r2] - - ldr r0, =kernel_table0-KERNEL_BASE - mcr p15, 0, r0, c2, c0, 1 @ Set TTBR1 to r0 - mcr p15, 0, r0, c2, c0, 0 @ Set TTBR0 to r0 too (for identity) - - mov r1, #'c' - str r1, [r2] - - mov r0, #1 - mcr p15, 0, r0, c2, c0, 2 @ Set TTCR to 1 (50/50 split) - - mov r1, #'e' - str r1, [r2] - - mov r0, #3 - mcr p15, 0, r0, c3, c0, 0 @ Set Domain 0 to Manager - - mov r1, #'s' - str r1, [r2] - - @ Enable VMSA - mrc p15, 0, r0, c1, c0, 0 - orr r0, r0, #1 - orr r0, r0, #1 << 23 - mvn r1, #1 << 2 - and r0, r0, r1 - mcr p15, 0, r0, c1, c0, 0 - - @ HACK! Disable caching - mrc p15, 0, r1, c1, c0, 0 - - ldr r2, =0xF1000000 - mov r1, #'s' - str r1, [r2] - - @ Enable access faults on domains 0 & 1 - mov r0, #0x55 @ 01010101b - mcr p15, 0, r0, c3, c0, 0 - - mov r1, #'2' - str r1, [r2] - - @ - @ Check for security extensions - @ - mrc p15, 0, r0, c0, c1, 1 - and r0, #0xF0 - @ - Present - ldrne r0,=KERNEL_BASE - mcrne p15, 0, r0, c12, c0, 0 @ Set the VBAR (brings exceptions into high memory) - @ - Absent - mrceq p15, 0, r0, c1, c0, 0 @ Set SCTLR.V - orreq r0, #0x2000 - mcreq p15, 0, r0, c1, c0, 0 - - mov r1, #'-' - str r1, [r2] - - @ Prepare for interrupts - cps #18 @ IRQ Mode - ldr sp, =irqstack+0x1000 @ Set up stack - cps #23 @ Abort Mode - ldr sp, =abortstack+0x1000 - cps #19 - - mov r1, #'a' - str r1, [r2] - mov r1, #'r' - str r1, [r2] - mov r1, #'m' - str r1, [r2] - mov r1, #13 - str r1, [r2] - mov r1, #10 - str r1, [r2] - -.extern bss_start -.extern bss_size_div_4 -.zero_bss: - ldr r0, =bss_start - ldr r1, =bss_end - mov r3, #0 -.zero_bss_loop: - str r3, [r0],#4 - cmp r0, r1 - bls .zero_bss_loop - -.goto_c: - ldr sp, =0x80000000-8 @ Set up stack (top of user range) - ldr r0, =kmain - mov pc, r0 -1: b 1b @ Infinite loop - -.comm irqstack, 0x1000 @ ; 4KiB Stack -.comm abortstack, 0x1000 @ ; 4KiB Stack - -.extern SyscallHandler -SVC_Handler: -@ sub lr, #4 - srsdb sp!, #19 @ Save state to stack - cpsie ifa, #19 @ Ensure we're in supervisor with interrupts enabled (should already be there) - push {r0-r12} - - ldr r4, [lr,#-4] - mvn r5, #0xFF000000 - and r4, r5 - - tst r4, #0x1000 - bne .arm_specifics - - push {r4} - - mov r0, sp - ldr r4, =SyscallHandler - blx r4 - -@ ldr r0, =csSyscallPrintRetAddr -@ ldr r1, [sp,#9*4+5*4] -@ ldr r4, =Log -@ blx r4 - - pop {r2} @ errno - pop {r0,r1} @ Ret/RetHi - add sp, #2*4 @ Saved r2/r3 - - pop {r4-r12} - rfeia sp! @ Pop state (actually RFEFD) -.arm_specifics: - and r4, #0xFF - mov r0, r4 @ Number - mov r1, sp @ Arguments - - ldr r4, =ARMv7_int_HandleSyscalls - blx r4 - - add sp, #4*4 - pop {r4-r12} - rfeia sp! - - -.globl gpIRQHandler -gpIRQHandler: .long 0 -IRQ_saved_sp: .long 0 -IRQ_saved_lr: .long 0 -.globl IRQHandler -IRQHandler: - sub lr, #4 @ Adjust LR to the correct value - srsdb sp!, #19 @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD) - cps #19 - - PUSH_GPRS - -@ ldr r0, =csIRQ_Tag -@ ldr r1, =csIRQ_Fmt -@ ldr r4, =Log_Debug -@ blx r4 - - @ Call the registered handler - ldr r0, gpIRQHandler - blx r0 - - @ Restore CPU state - POP_GPRS - cpsie i - rfeia sp! @ Pop state (actually RFEFD) - bx lr - -.globl DataAbort -DataAbort: - sub lr, #8 @ Adjust LR to the correct value - srsdb sp!, #23 @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD) -@ cpsid ifa, #19 - PUSH_GPRS - - mov r3, #0 @ not a prefetch abort - mrc p15, 0, r2, c5, c0, 0 @ Read DFSR (Data Fault Status Register) to R2 - mrc p15, 0, r1, c6, c0, 0 @ Read DFAR (Data Fault Address Register) into R1 - mov r0, lr @ PC - ldr r4, =MM_PageFault - blx r4 - - POP_GPRS - rfeia sp! @ Pop state (actually RFEFD) - -.globl PrefetchAbort -PrefetchAbort: - sub lr, #4 @ Adjust LR to the correct value - srsdb sp!, #23 @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD) -@ cpsid ifa, #19 - PUSH_GPRS - - ldr r0, =csAbort_Tag - ldr r1, =csPrefetchAbort_Fmt -# mov r2, lr - mrc p15, 0, r2, c6, c0, 2 @ Read IFAR (Instruction Fault Address Register) into R3 - mrc p15, 0, r3, c5, c0, 1 @ Read IFSR (Instruction Fault Status Register) into R3 - ldr r5, =Log_Error - blx r5 - -.loop: - wfi - b .loop -.globl Undef_Handler -Undef_Handler: - wfi - b Undef_Handler - - - -.section .rodata -csIRQ_Tag: -csAbort_Tag: - .asciz "ARMv7" -csIRQ_Fmt: - .asciz "IRQ" -csDataAbort_Fmt: - .asciz "Data Abort - %p accessed %p, DFSR=%x Unk:%x Unk:%x" -csPrefetchAbort_Fmt: - .asciz "Prefetch Abort at %p, IFSR=%x" -csSyscallPrintRetAddr: - .asciz "Syscall ret to %p" - -.section .padata -.globl kernel_table0 - -kernel_table0: - .long 0x00000402 @ Identity map the first 1 MiB - .rept 0x7FC - 1 - .long 0 - .endr - .long user_table1_map + 0x000 - KERNEL_BASE + 1 @ 0x7FC00000 - .long user_table1_map + 0x400 - KERNEL_BASE + 1 @ 0x7FD00000 - .long user_table1_map + 0x800 - KERNEL_BASE + 1 @ KStacks - .long user_table1_map + 0xC00 - KERNEL_BASE + 1 - @ 0x80000000 - User/Kernel split - .long 0x00000402 @ Map first 4 MiB to 2GiB (KRW only) - .long 0x00100402 @ - .long 0x00200402 @ - .long 0x00300402 @ - .rept 0xF00 - 0x800 - 4 - .long 0 - .endr -#if PCI_PADDR - .long PCI_PADDR + 0*(1 << 20) + 0x402 @ Map PCI config space - .long PCI_PADDR + 1*(1 << 20) + 0x402 - .long PCI_PADDR + 2*(1 << 20) + 0x402 - .long PCI_PADDR + 3*(1 << 20) + 0x402 - .long PCI_PADDR + 4*(1 << 20) + 0x402 - .long PCI_PADDR + 5*(1 << 20) + 0x402 - .long PCI_PADDR + 6*(1 << 20) + 0x402 - .long PCI_PADDR + 7*(1 << 20) + 0x402 - .long PCI_PADDR + 8*(1 << 20) + 0x402 - .long PCI_PADDR + 9*(1 << 20) + 0x402 - .long PCI_PADDR + 10*(1 << 20) + 0x402 - .long PCI_PADDR + 11*(1 << 20) + 0x402 - .long PCI_PADDR + 12*(1 << 20) + 0x402 - .long PCI_PADDR + 13*(1 << 20) + 0x402 - .long PCI_PADDR + 14*(1 << 20) + 0x402 - .long PCI_PADDR + 15*(1 << 20) + 0x402 -#else - .rept 16 - .long 0 - .endr -#endif - .long hwmap_table_0 + 0x000 - KERNEL_BASE + 1 - .long hwmap_table_0 + 0x400 - KERNEL_BASE + 1 - .long hwmap_table_0 + 0x800 - KERNEL_BASE + 1 - .long hwmap_table_0 + 0xC00 - KERNEL_BASE + 1 - .rept 0xFF8 - 0xF00 - 16 - 4 - .long 0 - .endr - @ Page fractals - .long kernel_table1_map + 0x000 - KERNEL_BASE + 1 - .long kernel_table1_map + 0x400 - KERNEL_BASE + 1 - .long kernel_table1_map + 0x800 - KERNEL_BASE + 1 - .long kernel_table1_map + 0xC00 - KERNEL_BASE + 1 - .long kernel_exception_map + 0x000 - KERNEL_BASE + 1 - .long kernel_exception_map + 0x400 - KERNEL_BASE + 1 - .long kernel_exception_map + 0x800 - KERNEL_BASE + 1 - .long kernel_exception_map + 0xC00 - KERNEL_BASE + 1 - -@ PID0 user table -.globl user_table1_map -@ User table1 data table (only the first half is needed) -@ - Abused to provide kernel stacks in the unused half of the table -user_table1_map: @ Size = 4KiB (only 2KiB used) - .rept 0x800/4-1 - .long 0 - .endr - .long user_table1_map - KERNEL_BASE + 0x13 @ ...1FF000 = 0x7FDFF000 - @ Kernel stack zone - .long kernel_table0 + 0x0000 - KERNEL_BASE + 0x13 @ ...200000 = 0x7FE00000 - .long kernel_table0 + 0x1000 - KERNEL_BASE + 0x13 @ ...201000 = 0x7FE01000 - .rept (0x800/4)-(MM_KSTACK_SIZE/0x1000)-2 - .long 0 - .endr - #if MM_KSTACK_SIZE != 0x2000 - #error Kernel stack size not changed in start.S - #endif - .long stack + 0x0000 - KERNEL_BASE + 0x13 @ Kernel Stack - .long stack + 0x1000 - KERNEL_BASE + 0x13 @ - -.globl kernel_table1_map -kernel_table1_map: @ Size = 4KiB - .rept (0xF00+16)/4 - .long 0 - .endr - .long hwmap_table_0 - KERNEL_BASE + 0x13 - .rept 0xFF8/4 - (0xF00+16)/4 - 1 - .long 0 - .endr - .long kernel_table1_map - KERNEL_BASE + 0x13 - .long kernel_exception_map - KERNEL_BASE + 0x13 - -@ Hardware mappings -.globl hwmap_table_0 -hwmap_table_0: - .long UART0_PADDR + 0x13 @ UART0 - .rept 1024 - 1 - .long 0 - .endr -.globl kernel_exception_map -kernel_exception_map: - @ Padding - .rept 1024-256 - .long 0 - .endr - @ Align to nearly the end - .rept 256-16 - .long 0 - .endr - .long 0x212 @ Map first page for exceptions (Kernel RO, Execute) - .rept 16-1-2 - .long 0 - .endr - .long gUsertextPhysStart + 0x22 @ User .text (User RO, Kernel RW, because both is COW) - .long 0 - -.section .padata -stack: - .space MM_KSTACK_SIZE, 0 @ Original kernel stack - -// vim: ts=8 ft=armv7 - diff --git a/Kernel/arch/armv7/time.c b/Kernel/arch/armv7/time.c deleted file mode 100644 index d4ae4fa6..00000000 --- a/Kernel/arch/armv7/time.c +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Acess2 - * - * ARM7 Time code - * arch/arm7/time.c - */ -#include - -// === GLOBALS === -tTime giTimestamp; - -// === CODE === -tTime now(void) -{ - return giTimestamp; -} diff --git a/Kernel/arch/helpers.h b/Kernel/arch/helpers.h deleted file mode 100644 index 4d85fcb4..00000000 --- a/Kernel/arch/helpers.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * arch/helpers.h - * - Misc helper functions for the arch code - */ -#ifndef _ARCH_HELPERS_H_ -#define _ARCH_HELPERS_H_ - -// Divide -// - Find what power of two times Den is > Num -// - Iterate down in bit significance -// > If the `N` value is greater than `D`, we can set this bit -#define DEF_DIVMOD(s) Uint##s __divmod##s(Uint##s N, Uint##s D, Uint##s*Rem){\ - Uint##s ret=0,add=1;\ - while(N>=D&&add) {D<<=1;add<<=1;}\ - while(add>1){\ - add>>=1;D>>=1;\ - if(N>=D){ret+=add;N-=D;}\ - }\ - if(Rem)*Rem = N;\ - return ret;\ -} - -#endif - diff --git a/Kernel/arch/m68k/Makefile b/Kernel/arch/m68k/Makefile deleted file mode 100644 index 4caea1e1..00000000 --- a/Kernel/arch/m68k/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# -# Acess2 Kernel -# m68k Architecture Makefile -# arch/m68k/Makefile - -CFLAGS = - -A_OBJ = main.o debug.o lib.o time.o proc.o mm_virt.o mm_phys.o - -LDFLAGS += `$(CC) --print-libgcc-file-name` - diff --git a/Kernel/arch/m68k/debug.c b/Kernel/arch/m68k/debug.c deleted file mode 100644 index 8c7de3b1..00000000 --- a/Kernel/arch/m68k/debug.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Acess2 M68K port - * - By John Hodge (thePowersGang) - * - * arch/m68k/debug.c - * - Debugging output - */ -#include - -// === PROTOTYPES === -void StartupPrint(const char *Str); -void KernelPanic_SetMode(void); -void KernelPanic_PutChar(char ch); - -// === CODE === -void StartupPrint(const char *Str) -{ -} - -void KernelPanic_SetMode(void) -{ -} - -void KernelPanic_PutChar(char ch) -{ -} - diff --git a/Kernel/arch/m68k/include/arch.h b/Kernel/arch/m68k/include/arch.h deleted file mode 100644 index 0b2dc5b4..00000000 --- a/Kernel/arch/m68k/include/arch.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Acess2 M68000 port - * - By John Hodge (thePowersGang) - * - * arch/m68k/include/arch.h - * - Architectre config - */ -#ifndef _M68K_ARCH_H_ -#define _M68K_ARCH_H_ - -#define INVLPTR ((void*)-1) -#define BITS 32 - -typedef unsigned long long Uint64; -typedef unsigned long Uint32; -typedef unsigned short Uint16; -typedef unsigned char Uint8; - -typedef signed long long Sint64; -typedef signed long Sint32; -typedef signed short Sint16; -typedef signed char Sint8; - -typedef unsigned int Uint; -typedef unsigned int size_t; - -typedef char BOOL; - -typedef Uint32 tVAddr; -typedef Uint32 tPAddr; - -struct sShortSpinlock -{ - int v; -}; - -// Non preemptive and no SMP, no need for these -#define SHORTLOCK(lock) do{}while(0) -#define SHORTREL(lock) do{}while(0) -#define IS_LOCKED(lock) (0) -#define CPU_HAS_LOCK(lock) (0) - -#define Debug_PutCharDebug(ch) do{}while(0) -#define Debug_PutStringDebug(ch) do{}while(0) - -#define HALT() do{}while(0) - -#define USER_MAX 0 - -#define PAGE_SIZE 0x1000 - -#endif - diff --git a/Kernel/arch/m68k/include/mm_virt.h b/Kernel/arch/m68k/include/mm_virt.h deleted file mode 100644 index 01480b9b..00000000 --- a/Kernel/arch/m68k/include/mm_virt.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Acess2 M68000 port - * - By John Hodge (thePowersGang) - * - * arch/m68k/include/mm_virt.h - * - Virtual memory addresses - */ -#ifndef _M68K_MM_VIRT_H_ -#define _M68K_MM_VIRT_H_ - -#define MM_KHEAP_BASE 0 -#define MM_KHEAP_MAX 0 - -#define MM_USER_MIN 0 -#define USER_LIB_MAX 0 -#define MM_MODULE_MIN 0 -#define MM_MODULE_MAX 0 - -#define MM_PPD_HANDLES 0 -#define MM_KERNEL_VFS 0 - -#endif - diff --git a/Kernel/arch/m68k/include/proc.h b/Kernel/arch/m68k/include/proc.h deleted file mode 100644 index 8cf6aa0c..00000000 --- a/Kernel/arch/m68k/include/proc.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Acess2 M68000 port - * - By John Hodge (thePowersGang) - * - * arch/m68k/include/proc.h - * - Task management defs - */ -#ifndef _M68K_PROC_H_ -#define _M68K_PROC_H_ - -#define MAX_CPUS 1 - -typedef int tMemoryState; // Unused - -typedef struct { - Uint32 IP; - Uint32 SP; -} tTaskState; - -typedef struct { - Uint32 Num; - union { - Uint32 Arg1; - Uint32 Return; - }; - union { - Uint32 Arg2; - Uint32 RetHi; - }; - union { - Uint32 Arg3; - Uint32 Error; - }; - Uint32 Arg4; - Uint32 Arg5; - Uint32 Arg6; -} tSyscallRegs; - -#endif - diff --git a/Kernel/arch/m68k/lib.c b/Kernel/arch/m68k/lib.c deleted file mode 100644 index 2888a318..00000000 --- a/Kernel/arch/m68k/lib.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Acess2 M68K port - * - By John Hodge (thePowersGang) - * - * arch/m68k/lib.c - * - Library functions - */ -#include -#include "../helpers.h" -#include - -Uint64 __divmod64(Uint64 Num, Uint64 Den, Uint64 *Rem); -Uint64 __udivdi3(Uint64 Num, Uint64 Den); -Uint64 __umoddi3(Uint64 Num, Uint64 Den); - -// === CODE === -void *memcpy(void *__dest, const void *__src, size_t __len) -{ - register Uint8 *dest = __dest; - register const Uint8 *src = __src; - - while(__len --) - *dest++ = *src++; - - return __dest; -} - -void *memset(void *__dest, int __val, size_t __count) -{ - register Uint8 *dest = __dest; - - while(__count --) - *dest = __val; - - return __dest; -} - -int memcmp(const void *__p1, const void *__p2, size_t __maxlen) -{ - const char *m1 = __p1, *m2 = __p2; - while( __maxlen-- ) - { - if(*m1 != *m2) return *m1 - *m2; - } - return 0; -} - -DEF_DIVMOD(64) - -Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem) -{ - Uint64 ret; - if(Den == 0) return 0; // TODO: #div0 - if(Num < Den) { - if(Rem) *Rem = Num; - return 0; - } - if(Num == 0) { - if(Rem) *Rem = 0; - return 0; - } - if(Den == 1) { - if(Rem) *Rem = 0; - return Num; - } - if(Den == 2) { - if(Rem) *Rem = Num & 1; - return Num >> 1; - } - if(Den == 16) { - if(Rem) *Rem = Num & 0xF; - return Num >> 4; - } - if(Den == 32) { - if(Rem) *Rem = Num & 0x1F; - return Num >> 5; - } - if(Den == 0x1000) { - if(Rem) *Rem = Num & 0xFFF; - return Num >> 12; - } - - if( !(Den >> 32) && !(Num >> 32) ) { - if(Rem) *Rem = (Uint32)Num % (Uint32)Den; // Clear high bits - return (Uint32)Num / (Uint32)Den; - } - - ret = __divmod64(Num, Den, Rem); - return ret; -} - -// Unsigned Divide 64-bit Integer -Uint64 __udivdi3(Uint64 Num, Uint64 Den) -{ - return DivMod64U(Num, Den, NULL); -} - -// Unsigned Modulus 64-bit Integer -Uint64 __umoddi3(Uint64 Num, Uint64 Den) -{ - Uint64 ret = 0; - DivMod64U(Num, Den, &ret); - return ret; -} - -// ---- PCI (stubbed) -Uint32 PCI_CfgReadDWord(Uint32 Addr) -{ - return 0xFFFFFFFF; -} -void PCI_CfgWriteDWord(Uint32 Addr, Uint32 Data) -{ -} - diff --git a/Kernel/arch/m68k/link.ld b/Kernel/arch/m68k/link.ld deleted file mode 100644 index c0b0c9d4..00000000 --- a/Kernel/arch/m68k/link.ld +++ /dev/null @@ -1,40 +0,0 @@ -ENTRY (_start) - -_kernel_base = 0x0; - -SECTIONS -{ - . = 0; - .init : - { - *(.init) - } - . += _kernel_base; - .text : AT( ADDR(.text) - _kernel_base ) - { - *(.text*) - *(.rodata*) - } - /* 0x4000 (4 pages) alignment needed for root table */ - .data ALIGN(0x4000) : AT( ADDR(.data) - _kernel_base ) - { - *(.padata) - *(.data*) - - gKernelSymbols = .; - *(KEXPORT) - gKernelSymbolsEnd = .; - - gKernelModules = .; - *(KMODULES) - gKernelModulesEnd = .; - } - .bss : AT( ADDR(.bss) - _kernel_base ) - { - *(.bss*) - *(COMMON*) - . = ALIGN(0x1000); - *(.pabss) - } - gKernelEnd = .; -} diff --git a/Kernel/arch/m68k/main.c b/Kernel/arch/m68k/main.c deleted file mode 100644 index 3f78e4bb..00000000 --- a/Kernel/arch/m68k/main.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Acess2 M68K port - * - By John Hodge (thePowersGang) - * - * arch/m68k/main.c - * - C entrypoint - */ -#include -#include - -// === PROTOTYPES === -void kmain(void); - -// === CODE === -void kmain(void) -{ - LogF("Acess2 m68k v"EXPAND_STR(KERNEL_VERSION)"\n"); - LogF(" Build %i, Git Hash %s\n", BUILD_NUM, gsGitHash); - -} - -void Arch_LoadBootModules(void) -{ - -} - diff --git a/Kernel/arch/m68k/mm_phys.c b/Kernel/arch/m68k/mm_phys.c deleted file mode 100644 index 24fd1f2c..00000000 --- a/Kernel/arch/m68k/mm_phys.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Acess2 M68K port - * - By John Hodge (thePowersGang) - * - * arch/m68k/mm_phys.c - * - Stubbed physical memory management - */ -#include - -// === CODE === -void MM_RefPhys(tPAddr Page) -{ - // TODO: Refcount pages - Log_Warning("MMPhys", "TODO: Implement MM_RefPhys"); -} - -int MM_SetPageNode(tPAddr Page, void *Node) -{ - Log_Warning("MMPhys", "TODO: Implement MM_SetPageNode"); - return -1; -} - diff --git a/Kernel/arch/m68k/mm_virt.c b/Kernel/arch/m68k/mm_virt.c deleted file mode 100644 index 88990a1e..00000000 --- a/Kernel/arch/m68k/mm_virt.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Acess2 M68K port - * - By John Hodge (thePowersGang) - * - * arch/m68k/mm_virt.c - * - Stubbed virtual memory management (no MMU) - */ -#include -#include -#include - -// === CODE === -tPAddr MM_GetPhysAddr(tVAddr Addr) -{ - return Addr; -} - -void MM_SetFlags(tVAddr Addr, Uint Val, Uint Mask) -{ - return ; -} - -Uint MM_GetFlags(tVAddr Addr) -{ - return 0; -} - -int MM_Map(tVAddr Dest, tPAddr Src) -{ - Dest &= (PAGE_SIZE-1); - Src &= (PAGE_SIZE-1); - if(Dest != Src) - memcpy((void*)Dest, (void*)Src, PAGE_SIZE); - return 0; -} - -tPAddr MM_Allocate(tVAddr Dest) -{ - return Dest; -} - -void MM_Deallocate(tVAddr Addr) -{ -} - -void MM_ClearUser(void) -{ -} - -void MM_DumpTables(tVAddr Start, tVAddr End) -{ - -} - diff --git a/Kernel/arch/m68k/proc.c b/Kernel/arch/m68k/proc.c deleted file mode 100644 index c148bff7..00000000 --- a/Kernel/arch/m68k/proc.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Acess2 M68K port - * - By John Hodge (thePowersGang) - * - * arch/m68k/proc.c - * - Multithreading - */ -#include -#include -#include - -// === IMPORTS === -extern tThread gThreadZero; - -// === GLOBALS === -tThread *gpCurrentThread = &gThreadZero; - -// === CODE === -void ArchThreads_Init(void) -{ -} - -void Proc_Start(void) -{ -} - -tThread *Proc_GetCurThread(void) -{ - return gpCurrentThread; -} - -int GetCPUNum(void) -{ - return 0; -} - -tTID Proc_Clone(Uint Flags) -{ - UNIMPLEMENTED(); - return -1; -} - -tTID Proc_NewKThread(tThreadFunction Fcn, void *Arg) -{ - UNIMPLEMENTED(); - return -1; -} - -tTID Proc_SpawnWorker(tThreadFunction Fcn, void *Arg) -{ - UNIMPLEMENTED(); - return -1; -} - -void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, char **ArgV, int DataSize) -{ - Log_KernelPanic("Proc", "TODO: Implement Proc_StartUser"); - for(;;); -} - -void Proc_CallFaultHandler(tThread *Thread) -{ - -} - -void Proc_DumpThreadCPUState(tThread *Thread) -{ - -} - -void Proc_Reschedule(void) -{ - Log_Notice("Proc", "TODO: Implement Proc_Reschedule"); -} - diff --git a/Kernel/arch/m68k/time.c b/Kernel/arch/m68k/time.c deleted file mode 100644 index 0fffaf66..00000000 --- a/Kernel/arch/m68k/time.c +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Acess2 M68K port - * - By John Hodge (thePowersGang) - * - * arch/m68k/time.c - * - Timekeeping - */ -#include - -// === CODE === -Sint64 now(void) -{ - return 0; -} diff --git a/Kernel/arch/x86/Makefile b/Kernel/arch/x86/Makefile deleted file mode 100644 index c83a5af2..00000000 --- a/Kernel/arch/x86/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# -# Acess2 Kernel -# i386 Architecture Makefile -# arch/i386/Makefile - -AS_SUFFIX = asm - -CPPFLAGS = -CFLAGS = -ASFLAGS = -f elf - -USE_MP=0 - -ifeq ($(PLATFORM),default) - USE_MP=0 -else ifeq ($(PLATFORM),smp) - USE_MP=1 -endif - -ASFLAGS += -D USE_MP=$(USE_MP) -CPPFLAGS += -DUSE_MP=$(USE_MP) - -A_OBJ = start.ao main.o lib.o desctab.ao errors.o irq.o -A_OBJ += mm_phys.o mm_virt.o -A_OBJ += proc.o proc.ao time.o vm8086.o -A_OBJ += kpanic.o pci.o diff --git a/Kernel/arch/x86/desctab.asm b/Kernel/arch/x86/desctab.asm deleted file mode 100644 index 6eaa651a..00000000 --- a/Kernel/arch/x86/desctab.asm +++ /dev/null @@ -1,327 +0,0 @@ -; AcessOS Microkernel Version -; -; desctab.asm -[BITS 32] - - -[section .data] -; IDT -ALIGN 8 -[global gIDT] -gIDT: - ; CS = 0x08, Type = 32-bit Interrupt (0xE = 1 110) - times 256 dd 0x00080000,0x00000E00 -[global gIDTPtr] -gIDTPtr: - dw 256 * 16 - 1 ; Limit - dd gIDT ; Base - -[section .text] - -[global Desctab_Install] -Desctab_Install: - ; Set up IDT - ; Helper Macros - ; - Set an IDT entry to an ISR -%macro SETISR 1 - mov eax, Isr%1 - mov WORD [gIDT + %1*8], ax - shr eax, 16 - mov WORD [gIDT + %1*8+6], ax - ; Enable - mov ax, WORD [gIDT + %1*8 + 4] - or ax, 0x8000 - mov WORD [gIDT + %1*8 + 4], ax -%endmacro - ; Enable user calling of an ISR -%macro SET_USER 1 - or WORD [gIDT + %1*8 + 4], 0x6000 -%endmacro - ; Set an ISR as a trap (leaves interrupts enabled when invoked) -%macro SET_TRAP 1 - or WORD [gIDT + %1*8 + 4], 0x0100 -%endmacro - - ; Error handlers - %assign i 0 - %rep 32 - SETISR i - %assign i i+1 - %endrep - - ; User Syscall - SETISR 0xAC - SET_USER 0xAC - SET_TRAP 0xAC ; Interruptable - - ; MP ISRs - %if USE_MP - SETISR 0xED ; 0xED Inter-processor HALT - SETISR 0xEE ; 0xEE Timer - SETISR 0xEF ; 0xEF Spurious Interrupt - %endif - - ; IRQs - %assign i 0xF0 - %rep 16 - SETISR i - %assign i i+1 - %endrep - - ; Load IDT - lidt [gIDTPtr] - - ; Remap PIC - push edx ; Save EDX - mov dx, 0x20 - mov al, 0x11 - out dx, al ; Init Command - mov dx, 0x21 - mov al, 0xF0 - out dx, al ; Offset (Start of IDT Range) - mov al, 0x04 - out dx, al ; IRQ connected to Slave (00000100b) = IRQ2 - mov al, 0x01 - out dx, al ; Set Mode - mov al, 0x00 - out dx, al ; Set Mode - - mov dx, 0xA0 - mov al, 0x11 - out dx, al ; Init Command - mov dx, 0xA1 - mov al, 0xF8 - out dx, al ; Offset (Start of IDT Range) - mov al, 0x02 - out dx, al ; IRQ Line connected to master - mov al, 0x01 - out dx, al ; Set Mode - mov dl, 0x00 - out dx, al ; Set Mode - pop edx - - ret - - -; =============== -; = Define ISRs = -; =============== -%macro ISR_ERRNO 1 -[global Isr%1] -Isr%1: - ;xchg bx, bx - push %1 - jmp ErrorCommon -%endmacro -%macro ISR_NOERR 1 -[global Isr%1] -Isr%1: - ;xchg bx, bx - push 0 - push %1 - jmp ErrorCommon -%endmacro - -%macro DEF_SYSCALL 1 -[global Isr%1] -Isr%1: - push 0 - push %1 - jmp SyscallCommon -%endmacro - -%macro DEF_IRQ 1 -[global Isr%1] -Isr%1: - push 0 - push %1 - jmp IRQCommon -%endmacro - -ISR_NOERR 0; 0: Divide By Zero Exception -ISR_NOERR 1; 1: Debug Exception -ISR_NOERR 2; 2: Non Maskable Interrupt Exception -ISR_NOERR 3; 3: Int 3 Exception -ISR_NOERR 4; 4: INTO Exception -ISR_NOERR 5; 5: Out of Bounds Exception -ISR_NOERR 6; 6: Invalid Opcode Exception -ISR_NOERR 7; 7: Coprocessor Not Available Exception -ISR_ERRNO 8; 8: Double Fault Exception (With Error Code!) -ISR_NOERR 9; 9: Coprocessor Segment Overrun Exception -ISR_ERRNO 10; 10: Bad TSS Exception (With Error Code!) -ISR_ERRNO 11; 11: Segment Not Present Exception (With Error Code!) -ISR_ERRNO 12; 12: Stack Fault Exception (With Error Code!) -ISR_ERRNO 13; 13: General Protection Fault Exception (With Error Code!) -ISR_ERRNO 14; 14: Page Fault Exception (With Error Code!) -ISR_NOERR 15; 15: Reserved Exception -ISR_NOERR 16; 16: Floating Point Exception -ISR_NOERR 17; 17: Alignment Check Exception -ISR_NOERR 18; 18: Machine Check Exception -ISR_NOERR 19; 19: Reserved -ISR_NOERR 20; 20: Reserved -ISR_NOERR 21; 21: Reserved -ISR_NOERR 22; 22: Reserved -ISR_NOERR 23; 23: Reserved -ISR_NOERR 24; 24: Reserved -ISR_NOERR 25; 25: Reserved -ISR_NOERR 26; 26: Reserved -ISR_NOERR 27; 27: Reserved -ISR_NOERR 28; 28: Reserved -ISR_NOERR 29; 29: Reserved -ISR_NOERR 30; 30: Reserved -ISR_NOERR 31; 31: Reserved - -DEF_SYSCALL 0xAC ; Acess System Call - -%if USE_MP -[global Isr0xED] -; 0xED - Interprocessor HALT -Isr0xED: - cli -.jmp: hlt - jmp .jmp - -[global Isr0xEE] -[extern SchedulerBase] -; AP's Timer Interrupt -Isr0xEE: - push eax ; Line up with interrupt number - mov eax, dr1 ; CPU Number - push eax - mov eax, [esp+4] ; Load EAX back - jmp SchedulerBase -; Spurious Interrupt -[global Isr0xEF] -Isr0xEF: - xchg bx, bx ; MAGIC BREAK - iret -%endif - -; IRQs -; - Timer -[global Isr240] -[global Isr240.jmp] -[extern SchedulerBase] -[extern SetAPICTimerCount] -Isr240: - push 0 ; Line up with Argument in errors - push 0 ; CPU Number - ;xchg bx, bx ; MAGIC BREAK -Isr240.jmp: - %if USE_MP - jmp SetAPICTimerCount ; This is reset once the bus speed has been calculated - %else - jmp SchedulerBase - %endif -; - Assignable -%assign i 0xF1 -%rep 16 - DEF_IRQ i -%assign i i+1 -%endrep - -; --------------------- -; Common error handling -; --------------------- -[extern ErrorHandler] -ErrorCommon: - ;xchg bx, bx ; MAGIC BREAK - - pusha - push ds - push es - push fs - push gs - - ; Clear TF -; pushf -; and WORD [esp], 0xFEFF -; popf - - mov ax, 0x10 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - - push esp - call ErrorHandler - add esp, 4 - - pop gs - pop fs - pop es - pop ds - popa - add esp, 8 ; Error Code and ID - iret - -; -------------------------- -; Common System Call Handler -; -------------------------- -[extern SyscallHandler] -SyscallCommon: - pusha - push ds - push es - push fs - push gs - - push esp - call SyscallHandler - add esp, 4 - - ; Pass changes to TF on to the user - ; EFLAGS is stored at ESP[4+8+2+2] - ; 4 Segment Registers - ; 8 GPRs - ; 2 Error Code / Interrupt ID - ; 2 CS/EIP - pushf - pop eax - and eax, 0x100 ; 0x100 = Trace Flag - and WORD [esp+(4+8+2+2)*4], ~0x100 ; Clear - or DWORD [esp+(4+8+2+2)*4], eax ; Set for user - - pop gs - pop fs - pop es - pop ds - popa - add esp, 8 ; Error Code and ID - iret - -; ------------ -; IRQ Handling -; ------------ -[extern IRQ_Handler] -[global IRQCommon] -[global IRQCommon_handled] -IRQCommon_handled equ IRQCommon.handled -IRQCommon: - pusha - push ds - push es - push fs - push gs - - mov ax, 0x10 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - - push esp - call IRQ_Handler -.handled: - add esp, 4 - - pop gs - pop fs - pop es - pop ds - popa - add esp, 8 ; Error Code and ID - iret - -; vim: ft=nasm ts=8 diff --git a/Kernel/arch/x86/errors.c b/Kernel/arch/x86/errors.c deleted file mode 100644 index d2d2cef1..00000000 --- a/Kernel/arch/x86/errors.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Acess2 - x86 Architecture - * arch/x86/errors.c - * - CPU Error Handler - */ -#include -#include -#include - -// === CONSTANTS === -#define MAX_BACKTRACE 8 //!< Maximum distance to trace the stack backwards - -// === IMPORTS === -extern void MM_PageFault(Uint Addr, Uint ErrorCode, tRegs *Regs); -extern void VM8086_GPF(tRegs *Regs); -extern void Threads_Dump(void); -extern void Threads_Fault(int Num); -extern int GetCPUNum(void); -extern void MM_DumpTables(tVAddr, tVAddr); -extern void Proc_EnableSSE(void); -extern void Proc_RestoreSSE(Uint32 Data); - -// === PROTOTYPES === -void __stack_chk_fail(void); -void ErrorHandler(tRegs *Regs); -void Proc_PrintBacktrace(void); -void Error_Backtrace(Uint eip, Uint ebp); -void StartupPrint(char *Str); - -// === GLOBALS === -const char *csaERROR_NAMES[] = { - "Divide By Zero", "Debug", "NMI Exception", "INT3", - "INTO Instr - Overflow", "BOUND Instr - Out of Bounds", "Invalid Opcode", "Coprocessor not avaliable", - "Double Fault", "Coprocessor Segment Overrun", "Bad TSS", "Segment Not Present", - "Stack Fault Exception", "GPF", "#PF", "Reserved", - "Floating Point Exception", "Alignment Check Exception", "Machine Check Exception", "Reserved", - "Reserved", "Reserved", "Reserved", "Reserved", - "Reserved", "Reserved", "Reserved", "Reserved", - "Reserved", "Reserved", "Reserved", "Reserved" - }; - -// === CODE === -/** - * \brief Keeps GCC happy - */ -void __stack_chk_fail(void) -{ - Panic("FATAL ERROR: Stack Check Failed\n"); - for(;;); -} - -/** - * \fn void ErrorHandler(tRegs *Regs) - * \brief General Error Handler - * \param Regs Register state at error - */ -void ErrorHandler(tRegs *Regs) -{ - Uint cr; - - //if( Regs && !(Regs->int_num == 13 && Regs->eflags & 0x20000) ) - // __asm__ __volatile__ ("xchg %bx, %bx"); - //Log_Debug("X86", "Regs = %p", Regs); - //Log_Debug("X86", "Error %i at 0x%08x", Regs->int_num, Regs->eip); - - __asm__ __volatile__ ("cli"); - - // Debug exception (used for single-stepping) - if(Regs->int_num == 1) - { - static Uint32 lastEIP = 0; - tThread *thread = Proc_GetCurThread(); - if( Regs->eip == lastEIP ) - return; - Log("%p(%i %s) IP=%08x", thread, thread->TID, thread->ThreadName, Regs->eip); - lastEIP = Regs->eip; - return ; - } - - // Page Fault - if(Regs->int_num == 14) - { - __asm__ __volatile__ ("sti"); // Should be OK, TODO: Test - __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr)); - MM_PageFault( cr, Regs->err_code, Regs ); - return ; - } - - // #NM - Coprocessor unavaliable - if(Regs->int_num == 7) - { - tThread *thread = Proc_GetCurThread(); - if(!thread->SavedState.bSSEModified) - { - Proc_EnableSSE(); - if(!thread->SavedState.SSE) - thread->SavedState.SSE = malloc(sizeof(tSSEState) + 0xF); - else - Proc_RestoreSSE( ((Uint)thread->SavedState.SSE + 0xF) & ~0xF ); - thread->SavedState.bSSEModified = 1; - __asm__ __volatile__ ("sti"); - return ; - } - // oops, SSE enabled but a #NM, bad news - } - - // VM8086 GPF - if(Regs->int_num == 13 && Regs->eflags & 0x20000) - { - VM8086_GPF(Regs); - return ; - } - - // Check if it's a user mode fault - if( (Regs->cs & 3) == 3 ) { - Log_Warning("Arch", "User Fault - %s, Code: 0x%x", - csaERROR_NAMES[Regs->int_num], Regs->err_code); - Log_Warning("Arch", "at CS:EIP %04x:%08x", - Regs->cs, Regs->eip); - MM_DumpTables(0, KERNEL_BASE); - switch( Regs->int_num ) - { - // Division by Zero - case 0: Threads_Fault(FAULT_DIV0); break; - // Invalid opcode - case 6: Threads_Fault(FAULT_OPCODE); break; - // GPF - case 13: Threads_Fault(FAULT_ACCESS); break; - // Floating Point Exception - case 16: Threads_Fault(FAULT_FLOAT); break; - - default: Threads_Fault(FAULT_MISC); break; - } - return ; - } - - Debug_KernelPanic(); - - LogF("CPU %i Error %i - %s, Code: 0x%x - At %08x\n", - GetCPUNum(), - Regs->int_num, csaERROR_NAMES[Regs->int_num], Regs->err_code, - Regs->eip); - - //Warning("CPU Error %i - %s, Code: 0x%x", - // Regs->int_num, csaERROR_NAMES[Regs->int_num], Regs->err_code); - //Warning(" CS:EIP = 0x%04x:%08x", Regs->cs, Regs->eip); - __ASM__ ("xchg %bx, %bx"); - if(Regs->cs == 0x08) - Warning(" SS:ESP = 0x0010:%08x", (Uint)Regs+sizeof(tRegs)); - else - Warning(" SS:ESP = 0x%04x:%08x", Regs->ss, Regs->esp); - Warning(" EFLAGS = 0x%08x", Regs->eflags); - Warning(" EAX %08x ECX %08x EDX %08x EBX %08x", - Regs->eax, Regs->ecx, Regs->edx, Regs->ebx); - Warning(" ESP %08x EBP %08x ESI %08x EDI %08x", - Regs->esp, Regs->ebp, Regs->esi, Regs->edi); - Warning(" DS %04x ES %04x FS %04x GS %04x", - Regs->ds, Regs->es, Regs->fs, Regs->gs); - - // Control Registers - __asm__ __volatile__ ("mov %%cr0, %0":"=r"(cr)); - Warning(" CR0 0x%08x", cr); - __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr)); - Warning(" CR2 0x%08x", cr); - __asm__ __volatile__ ("mov %%cr3, %0":"=r"(cr)); - Warning(" CR3 0x%08x", cr); - - switch( Regs->int_num ) - { - case 6: // #UD - Warning(" Offending bytes: %02x %02x %02x %02x", - *(Uint8*)(Regs->eip+0), *(Uint8*)(Regs->eip+1), - *(Uint8*)(Regs->eip+2), *(Uint8*)(Regs->eip+3)); - break; - } - - // Print Stack Backtrace - Error_Backtrace(Regs->eip, Regs->ebp); - - // Dump running threads - Threads_Dump(); - - for(;;) __asm__ __volatile__ ("hlt"); -} - -void Proc_PrintBacktrace(void) -{ - Uint32 ebp; - __asm__ __volatile__ ("mov %%ebp, %0" : "=r" (ebp)); - Error_Backtrace( *(Uint32*)(ebp+4), *(Uint32*)ebp ); -} - -/** - * \fn void Error_Backtrace(Uint eip, Uint ebp) - * \brief Unrolls the stack to trace execution - * \param eip Current Instruction Pointer - * \param ebp Current Base Pointer (Stack Frame) - */ -void Error_Backtrace(Uint eip, Uint ebp) -{ - int i = 0; -// Uint delta = 0; -// char *str = NULL; - - //if(eip < 0xC0000000 && eip > 0x1000) - //{ - // LogF("Backtrace: User - 0x%x\n", eip); - // return; - //} - - #if 0 - if(eip > 0xE0000000) - { - LogF("Backtrace: Data Area - 0x%x\n", eip); - return; - } - - if(eip > 0xC8000000) - { - LogF("Backtrace: Kernel Module - 0x%x\n", eip); - return; - } - #endif - - //str = Debug_GetSymbol(eip, &delta); -// if(str == NULL) - LogF("Backtrace: 0x%x", eip); -// else -// LogF("Backtrace: %s+0x%x", str, delta); - if(!MM_GetPhysAddr(ebp)) - { - LogF("\nBacktrace: Invalid EBP, stopping\n"); - return; - } - - - while( MM_GetPhysAddr(ebp) && i < MAX_BACKTRACE ) - { - if( ebp >= MM_KERNEL_STACKS_END ) break; - //str = Debug_GetSymbol(*(Uint*)(ebp+4), &delta); -// if(str == NULL) - LogF(" >> 0x%x", *(Uint*)(ebp+4)); -// else -// LogF(" >> %s+0x%x", str, delta); - ebp = *(Uint*)ebp; - i++; - } - LogF("\n"); -} - -/** - * \fn void StartupPrint(char *Str) - * \brief Str String to print - * \note WHY IS THIS HERE?!?! - */ -void StartupPrint(char *Str) -{ - Uint16 *buf = (void*)0xC00B8000; - int i = 0; - static int line = 0; - while(*Str) - { - buf[line*80 + i++] = *Str | 0x0700; - Str ++; - } - - // Clear the rest of the line - while(i < 80) - buf[line*80 + i++] = 0x0720; - - line ++; - if(line == 25) - { - line --; - memcpy(buf, &buf[80], 80*24*2); - memset(&buf[80*24], 0, 80*2); - } -} - -// === EXPORTS === -EXPORT(__stack_chk_fail); diff --git a/Kernel/arch/x86/include/arch.h b/Kernel/arch/x86/include/arch.h deleted file mode 100644 index 8a987058..00000000 --- a/Kernel/arch/x86/include/arch.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Acess2 - * - x86 Architecture - * arch/x86/include/arch.h - */ -#ifndef _ARCH_H_ -#define _ARCH_H_ - -// - Base Defintions -#define KERNEL_BASE 0xC0000000 -#define BITS 32 -#define PAGE_SIZE 0x1000 - -#define INVLPTR ((void*)-1) - -// Allow nested spinlocks? -#define LOCK_DISABLE_INTS 1 - -// - Processor/Machine Specific Features -#if ARCH != x86 && ARCH != x86_smp -# error "Unknown architecture '" #ARCH "'" -#endif - -#if USE_MP -# define MAX_CPUS 8 -#else -# define MAX_CPUS 1 -#endif - -#if USE_PAE -# define PHYS_BITS 48 -#else -# define PHYS_BITS 32 -#endif - -#define __ASM__ __asm__ __volatile__ - -// === Spinlocks === -/** - * \brief Short Spinlock structure - */ -struct sShortSpinlock { - volatile int Lock; //!< Lock value - - #if LOCK_DISABLE_INTS - int IF; //!< Interrupt state on call to SHORTLOCK - #endif -}; - -// === MACROS === -/** - * \brief Halt the CPU (shorter version of yield) - */ -#if 1 -#define HALT() do { \ - Uint32 flags; \ - __asm__ __volatile__ ("pushf;pop %0" : "=a"(flags)); \ - if( !(flags & 0x200) ) Panic("HALT called with interrupts disabled"); \ - __asm__ __volatile__ ("hlt"); \ -} while(0) -#else -#define HALT() __asm__ __volatile__ ("hlt") -#endif -/** - * \brief Fire a magic breakpoint (bochs) - */ -#define MAGIC_BREAK() __asm__ __volatile__ ("xchg %bx, %bx") - -// === TYPES === -typedef unsigned int Uint; // Unsigned machine native integer -typedef unsigned char Uint8; -typedef unsigned short Uint16; -typedef unsigned long Uint32; -typedef unsigned long long Uint64; -typedef signed int Sint; // Signed Machine Native integer -typedef signed char Sint8; -typedef signed short Sint16; -typedef signed long Sint32; -typedef signed long long Sint64; -typedef Uint size_t; -typedef char BOOL; - -typedef Uint32 tPAddr; -typedef Uint32 tVAddr; - -typedef struct { - Uint32 gs, fs, es, ds; - Uint32 edi, esi, ebp, kesp; - Uint32 ebx, edx, ecx, eax; - Uint32 int_num, err_code; - Uint32 eip, cs; - Uint32 eflags, esp, ss; -} tRegs; - -typedef struct { - Uint Resvd1[4]; // GS, FS, ES, DS - Uint Arg4, Arg5; // EDI, ESI - Uint Arg6; // EBP - Uint Resvd2[1]; // Kernel ESP - union { - Uint Arg1; - Uint Error; - }; // EBX - union { - Uint Arg3; - Uint RetHi; // High 32 bits of ret - }; // EDX - Uint Arg2; // ECX - union { - Uint Num; - Uint Return; - }; // EAX - Uint Resvd3[5]; // Int, Err, Eip, CS, ... - Uint StackPointer; // ESP - Uint Resvd4[1]; // SS -} tSyscallRegs; - -// === FUNCTIONS === -extern void Debug_PutCharDebug(char ch); -extern void Debug_PutStringDebug(const char *String); - -extern int IS_LOCKED(struct sShortSpinlock *Lock); -extern int CPU_HAS_LOCK(struct sShortSpinlock *Lock); -extern void SHORTLOCK(struct sShortSpinlock *Lock); -extern void SHORTREL(struct sShortSpinlock *Lock); - -#endif // !defined(_ARCH_H_) diff --git a/Kernel/arch/x86/include/arch_int.h b/Kernel/arch/x86/include/arch_int.h deleted file mode 100644 index 81ea2d77..00000000 --- a/Kernel/arch/x86/include/arch_int.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * x86 Arch - Internal Definitions - * - arch/x86/include/arch_int.h - */ -#ifndef _ARCH_INT_H_ -#define _ARCH_INT_H_ - -/** - * \brief Spinlock primative atomic set-if-zero loop - */ -extern void __AtomicTestSetLoop(Uint *Ptr, Uint Value); - -/** - * \brief Clear and free an address space - */ -extern void MM_ClearSpace(Uint32 CR3); - -#endif - diff --git a/Kernel/arch/x86/include/desctab.h b/Kernel/arch/x86/include/desctab.h deleted file mode 100644 index 47a462aa..00000000 --- a/Kernel/arch/x86/include/desctab.h +++ /dev/null @@ -1,25 +0,0 @@ -/** - */ -#ifndef _DESCTAB_H_ -#define _DESCTAB_H_ - -typedef struct { - Uint16 LimitLow; - Uint16 BaseLow; - Uint8 BaseMid; - Uint8 Access; - struct { - unsigned LimitHi: 4; - unsigned Flags: 4; - } __attribute__ ((packed)); - Uint8 BaseHi; -} __attribute__ ((packed)) tGDT; - -typedef struct { - Uint16 OffsetLo; - Uint16 CS; - Uint16 Flags; - Uint16 OffsetHi; -} __attribute__ ((packed)) tIDT; - -#endif diff --git a/Kernel/arch/x86/include/mm_phys.h b/Kernel/arch/x86/include/mm_phys.h deleted file mode 100644 index 96d3e64a..00000000 --- a/Kernel/arch/x86/include/mm_phys.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * AcessOS Microkernel Version - * mm_phys.h - */ -#ifndef _MM_PHYS_H -#define _MM_PHYS_H - -// === FUNCTIONS === -//extern tPAddr MM_AllocPhys(void); -//extern tPAddr MM_AllocPhysRange(int Pages, int MaxBits); -//extern void MM_RefPhys(tPAddr PAddr); -//extern void MM_DerefPhys(tPAddr PAddr); -//extern int MM_GetRefCount(tPAddr Addr); - -#endif diff --git a/Kernel/arch/x86/include/mm_virt.h b/Kernel/arch/x86/include/mm_virt.h deleted file mode 100644 index d84c9650..00000000 --- a/Kernel/arch/x86/include/mm_virt.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Acess2 - * - Virtual Memory Manager (Header) - */ -#ifndef _MM_VIRT_H -#define _MM_VIRT_H - -// NOTES: -// - 1PD is 0x400000 - -// - Memory Layout -#define MM_USER_MIN 0x00200000 -#define USER_STACK_SZ 0x00020000 // 128 KiB -#define USER_STACK_TOP 0x00800000 -#define USER_LIB_MAX 0xBC000000 -#define MM_USER_MAX 0xBC000000 // Top load address for user libraries -#define MM_PPD_MIN 0xBC000000 // Per-Process Data base -#define MM_PPD_HANDLES 0xBC000000 // - VFS Handles (Practically unlimited) -#define MM_PPD_MMAP 0xBD000000 // - MMap Entries (24b each = 0x2AAAA max) -#define MM_PPD_UNALLOC 0xBE000000 // -#define MM_PPD_CFG 0xBFFFF000 // - Per-process config entries -#define MM_PPD_MAX 0xC0000000 // - -#define MM_KHEAP_BASE 0xC0400000 // C+4MiB -#define MM_KHEAP_MAX 0xCF000000 // -#define MM_KERNEL_VFS 0xCF000000 // -#define MM_KUSER_CODE 0xCFFF0000 // 16 Pages -#define MM_MODULE_MIN 0xD0000000 // Lowest Module Address -#define MM_MODULE_MAX 0xE0000000 // 128 MiB - -// Page Info (Which VFS node owns each physical page) -// 2^32/2^12*16 -// = 2^24 = 16 MiB = 0x4000000 -// 256 items per page -#define MM_PAGENODE_BASE 0xE0000000 - -// Needs (2^32/2^12*4 bytes) -// - 2^22 bytes max = 4 MiB = 0x1000000 -// 1024 items per page -#define MM_REFCOUNT_BASE 0xE4000000 - -#define MM_KERNEL_STACKS 0xF0000000 -#define MM_KERNEL_STACK_SIZE 0x00008000 -#define MM_KERNEL_STACKS_END 0xFC000000 - -// === FUNCTIONS === -extern void MM_FinishVirtualInit(void); -extern void MM_SetCR3(Uint CR3); -extern tPAddr MM_Clone(int bCloneUser); -extern tVAddr MM_NewKStack(void); -extern tVAddr MM_NewWorkerStack(Uint *InitialStack, size_t StackSize); - -#endif diff --git a/Kernel/arch/x86/include/mp.h b/Kernel/arch/x86/include/mp.h deleted file mode 100644 index 0dd5a1e7..00000000 --- a/Kernel/arch/x86/include/mp.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - */ -#ifndef _MP_H -#define _MP_H - -#define MPPTR_IDENT ('_'|('M'<<8)|('P'<<16)|('_'<<24)) -#define MPTABLE_IDENT ('P'|('C'<<8)|('M'<<16)|('P'<<24)) - -typedef struct sMPInfo { - Uint32 Sig; // '_MP_' - Uint32 MPConfig; - Uint8 Length; - Uint8 Version; - Uint8 Checksum; - Uint8 Features[5]; // 2-4 are unused -} tMPInfo; - -typedef union uMPTable_Ent { - Uint8 Type; - struct { - Uint8 Type; - Uint8 APICID; - Uint8 APICVer; - Uint8 CPUFlags; // bit 0: Enabled, bit 1: Boot Processor - Uint32 CPUSignature; // Stepping, Model, Family - Uint32 FeatureFlags; - Uint32 Reserved[2]; - } __attribute__((packed)) Proc; // 0x00 - struct { - Uint8 Type; - Uint8 ID; - char TypeString[6]; - } __attribute__((packed)) Bus; // 0x01 - struct { - Uint8 Type; - Uint8 ID; - Uint8 Version; - Uint8 Flags; // bit 0: Enabled - Uint32 Addr; - } __attribute__((packed)) IOAPIC; // 0x02 - struct { - Uint8 Type; - Uint8 IntType; - Uint16 Flags; // 0,1: Polarity, 2,3: Trigger Mode - Uint8 SourceBusID; - Uint8 SourceBusIRQ; - Uint8 DestAPICID; - Uint8 DestAPICIRQ; - } __attribute__((packed)) IOInt; - struct { - Uint8 Type; - Uint8 IntType; - Uint16 Flags; // 0,1: Polarity, 2,3: Trigger Mode - Uint8 SourceBusID; - Uint8 SourceBusIRQ; - Uint8 DestLocalAPICID; - Uint8 DestLocalAPICIRQ; - } __attribute__((packed)) LocalInt; -} __attribute__((packed)) tMPTable_Ent; - -typedef struct sMPTable { - Uint32 Sig; - Uint16 BaseTableLength; - Uint8 SpecRev; - Uint8 Checksum; - - char OemID[8]; - char ProductID[12]; - - Uint32 OEMTablePtr; - Uint16 OEMTableSize; - Uint16 EntryCount; - - Uint32 LocalAPICMemMap; //!< Address used to access the local APIC - Uint16 ExtendedTableLen; - Uint8 ExtendedTableChecksum; - Uint8 Reserved; - - tMPTable_Ent Entries[]; -} tMPTable; - -typedef volatile struct { - Uint32 Addr; - Uint32 Resvd1[3]; - union { - Uint8 Byte; - Uint16 Word; - Uint32 DWord; - } Value; - Uint32 Resvd2[3]; -} tIOAPIC; - -typedef struct { - Uint32 Val; - Uint32 Padding[3]; -} volatile tReg; - -typedef volatile struct { - tReg _unused1[2]; - tReg ID; - tReg Version; - tReg _unused2[4]; - tReg TPR; // Task Priority Register - tReg APR; // Arbitration Priority Register (RO) - tReg PPR; // Processor Priority Register (RO) - tReg EOI; // EOI Register (Write Only) - tReg _unused3[1]; - tReg LogDest; // Logical Destination Register - tReg DestFmt; // Destination Format Register (0-27: RO, 28-31: RW) - tReg SIV; // Spurious Interrupt Vector Register (0-8: RW, 9-31: RO) - tReg ISR[8]; // In-Service Register - Total 256 Bits (RO) - tReg TMR[8]; // Trigger Mode Register - Total 256 Bits (RO) - tReg IRR[8]; // Interrupt Request Register - Total 256 Bits (RO) - tReg ErrorStatus; // Error Status Register (RO) - tReg _unused4[6]; - tReg LVTCMI; // LVT CMI Registers - // 0x300 - tReg ICR[2]; // Interrupt Command Register (RW) - // LVT Registers (Controls Local Vector Table) - // Structure: - // 0-7: Vector - IDT Vector for the interrupt - // 12: Delivery Status (0: Idle, 1: Send Pending) - // 16: Mask (0: Enabled, 1: Disabled) - // 0x320 - tReg LVTTimer; // LVT Timer Register (RW) - tReg LVTThermalSensor; // LVT Thermal Sensor Register (RW) - tReg LVTPerfMonCounters; // LVT Performance Monitor Counters Register (RW) - tReg LVTLInt0; // LVT Local Interrupt (LINT) #0 Register (RW); - tReg LVTLInt1; // LVT Local Interrupt (LINT) #1 Register (RW); - tReg LVTError; // LVT Error Register (RW); - // 0x380 - tReg InitialCount; // Initial Count Register (Used for the timer) (RW) - tReg CurrentCount; // Current Count Register (Used for the timer) (RW) - tReg _unused5[4]; - // 0x3E0 - tReg DivideConifg; // Divide Configuration Register (RW) - tReg _unused6[1]; -} volatile tAPIC; - -#endif diff --git a/Kernel/arch/x86/include/multiboot2.h b/Kernel/arch/x86/include/multiboot2.h deleted file mode 100644 index 735109a2..00000000 --- a/Kernel/arch/x86/include/multiboot2.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Acess 2 - * By John Hodge (thePowersGang) - * - * multiboot2.h - * - Multiboot 2 Header - */ -#ifndef _MULTIBOOT2_H_ -#define _MULTIBOOT2_H_ - -#define MULTIBOOT2_MAGIC 0x36D76289 - -typedef struct sMultiboot2Info -{ - Uint32 TotalSize; - Uint32 Reserved; // SBZ -} tMultiboot2Info; - -#endif diff --git a/Kernel/arch/x86/include/proc.h b/Kernel/arch/x86/include/proc.h deleted file mode 100644 index 9ecd98b5..00000000 --- a/Kernel/arch/x86/include/proc.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * AcessOS Microkernel Version - * proc.h - */ -#ifndef _PROC_H -#define _PROC_H - -// === TYPES == -typedef struct sTSS { - Uint32 Link; - Uint32 ESP0, SS0; - Uint32 ESP1, SS1; - Uint32 ESP2, SS2; - Uint32 CR3; - Uint32 EIP; - Uint32 EFLAGS; - Uint32 EAX, ECX, EDX, EBX; - Uint32 ESP, EBP, ESI, EDI; - Uint32 ES, CS, DS, SS, FS, GS; - Uint32 LDTR; - Uint16 Resvd, IOPB; // IO Permissions Bitmap -} __attribute__((packed)) tTSS; - -typedef struct { - #if USE_PAE - Uint PDPT[4]; - #else - Uint32 CR3; - #endif -} tMemoryState; - -// 512 bytes, 16 byte aligned -typedef struct sSSEState -{ - char data[512]; -} tSSEState; - -typedef struct { - Uint EIP, ESP; - Uint32 UserCS, UserEIP; - tSSEState *SSE; - int bSSEModified; -} tTaskState; - -#include - -#define USER_MAX KERNEL_BASE - -#endif diff --git a/Kernel/arch/x86/include/vm8086.h b/Kernel/arch/x86/include/vm8086.h deleted file mode 100644 index 7c8dd47d..00000000 --- a/Kernel/arch/x86/include/vm8086.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Acess2 VM8086 BIOS Interface - * - By John Hodge (thePowersGang) - * - * vm8086.h - * - Core Header - */ -#ifndef _VM80806_H_ -#define _VM80806_H_ - -// === TYPES === -/** - * \note Semi-opaque - Past \a .IP, the implementation may add any data - * it needs to the state. - */ -typedef struct sVM8086 -{ - Uint16 AX, CX, DX, BX; - Uint16 BP, SP, SI, DI; - - Uint16 SS, DS, ES; - - Uint16 CS, IP; - - struct sVM8086_InternalData *Internal; -} tVM8086; - -// === FUNCTIONS === -/** - * \brief Create an instance of the VM8086 Emulator - * \note Do not free this pointer with ::free, instead use ::VM8086_Free - * \return Pointer to a tVM8086 structure, this structure may be larger than - * tVM8086 due to internal data. - */ -extern tVM8086 *VM8086_Init(void); -/** - * \brief Free an allocated tVM8086 structure - * \param State Emulator state to free - */ -extern void VM8086_Free(tVM8086 *State); -/** - * \brief Allocate a piece of memory in the emulated address space and - * return a host and emulated pointer to it. - * \param State Emulator state - * \param Size Size of memory block - * \param Segment Pointer to location to store the allocated memory's segment - * \param Offset Pointet to location to store the allocated memory's offset - * \return Host pointer to the allocated memory - */ -extern void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset); -/** - * \brief Gets a pointer to a piece of emulated memory - * \todo Only 1 machine page is garenteed to be contiguous - * \param State Emulator State - * \param Segment Source Segment - * \param Offset Source Offset - * \return Host pointer to the emulated memory - */ -extern void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset); -/** - * \brief Calls a real-mode interrupt described by the current state of the IVT. - * \param State Emulator State - * \param Interrupt BIOS Interrupt to call - */ -extern void VM8086_Int(tVM8086 *State, Uint8 Interrupt); - -#endif diff --git a/Kernel/arch/x86/irq.c b/Kernel/arch/x86/irq.c deleted file mode 100644 index e2dcf0e3..00000000 --- a/Kernel/arch/x86/irq.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * AcessOS Microkernel Version - * irq.c - */ -#include - -// === CONSTANTS === -#define MAX_CALLBACKS_PER_IRQ 4 -#define TRACE_IRQS 0 - -// === TYPES === -typedef void (*tIRQ_Callback)(int, void *); - -// === PROTOTYPES === -void IRQ_Handler(tRegs *Regs); - -// === GLOBALS === -tIRQ_Callback gIRQ_Handlers[16][MAX_CALLBACKS_PER_IRQ]; -void *gaIRQ_DataPointers[16][MAX_CALLBACKS_PER_IRQ]; - -// === CODE === -/** - * \fn void IRQ_Handler(tRegs *Regs) - * \brief Handle an IRQ - */ -void IRQ_Handler(tRegs *Regs) -{ - int i, irq = Regs->int_num - 0xF0; - - //Log("IRQ_Handler: (Regs={int_num:%i})", Regs->int_num); - - for( i = 0; i < MAX_CALLBACKS_PER_IRQ; i++ ) - { - if( gIRQ_Handlers[irq][i] ) { - gIRQ_Handlers[irq][i](irq, gaIRQ_DataPointers[irq][i]); - #if TRACE_IRQS - if( irq != 8 ) - Log("IRQ %i: Call %p", Regs->int_num, gIRQ_Handlers[Regs->int_num][i]); - #endif - } - } - - //Log(" IRQ_Handler: Resetting"); - if(irq >= 8) - outb(0xA0, 0x20); // ACK IRQ (Secondary PIC) - outb(0x20, 0x20); // ACK IRQ - //Log("IRQ_Handler: RETURN"); -} - -/** - * \fn int IRQ_AddHandler( int Num, void (*Callback)(int) ) - */ -int IRQ_AddHandler( int Num, void (*Callback)(int, void*), void *Ptr ) -{ - int i; - for( i = 0; i < MAX_CALLBACKS_PER_IRQ; i++ ) - { - if( gIRQ_Handlers[Num][i] == NULL ) { - Log_Log("IRQ", "Added IRQ%i Cb#%i %p", Num, i, Callback); - gIRQ_Handlers[Num][i] = Callback; - gaIRQ_DataPointers[Num][i] = Ptr; - return 1; - } - } - - Log_Warning("IRQ", "No free callbacks on IRQ%i", Num); - return 0; -} diff --git a/Kernel/arch/x86/kpanic.c b/Kernel/arch/x86/kpanic.c deleted file mode 100644 index 9300c69e..00000000 --- a/Kernel/arch/x86/kpanic.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Acess 2 Kernel - * - By John Hodge (thePowersGang) - * - * kpanic.c - * - x86 Kernel Panic Handler - */ - -#include -#include - -// === CONSTANTS === -#define FB ((Uint16 *)(KERNEL_BASE|0xB8000)) -#define BGC 0x4F00 // White on Red -//#define BGC 0xC000 // Black on Bright Red -//#define BGC 0x1F00 // White on Blue (BSOD!) - -// === IMPORTS === -extern Uint32 GetEIP(void); -extern void Error_Backtrace(Uint32 eip, Uint32 ebp); -#if USE_MP -extern void MP_SendIPIVector(int CPU, Uint8 Vector); -extern int giNumCPUs; -extern int GetCPUNum(void); -#endif - -// === PROTOTYPES === -void KernelPanic_SetMode(void); -void KernelPanic_PutChar(char Ch); - -// === CONSTANTS === -const struct { - Uint16 IdxPort; - Uint16 DatPort; - Uint8 Index; - Uint8 Value; -} caRegValues[] = { - //{0x3C0, 0x3C0, 0x10, 0x0C}, // Mode Control (Blink Enabled) - {0x3C0, 0x3C0, 0x10, 0x04}, // Mode Control (Blink Disabled) - {0x3C0, 0x3C0, 0x11, 0x00}, // Overscan Register - {0x3C0, 0x3C0, 0x12, 0x0F}, // Color Plane Enable - {0x3C0, 0x3C0, 0x13, 0x08}, // Horizontal Panning - {0x3C0, 0x3C0, 0x14, 0x00}, // Color Select - {0 , 0x3C2, 0 , 0x67}, // Miscellaneous Output Register - {0x3C4, 0x3C5, 0x01, 0x00}, // Clock Mode Register - {0x3C4, 0x3C5, 0x03, 0x00}, // Character select - {0x3C4, 0x3C5, 0x04, 0x07}, // Memory Mode Register - {0x3CE, 0x3CF, 0x05, 0x10}, // Mode Register - {0x3CE, 0x3CF, 0x06, 0x0E}, // Miscellaneous Register - {0x3D4, 0x3D5, 0x00, 0x5F}, // Horizontal Total - {0x3D4, 0x3D5, 0x01, 0x4F}, // Horizontal Display Enable End - {0x3D4, 0x3D5, 0x02, 0x50}, // Horizontal Blank Start - {0x3D4, 0x3D5, 0x03, 0x82}, // Horizontal Blank End - {0x3D4, 0x3D5, 0x04, 0x55}, // Horizontal Retrace Start - {0x3D4, 0x3D5, 0x05, 0x81}, // Horizontal Retrace End - {0x3D4, 0x3D5, 0x06, 0xBF}, // Vertical Total - {0x3D4, 0x3D5, 0x07, 0x1F}, // Overflow Register - {0x3D4, 0x3D5, 0x08, 0x00}, // Preset row scan - {0x3D4, 0x3D5, 0x09, 0x4F}, // Maximum Scan Line - {0x3D4, 0x3D5, 0x10, 0x9C}, // Vertical Retrace Start - {0x3D4, 0x3D5, 0x11, 0x8E}, // Vertical Retrace End - {0x3D4, 0x3D5, 0x12, 0x8F}, // Vertical Display Enable End - {0x3D4, 0x3D5, 0x13, 0x28}, // Logical Width - {0x3D4, 0x3D5, 0x14, 0x1F}, // Underline Location - {0x3D4, 0x3D5, 0x15, 0x96}, // Vertical Blank Start - {0x3D4, 0x3D5, 0x16, 0xB9}, // Vertical Blank End - {0x3D4, 0x3D5, 0x17, 0xA3} // CRTC Mode Control -}; -#define NUM_REGVALUES (sizeof(caRegValues)/sizeof(caRegValues[0])) - -// === GLOBALS === - int giKP_Pos = 0; - -// === CODE === -/** - * \brief Sets the screen mode for a kernel panic - */ -void KernelPanic_SetMode(void) -{ - int i; - - // This function is called by Panic(), but MM_PageFault and the - // CPU exception handers also call it, so let's not clear the screen - // twice - if( giKP_Pos ) return ; - - // Restore VGA 0xB8000 text mode - #if 1 - for( i = 0; i < NUM_REGVALUES; i++ ) - { - // Reset Flip-Flop - if( caRegValues[i].IdxPort == 0x3C0 ) inb(0x3DA); - - if( caRegValues[i].IdxPort ) - outb(caRegValues[i].IdxPort, caRegValues[i].Index); - outb(caRegValues[i].DatPort, caRegValues[i].Value); - } - - inb(0x3DA); - outb(0x3C0, 0x20); - #endif - - #if USE_MP - // Send halt to all processors - for( i = 0; i < giNumCPUs; i ++ ) - { - if(i == GetCPUNum()) continue ; - FB[i] = BGC|('A'+i); - MP_SendIPIVector(i, 0xED); - } - #endif - - // Clear Screen - for( i = 0; i < 80*25; i++ ) - { - FB[i] = BGC; - } - - { - Uint32 eip = GetEIP(); - Uint32 ebp; - __asm__ __volatile__ ("mov %%ebp, %0" : "=r" (ebp)); - Error_Backtrace(eip, ebp); - } -} - -void KernelPanic_PutChar(char Ch) -{ - if( giKP_Pos > 80*25 ) return ; - switch(Ch) - { - case '\t': - do { - FB[giKP_Pos] &= 0xFF00; - FB[giKP_Pos++] |= ' '; - } while(giKP_Pos & 7); - break; - - case '\n': - giKP_Pos += 80; - case '\r': - giKP_Pos -= giKP_Pos % 80; - break; - - default: - if(' ' <= Ch && Ch < 0x7F) - { - FB[giKP_Pos] &= 0xFF00; - FB[giKP_Pos] |= Ch; - } - giKP_Pos ++; - break; - } -} diff --git a/Kernel/arch/x86/lib.c b/Kernel/arch/x86/lib.c deleted file mode 100644 index 175f9a55..00000000 --- a/Kernel/arch/x86/lib.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Acess2 - * - * arch/x86/lib.c - * - General arch-specific stuff - */ -#include -#include -#include -#include // GetCPUNum - -#define TRACE_LOCKS 0 - -#define DEBUG_TO_E9 1 -#define DEBUG_TO_SERIAL 1 -#define SERIAL_PORT 0x3F8 -#define GDB_SERIAL_PORT 0x2F8 - -// === IMPRORTS === -#if TRACE_LOCKS -extern struct sShortSpinlock glDebug_Lock; -extern tMutex glPhysAlloc; -#define TRACE_LOCK_COND (Lock != &glDebug_Lock && Lock != &glThreadListLock && Lock != &glPhysAlloc.Protector) -//#define TRACE_LOCK_COND (Lock != &glDebug_Lock && Lock != &glPhysAlloc.Protector) -#endif - -// === PROTOTYPES == -Uint64 __divmod64(Uint64 Num, Uint64 Den, Uint64 *Rem); -Uint64 __udivdi3(Uint64 Num, Uint64 Den); -Uint64 __umoddi3(Uint64 Num, Uint64 Den); - -// === GLOBALS === - int gbDebug_SerialSetup = 0; - int gbGDB_SerialSetup = 0; - -// === CODE === -/** - * \brief Determine if a short spinlock is locked - * \param Lock Lock pointer - */ -int IS_LOCKED(struct sShortSpinlock *Lock) -{ - return !!Lock->Lock; -} - -/** - * \brief Check if the current CPU has the lock - * \param Lock Lock pointer - */ -int CPU_HAS_LOCK(struct sShortSpinlock *Lock) -{ - return Lock->Lock == GetCPUNum() + 1; -} - -void __AtomicTestSetLoop(Uint *Ptr, Uint Value) -{ - __ASM__( - "1:\n\t" - "xor %%eax, %%eax;\n\t" - "lock cmpxchgl %0, (%1);\n\t" - "jnz 1b;\n\t" - :: "r"(Value), "r"(Ptr) - : "eax" // EAX clobbered - ); -} -/** - * \brief Acquire a Short Spinlock - * \param Lock Lock pointer - * - * This type of mutex should only be used for very short sections of code, - * or in places where a Mutex_* would be overkill, such as appending - * an element to linked list (usually two assignement lines in C) - * - * \note This type of lock halts interrupts, so ensure that no timing - * functions are called while it is held. As a matter of fact, spend as - * little time as possible with this lock held - * \note If \a STACKED_LOCKS is set, this type of spinlock can be nested - */ -void SHORTLOCK(struct sShortSpinlock *Lock) -{ - int IF; - int cpu = GetCPUNum() + 1; - - // Save interrupt state - __ASM__ ("pushf;\n\tpop %0" : "=r"(IF)); - IF &= 0x200; // AND out all but the interrupt flag - - #if TRACE_LOCKS - if( TRACE_LOCK_COND ) - { - //Log_Log("LOCK", "%p locked by %p", Lock, __builtin_return_address(0)); - Debug("%i %p obtaining %p (Called by %p)", cpu-1, __builtin_return_address(0), Lock, __builtin_return_address(1)); - } - #endif - - __ASM__("cli"); - - // Wait for another CPU to release - __AtomicTestSetLoop( (Uint*)&Lock->Lock, cpu ); - Lock->IF = IF; - - #if TRACE_LOCKS - if( TRACE_LOCK_COND ) - { - //Log_Log("LOCK", "%p locked by %p", Lock, __builtin_return_address(0)); - Debug("%i %p locked by %p\t%p", cpu-1, Lock, __builtin_return_address(0), __builtin_return_address(1)); -// Debug("got it"); - } - #endif -} -/** - * \brief Release a short lock - * \param Lock Lock pointer - */ -void SHORTREL(struct sShortSpinlock *Lock) -{ - #if TRACE_LOCKS - if( TRACE_LOCK_COND ) - { - //Log_Log("LOCK", "%p released by %p", Lock, __builtin_return_address(0)); - Debug("Lock %p released by %p\t%p", Lock, __builtin_return_address(0), __builtin_return_address(1)); - } - #endif - - // Lock->IF can change anytime once Lock->Lock is zeroed - if(Lock->IF) { - Lock->Lock = 0; - __ASM__ ("sti"); - } - else { - Lock->Lock = 0; - } -} - -// === DEBUG IO === -#if USE_GDB_STUB -int putDebugChar(char ch) -{ - if(!gbGDB_SerialSetup) { - outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts - outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) - outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud - outb(GDB_SERIAL_PORT + 1, 0x00); // (base is (hi byte) - outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit (8N1) - outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it - outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set - gbGDB_SerialSetup = 1; - } - while( (inb(GDB_SERIAL_PORT + 5) & 0x20) == 0 ); - outb(GDB_SERIAL_PORT, ch); - return 0; -} -int getDebugChar(void) -{ - if(!gbGDB_SerialSetup) { - outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts - outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) - outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud - outb(GDB_SERIAL_PORT + 1, 0x00); // (hi byte) - outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit - outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it - outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set - gbGDB_SerialSetup = 1; - } - while( (inb(GDB_SERIAL_PORT + 5) & 1) == 0) ; - return inb(GDB_SERIAL_PORT); -} -#endif /* USE_GDB_STUB */ - -void Debug_PutCharDebug(char ch) -{ - #if DEBUG_TO_E9 - __asm__ __volatile__ ( "outb %%al, $0xe9" :: "a"(((Uint8)ch)) ); - #endif - - #if DEBUG_TO_SERIAL - if(!gbDebug_SerialSetup) { - outb(SERIAL_PORT + 1, 0x00); // Disable all interrupts - outb(SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) - outb(SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud - outb(SERIAL_PORT + 1, 0x00); // (hi byte) - outb(SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit - outb(SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it - outb(SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set - gbDebug_SerialSetup = 1; - } - while( (inb(SERIAL_PORT + 5) & 0x20) == 0 ); - outb(SERIAL_PORT, ch); - #endif -} - -void Debug_PutStringDebug(const char *String) -{ - while(*String) - Debug_PutCharDebug(*String++); -} - -// === IO Commands === -void outb(Uint16 Port, Uint8 Data) -{ - __asm__ __volatile__ ("outb %%al, %%dx"::"d"(Port),"a"(Data)); -} -void outw(Uint16 Port, Uint16 Data) -{ - __asm__ __volatile__ ("outw %%ax, %%dx"::"d"(Port),"a"(Data)); -} -void outd(Uint16 Port, Uint32 Data) -{ - __asm__ __volatile__ ("outl %%eax, %%dx"::"d"(Port),"a"(Data)); -} -Uint8 inb(Uint16 Port) -{ - Uint8 ret; - __asm__ __volatile__ ("inb %%dx, %%al":"=a"(ret):"d"(Port)); - return ret; -} -Uint16 inw(Uint16 Port) -{ - Uint16 ret; - __asm__ __volatile__ ("inw %%dx, %%ax":"=a"(ret):"d"(Port)); - return ret; -} -Uint32 ind(Uint16 Port) -{ - Uint32 ret; - __asm__ __volatile__ ("inl %%dx, %%eax":"=a"(ret):"d"(Port)); - return ret; -} - -/** - * \fn void *memset(void *Dest, int Val, size_t Num) - * \brief Do a byte granuality set of Dest - */ -void *memset(void *Dest, int Val, size_t Num) -{ - Uint32 val = Val&0xFF; - val |= val << 8; - val |= val << 16; - __asm__ __volatile__ ( - "rep stosl;\n\t" - "mov %3, %%ecx;\n\t" - "rep stosb" - :: "D" (Dest), "a" (val), "c" (Num/4), "r" (Num&3)); - return Dest; -} -/** - * \brief Set double words - */ -void *memsetd(void *Dest, Uint32 Val, size_t Num) -{ - __asm__ __volatile__ ("rep stosl" :: "D" (Dest), "a" (Val), "c" (Num)); - return Dest; -} - -/** - * \fn int memcmp(const void *m1, const void *m2, size_t Num) - * \brief Compare two pieces of memory - */ -int memcmp(const void *m1, const void *m2, size_t Num) -{ - const Uint8 *d1 = m1; - const Uint8 *d2 = m2; - if( Num == 0 ) return 0; // No bytes are always identical - - while(Num--) - { - if(*d1 != *d2) - return *d1 - *d2; - d1 ++; - d2 ++; - } - return 0; -} - -/** - * \fn void *memcpy(void *Dest, const void *Src, size_t Num) - * \brief Copy \a Num bytes from \a Src to \a Dest - */ -void *memcpy(void *Dest, const void *Src, size_t Num) -{ - tVAddr dst = (tVAddr)Dest; - tVAddr src = (tVAddr)Src; - if( (dst & 3) != (src & 3) ) - { - __asm__ __volatile__ ("rep movsb" :: "D" (dst), "S" (src), "c" (Num)); -// Debug("\nmemcpy:Num=0x%x by %p (UA)", Num, __builtin_return_address(0)); - } - #if 1 - else if( Num > 128 && (dst & 15) == (src & 15) ) - { - char tmp[16+15]; // Note, this is a hack to save/restor xmm0 - int count = 16 - (dst & 15); -// Debug("\nmemcpy:Num=0x%x by %p (SSE)", Num, __builtin_return_address(0)); - if( count < 16 ) - { - Num -= count; - __asm__ __volatile__ ("rep movsb" : "=D"(dst),"=S"(src): "0"(dst), "1"(src), "c"(count)); - } - - count = Num / 16; - __asm__ __volatile__ ( - "movdqa 0(%5), %%xmm0;\n\t" - "1:\n\t" - "movdqa 0(%1), %%xmm0;\n\t" - "movdqa %%xmm0, 0(%0);\n\t" - "add $16,%0;\n\t" - "add $16,%1;\n\t" - "loop 1b;\n\t" - "movdqa %%xmm0, 0(%5);\n\t" - : "=r"(dst),"=r"(src) - : "0"(dst), "1"(src), "c"(count), "r" (((tVAddr)tmp+15)&~15) - ); - - count = Num & 15; - if(count) - __asm__ __volatile__ ("rep movsb" :: "D"(dst), "S"(src), "c"(count)); - } - #endif - else - { -// Debug("\nmemcpy:Num=0x%x by %p", Num, __builtin_return_address(0)); - __asm__ __volatile__ ( - "rep movsl;\n\t" - "mov %3, %%ecx;\n\t" - "rep movsb" - :: "D" (Dest), "S" (Src), "c" (Num/4), "r" (Num&3)); - } - return Dest; -} - -/** - * \fn void *memcpyd(void *Dest, const void *Src, size_t Num) - * \brief Copy \a Num DWORDs from \a Src to \a Dest - */ -void *memcpyd(void *Dest, const void *Src, size_t Num) -{ - __asm__ __volatile__ ("rep movsl" :: "D" (Dest), "S" (Src), "c" (Num)); - return Dest; -} - -#include "../helpers.h" - -DEF_DIVMOD(64); - -Uint64 DivMod64U(Uint64 Num, Uint64 Div, Uint64 *Rem) -{ - if( Div < 0x100000000ULL && Num < 0xFFFFFFFF * Div ) { - Uint32 rem, ret_32; - __asm__ __volatile__( - "div %4" - : "=a" (ret_32), "=d" (rem) - : "a" ( (Uint32)(Num & 0xFFFFFFFF) ), "d" ((Uint32)(Num >> 32)), "r" (Div) - ); - if(Rem) *Rem = rem; - return ret_32; - } - - return __divmod64(Num, Div, Rem); -} - -/** - * \fn Uint64 __udivdi3(Uint64 Num, Uint64 Den) - * \brief Divide two 64-bit integers - */ -Uint64 __udivdi3(Uint64 Num, Uint64 Den) -{ - if(Den == 0) { - __asm__ __volatile__ ("int $0x0"); - return -1; - } - // Common speedups - if(Num <= 0xFFFFFFFF && Den <= 0xFFFFFFFF) - return (Uint32)Num / (Uint32)Den; - if(Den == 1) return Num; - if(Den == 2) return Num >> 1; // Speed Hacks - if(Den == 4) return Num >> 2; // Speed Hacks - if(Den == 8) return Num >> 3; // Speed Hacks - if(Den == 16) return Num >> 4; // Speed Hacks - if(Den == 32) return Num >> 5; // Speed Hacks - if(Den == 1024) return Num >> 10; // Speed Hacks - if(Den == 2048) return Num >> 11; // Speed Hacks - if(Den == 4096) return Num >> 12; - if(Num < Den) return 0; - if(Num < Den*2) return 1; - if(Num == Den*2) return 2; - - return __divmod64(Num, Den, NULL); -} - -/** - * \fn Uint64 __umoddi3(Uint64 Num, Uint64 Den) - * \brief Get the modulus of two 64-bit integers - */ -Uint64 __umoddi3(Uint64 Num, Uint64 Den) -{ - Uint64 ret = 0; - if(Den == 0) { - __asm__ __volatile__ ("int $0x0"); // Call Div by Zero Error - return -1; - } - if(Den == 1) return 0; // Speed Hacks - if(Den == 2) return Num & 1; // Speed Hacks - if(Den == 4) return Num & 3; // Speed Hacks - if(Den == 8) return Num & 7; // Speed Hacks - if(Den == 16) return Num & 15; // Speed Hacks - if(Den == 32) return Num & 31; // Speed Hacks - if(Den == 1024) return Num & 1023; // Speed Hacks - if(Den == 2048) return Num & 2047; // Speed Hacks - if(Den == 4096) return Num & 4095; // Speed Hacks - - if(Num >> 32 == 0 && Den >> 32 == 0) - return (Uint32)Num % (Uint32)Den; - - __divmod64(Num, Den, &ret); - return ret; -} - - -// --- EXPORTS --- -EXPORT(memcpy); EXPORT(memset); -EXPORT(memcmp); -//EXPORT(memcpyw); EXPORT(memsetw); -EXPORT(memcpyd); EXPORT(memsetd); -EXPORT(inb); EXPORT(inw); EXPORT(ind); -EXPORT(outb); EXPORT(outw); EXPORT(outd); -EXPORT(__udivdi3); EXPORT(__umoddi3); - -EXPORT(SHORTLOCK); -EXPORT(SHORTREL); -EXPORT(IS_LOCKED); diff --git a/Kernel/arch/x86/link.ld b/Kernel/arch/x86/link.ld deleted file mode 100644 index a09b29af..00000000 --- a/Kernel/arch/x86/link.ld +++ /dev/null @@ -1,71 +0,0 @@ -/* - * AcessMicro Kernel - * Linker Script - */ - -lowStart = start - 0xC0000000; -ENTRY(lowStart) -OUTPUT_FORMAT(elf32-i386) - -SECTIONS { - . = 0x100000; - __load_addr = .; - .multiboot : AT(ADDR(.multiboot)) { - *(.multiboot) - } - - . += 0xC0000000; - - .text ALIGN(0x1000): AT(ADDR(.text) - 0xC0000000) { - *(.text) - } - - .usertext ALIGN(0x1000): AT(ADDR(.usertext) - 0xC0000000) { - _UsertextBase = .; - *(.usertext) - } - _UsertextEnd = .; - - .rodata ALIGN(0x1000): AT(ADDR(.rodata) - 0xC0000000) { - *(.initpd) - *(.rodata) - *(.rdata) - gKernelModules = .; - *(KMODULES) - gKernelModulesEnd = .; - . = ALIGN(4); - gKernelSymbols = .; - *(KEXPORT) - gKernelSymbolsEnd = .; - - - } - /* - .debug_abbrev : { *(.debug_abbrev) } - .debug_info : { *(.debug_info) } - .debug_line : { *(.debug_line) } - .debug_loc : { *(.debug_loc) } - .debug_pubnames : { *(.debug_pubnames) } - .debug_aranges : { *(.debug_aranges) } - .debug_ranges : { *(.debug_ranges) } - .debug_str : { *(.debug_str) } - .debug_frame : { *(.debug_frame) } - */ - - .padata ALIGN (0x1000) : AT(ADDR(.padata) - 0xC0000000) { - *(.padata) - } - - .data ALIGN (0x1000) : AT(ADDR(.data) - 0xC0000000) { - *(.data) - } - - __bss_start = .; - .bss : AT(ADDR(.bss) - 0xC0000000) { - _sbss = .; - *(COMMON) - *(.bss) - _ebss = .; - } - gKernelEnd = (. + 0xFFF)&0xFFFFF000; -} diff --git a/Kernel/arch/x86/main.c b/Kernel/arch/x86/main.c deleted file mode 100644 index 2106b6af..00000000 --- a/Kernel/arch/x86/main.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Acess 2 - * x86 Kernel Main - * arch/x86/main.c - */ -#include -#include -#include -#include -#include -#include - -#define VGA_ERRORS 0 - -#define MAX_ARGSTR_POS (0x400000-0x2000) - -// === IMPORTS === -extern void Heap_Install(void); -extern void Desctab_Install(void); -extern void MM_PreinitVirtual(void); -extern void MM_Install(tMBoot_Info *MBoot); -extern void MM_InstallVirtual(void); -extern void Threads_Init(void); -extern int Time_Setup(void); -// --- Core --- -extern void System_Init(char *Commandline); - -// === PROTOTYPES === - int kmain(Uint MbMagic, void *MbInfoPtr); - -// === GLOBALS === -char *gsBootCmdLine = NULL; -struct { - Uint32 PBase; - void *Base; - Uint Size; - char *ArgString; -} *gaArch_BootModules; - int giArch_NumBootModules = 0; - -// === CODE === -int kmain(Uint MbMagic, void *MbInfoPtr) -{ - int i; - tMBoot_Module *mods; - tMBoot_Info *mbInfo; - - LogF("Acess2 x86-"PLATFORM" v"EXPAND_STR(KERNEL_VERSION)"\n"); - LogF(" Build %i, Git Hash %s\n", BUILD_NUM, gsGitHash); - - Log("MbMagic = %08x, MbInfoPtr = %p", MbMagic, MbInfoPtr); - - // Set up non-boot info dependent stuff - Desctab_Install(); // Set up GDT and IDT - MM_PreinitVirtual(); // Initialise virtual mappings - - switch(MbMagic) - { - // Multiboot 1 - case MULTIBOOT_MAGIC: - // Adjust Multiboot structure address - mbInfo = (void*)( (Uint)MbInfoPtr + KERNEL_BASE ); - gsBootCmdLine = (char*)(mbInfo->CommandLine + KERNEL_BASE); - - MM_Install( mbInfo ); // Set up physical memory manager - break; - - // Multiboot 2 - case MULTIBOOT2_MAGIC: - Panic("Multiboot 2 not yet supported"); - //MM_InstallMBoot2( MbInfo ); // Set up physical memory manager - return 0; - break; - - default: - Panic("Multiboot magic invalid %08x, expected %08x or %08x\n", - MbMagic, MULTIBOOT_MAGIC, MULTIBOOT2_MAGIC); - return 0; - } - - MM_InstallVirtual(); // Clean up virtual address space - Heap_Install(); // Create initial heap - - // Start Multitasking - Threads_Init(); - - // Start Timers - Time_Setup(); - - Log_Log("Arch", "Starting VFS..."); - // Load Virtual Filesystem - VFS_Init(); - - // Load initial modules - mods = (void*)( mbInfo->Modules + KERNEL_BASE ); - giArch_NumBootModules = mbInfo->ModuleCount; - gaArch_BootModules = malloc( giArch_NumBootModules * sizeof(*gaArch_BootModules) ); - for( i = 0; i < mbInfo->ModuleCount; i ++ ) - { - int ofs; - - Log_Log("Arch", "Multiboot Module at 0x%08x, 0x%08x bytes (String at 0x%08x)", - mods[i].Start, mods[i].End-mods[i].Start, mods[i].String); - - gaArch_BootModules[i].PBase = mods[i].Start; - gaArch_BootModules[i].Size = mods[i].End - mods[i].Start; - - // Always HW map the module data - ofs = mods[i].Start&0xFFF; - gaArch_BootModules[i].Base = (void*)( MM_MapHWPages(mods[i].Start, - (gaArch_BootModules[i].Size+ofs+0xFFF) / 0x1000 - ) + ofs ); - - // Only map the string if needed - if( (tVAddr)mods[i].String > MAX_ARGSTR_POS ) - { - // Assumes the string is < 4096 bytes long) - gaArch_BootModules[i].ArgString = (void*)( - MM_MapHWPages(mods[i].String, 2) + (mods[i].String&0xFFF) - ); - } - else - gaArch_BootModules[i].ArgString = (char *)mods[i].String + KERNEL_BASE; - } - - // Pass on to Independent Loader - Log_Log("Arch", "Starting system"); - System_Init(gsBootCmdLine); - - // Sleep forever (sleeping beauty) - for(;;) - Threads_Sleep(); - return 0; -} - -void Arch_LoadBootModules(void) -{ - int i, j, numPages; - for( i = 0; i < giArch_NumBootModules; i ++ ) - { - Log_Log("Arch", "Loading '%s'", gaArch_BootModules[i].ArgString); - - if( !Module_LoadMem( gaArch_BootModules[i].Base, gaArch_BootModules[i].Size, gaArch_BootModules[i].ArgString ) ) - { - Log_Warning("Arch", "Unable to load module"); - } - - // Unmap and free - numPages = (gaArch_BootModules[i].Size + ((Uint)gaArch_BootModules[i].Base&0xFFF) + 0xFFF) >> 12; - MM_UnmapHWPages( (tVAddr)gaArch_BootModules[i].Base, numPages ); - - for( j = 0; j < numPages; j++ ) - MM_DerefPhys( gaArch_BootModules[i].PBase + (j << 12) ); - - if( (tVAddr) gaArch_BootModules[i].ArgString > MAX_ARGSTR_POS ) - MM_UnmapHWPages( (tVAddr)gaArch_BootModules[i].ArgString, 2 ); - } - Log_Log("Arch", "Boot modules loaded"); - free( gaArch_BootModules ); -} diff --git a/Kernel/arch/x86/mm_phys.c b/Kernel/arch/x86/mm_phys.c deleted file mode 100644 index aa1b2603..00000000 --- a/Kernel/arch/x86/mm_phys.c +++ /dev/null @@ -1,552 +0,0 @@ -/* - * Acess2 - * - Physical memory manager - */ -#define DEBUG 0 -#include -#include -#include - -//#define USE_STACK 1 -#define TRACE_ALLOCS 0 // Print trace messages on AllocPhys/DerefPhys - - -// === IMPORTS === -extern char gKernelEnd[]; -extern void Proc_PrintBacktrace(void); - -// === PROTOTYPES === -void MM_Install(tMBoot_Info *MBoot); -//tPAddr MM_AllocPhys(void); -//tPAddr MM_AllocPhysRange(int Pages, int MaxBits); -//void MM_RefPhys(tPAddr PAddr); -//void MM_DerefPhys(tPAddr PAddr); -// int MM_GetRefCount(tPAddr PAddr); - -// === GLOBALS === -tMutex glPhysAlloc; -Uint64 giPhysAlloc = 0; // Number of allocated pages -Uint64 giPageCount = 0; // Total number of pages -Uint64 giLastPossibleFree = 0; // Last possible free page (before all pages are used) - -Uint32 gaSuperBitmap[1024]; // Blocks of 1024 Pages -Uint32 gaPageBitmap[1024*1024/32]; // Individual pages - int *gaPageReferences; -void **gaPageNodes = (void*)MM_PAGENODE_BASE; -#define REFENT_PER_PAGE (0x1000/sizeof(gaPageReferences[0])) - -// === CODE === -void MM_Install(tMBoot_Info *MBoot) -{ - Uint kernelPages, num; - Uint i; - Uint64 maxAddr = 0; - tMBoot_Module *mods; - tMBoot_MMapEnt *ent; - - // --- Find largest address - MBoot->MMapAddr |= KERNEL_BASE; - ent = (void *)( MBoot->MMapAddr ); - while( (Uint)ent < MBoot->MMapAddr + MBoot->MMapLength ) - { - // Adjust for size - ent->Size += 4; - - // If entry is RAM and is above `maxAddr`, change `maxAddr` - if(ent->Type == 1 && ent->Base + ent->Length > maxAddr) - maxAddr = ent->Base + ent->Length; - // Go to next entry - ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size ); - } - - if(maxAddr == 0) { - giPageCount = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value - } - else { - giPageCount = maxAddr >> 12; - } - giLastPossibleFree = giPageCount - 1; - - memsetd(gaPageBitmap, 0xFFFFFFFF, giPageCount/32); - - // Set up allocateable space - ent = (void *)( MBoot->MMapAddr ); - while( (Uint)ent < MBoot->MMapAddr + MBoot->MMapLength ) - { - memsetd( &gaPageBitmap[ent->Base/(4096*32)], 0, ent->Length/(4096*32) ); - ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size ); - } - - // Get used page count (Kernel) - kernelPages = (Uint)&gKernelEnd - KERNEL_BASE - 0x100000; - kernelPages += 0xFFF; // Page Align - kernelPages >>= 12; - - // Fill page bitmap - num = kernelPages/32; - memsetd( &gaPageBitmap[0x100000/(4096*32)], -1, num ); - gaPageBitmap[ 0x100000/(4096*32) + num ] = (1 << (kernelPages & 31)) - 1; - - // Fill Superpage bitmap - num = kernelPages/(32*32); - memsetd( &gaSuperBitmap[0x100000/(4096*32*32)], -1, num ); - gaSuperBitmap[ 0x100000/(4096*32*32) + num ] = (1 << ((kernelPages / 32) & 31)) - 1; - - // Mark Multiboot's pages as taken - // - Structure - MM_RefPhys( (Uint)MBoot - KERNEL_BASE ); - // - Module List - for(i = (MBoot->ModuleCount*sizeof(tMBoot_Module)+0xFFF)>12; i--; ) - MM_RefPhys( MBoot->Modules + (i << 12) ); - // - Modules - mods = (void*)(MBoot->Modules + KERNEL_BASE); - for(i = 0; i < MBoot->ModuleCount; i++) - { - num = (mods[i].End - mods[i].Start + 0xFFF) >> 12; - while(num--) - MM_RefPhys( (mods[i].Start & ~0xFFF) + (num<<12) ); - } - - gaPageReferences = (void*)MM_REFCOUNT_BASE; - - Log_Log("PMem", "Physical memory set up"); -} - -/** - * \fn tPAddr MM_AllocPhys(void) - * \brief Allocates a physical page from the general pool - */ -tPAddr MM_AllocPhys(void) -{ - // int a, b, c; - int indx = -1; - tPAddr ret; - - ENTER(""); - - Mutex_Acquire( &glPhysAlloc ); - - // Classful scan - #if 1 - { - const int addrClasses[] = {0,16,20,24,32,64}; - const int numAddrClasses = sizeof(addrClasses)/sizeof(addrClasses[0]); - int i; - int first, last; - for( i = numAddrClasses; i -- > 1; ) - { - first = 1UL << (addrClasses[i-1] - 12); - last = (1UL << (addrClasses[i] - 12)) - 1; - // Range is above the last free page - if( first > giLastPossibleFree ) - continue; - // Last possible free page is in the range - if( last > giLastPossibleFree ) - last = giLastPossibleFree; - - // Scan the range - for( indx = first; indx < last; ) - { - if( gaSuperBitmap[indx>>10] == -1 ) { - indx += 1024; - continue; - } - - if( gaPageBitmap[indx>>5] == -1 ) { - indx += 32; - continue; - } - - if( gaPageBitmap[indx>>5] & (1 << (indx&31)) ) { - indx ++; - continue; - } - break; - } - if( indx < last ) break; - - giLastPossibleFree = first; // Well, we couldn't find any in this range - } - // Out of memory? - if( i <= 1 ) indx = -1; - } - #elif 0 - // Find free page - // Scan downwards - LOG("giLastPossibleFree = %i", giLastPossibleFree); - for( indx = giLastPossibleFree; indx >= 0; ) - { - if( gaSuperBitmap[indx>>10] == -1 ) { - indx -= 1024; - continue; - } - - if( gaPageBitmap[indx>>5] == -1 ) { - indx -= 32; - continue; - } - - if( gaPageBitmap[indx>>5] & (1 << (indx&31)) ) { - indx --; - continue; - } - break; - } - if( indx >= 0 ) - giLastPossibleFree = indx; - LOG("indx = %i", indx); - #else - c = giLastPossibleFree % 32; - b = (giLastPossibleFree / 32) % 32; - a = giLastPossibleFree / 1024; - - LOG("a=%i,b=%i,c=%i", a, b, c); - for( ; gaSuperBitmap[a] == -1 && a >= 0; a-- ); - if(a < 0) { - Mutex_Release( &glPhysAlloc ); - Warning("MM_AllocPhys - OUT OF MEMORY (Called by %p) - %lli/%lli used", - __builtin_return_address(0), giPhysAlloc, giPageCount); - LEAVE('i', 0); - return 0; - } - for( ; gaSuperBitmap[a] & (1<= 0 ) - giLastPossibleFree = indx; - #endif - - if( indx < 0 ) { - Mutex_Release( &glPhysAlloc ); - Warning("MM_AllocPhys - OUT OF MEMORY (Called by %p) - %lli/%lli used (indx = %x)", - __builtin_return_address(0), giPhysAlloc, giPageCount, indx); - Log_Debug("PMem", "giLastPossibleFree = %lli", giLastPossibleFree); - LEAVE('i', 0); - return 0; - } - - if( indx > 0xFFFFF ) { - Panic("The fuck? Too many pages! (indx = 0x%x)", indx); - } - - if( indx >= giPageCount ) { - Mutex_Release( &glPhysAlloc ); - Log_Error("PMem", "MM_AllocPhys - indx(%i) > giPageCount(%i)", indx, giPageCount); - LEAVE('i', 0); - return 0; - } - - // Mark page used - if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[indx] ) ) - gaPageReferences[indx] = 1; - gaPageBitmap[ indx>>5 ] |= 1 << (indx&31); - - giPhysAlloc ++; - - // Get address - ret = indx << 12; - - // Mark used block - if(gaPageBitmap[ indx>>5 ] == -1) { - gaSuperBitmap[indx>>10] |= 1 << ((indx>>5)&31); - } - - // Release Spinlock - Mutex_Release( &glPhysAlloc ); - - LEAVE('X', ret); - #if TRACE_ALLOCS - if( now() > 4000 ) { - Log_Debug("PMem", "MM_AllocPhys: RETURN %P (%i free)", ret, giPageCount-giPhysAlloc); - Proc_PrintBacktrace(); - } - #endif - return ret; -} - -/** - * \fn tPAddr MM_AllocPhysRange(int Pages, int MaxBits) - * \brief Allocate a range of physical pages - * \param Pages Number of pages to allocate - * \param MaxBits Maximum number of address bits to use - */ -tPAddr MM_AllocPhysRange(int Pages, int MaxBits) -{ - int a, b; - int i, idx, sidx; - tPAddr ret; - - ENTER("iPages iMaxBits", Pages, MaxBits); - - // Sanity Checks - if(MaxBits < 0) { - LEAVE('i', 0); - return 0; - } - if(MaxBits > PHYS_BITS) MaxBits = PHYS_BITS; - - // Lock - Mutex_Acquire( &glPhysAlloc ); - - // Set up search state - if( giLastPossibleFree > ((tPAddr)1 << (MaxBits-12)) ) { - sidx = (tPAddr)1 << (MaxBits-12); - } - else { - sidx = giLastPossibleFree; - } - idx = sidx / 32; - sidx %= 32; - b = idx % 32; - a = idx / 32; - - #if 0 - LOG("a=%i, b=%i, idx=%i, sidx=%i", a, b, idx, sidx); - - // Find free page - for( ; gaSuperBitmap[a] == -1 && a --; ) b = 31; - if(a < 0) { - Mutex_Release( &glPhysAlloc ); - Warning("MM_AllocPhysRange - OUT OF MEMORY (Called by %p)", __builtin_return_address(0)); - LEAVE('i', 0); - return 0; - } - LOG("a = %i", a); - for( ; gaSuperBitmap[a] & (1 << b); b-- ) sidx = 31; - LOG("b = %i", b); - idx = a * 32 + b; - for( ; gaPageBitmap[idx] & (1 << sidx); sidx-- ) - LOG("gaPageBitmap[%i] = 0x%08x", idx, gaPageBitmap[idx]); - - LOG("idx = %i, sidx = %i", idx, sidx); - #else - - #endif - - // Check if the gap is large enough - while( idx >= 0 ) - { - // Find a free page - for( ; ; ) - { - // Bulk Skip - if( gaPageBitmap[idx] == -1 ) { - idx --; - sidx = 31; - continue; - } - - if( gaPageBitmap[idx] & (1 << sidx) ) { - sidx --; - if(sidx < 0) { sidx = 31; idx --; } - if(idx < 0) break; - continue; - } - break; - } - if( idx < 0 ) break; - - // Check if it is a free range - for( i = 0; i < Pages; i++ ) - { - // Used page? break - if( gaPageBitmap[idx] & (1 << sidx) ) - break; - - sidx --; - if(sidx < 0) { sidx = 31; idx --; } - if(idx < 0) break; - } - - if( i == Pages ) - break; - } - - // Check if an address was found - if( idx < 0 ) { - Mutex_Release( &glPhysAlloc ); - Warning("MM_AllocPhysRange - OUT OF MEMORY (Called by %p)", __builtin_return_address(0)); - LEAVE('i', 0); - return 0; - } - - // Mark pages used - for( i = 0; i < Pages; i++ ) - { - if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[idx*32+sidx] ) ) - gaPageReferences[idx*32+sidx] = 1; - gaPageBitmap[ idx ] |= 1 << sidx; - sidx ++; - giPhysAlloc ++; - if(sidx == 32) { sidx = 0; idx ++; } - } - - // Get address - ret = (idx << 17) | (sidx << 12); - - // Mark used block - if(gaPageBitmap[ idx ] == -1) gaSuperBitmap[idx/32] |= 1 << (idx%32); - - // Release Spinlock - Mutex_Release( &glPhysAlloc ); - - LEAVE('X', ret); - #if TRACE_ALLOCS - Log_Debug("PMem", "MM_AllocPhysRange: RETURN 0x%llx-0x%llx (%i free)", - ret, ret + (1<>= 12; - - // We don't care about non-ram pages - if(PAddr >= giPageCount) return; - - // Lock Structures - Mutex_Acquire( &glPhysAlloc ); - - // Reference the page - if( gaPageReferences ) - { - if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) == 0 ) - { - int i, base; - tVAddr addr = ((tVAddr)&gaPageReferences[PAddr]) & ~0xFFF; -// Log_Debug("PMem", "MM_RefPhys: Allocating info for %X", PAddr); - Mutex_Release( &glPhysAlloc ); - if( MM_Allocate( addr ) == 0 ) { - Log_KernelPanic("PMem", "MM_RefPhys: Out of physical memory allocating info for %X", PAddr*PAGE_SIZE); - } - Mutex_Acquire( &glPhysAlloc ); - - base = PAddr & ~(1024-1); - for( i = 0; i < 1024; i ++ ) { - gaPageReferences[base + i] = (gaPageBitmap[(base+i)/32] & (1 << (base+i)%32)) ? 1 : 0; - } - } - gaPageReferences[ PAddr ] ++; - } - - // Mark as used - gaPageBitmap[ PAddr / 32 ] |= 1 << (PAddr&31); - - // Mark used block - if(gaPageBitmap[ PAddr / 32 ] == -1) - gaSuperBitmap[PAddr/1024] |= 1 << ((PAddr/32)&31); - - // Release Spinlock - Mutex_Release( &glPhysAlloc ); -} - -/** - * \fn void MM_DerefPhys(tPAddr PAddr) - * \brief Dereferences a physical page - */ -void MM_DerefPhys(tPAddr PAddr) -{ - // Get page number - PAddr >>= 12; - - // We don't care about non-ram pages - if(PAddr >= giPageCount) return; - - // Check if it is freed - if( !(gaPageBitmap[PAddr / 32] & (1 << PAddr%32)) ) { - Log_Warning("MMVirt", "MM_DerefPhys - Non-referenced memory dereferenced"); - return; - } - - // Lock Structures - Mutex_Acquire( &glPhysAlloc ); - - if( giLastPossibleFree < PAddr ) - giLastPossibleFree = PAddr; - - // Dereference - if( !MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) || (-- gaPageReferences[PAddr]) == 0 ) - { - #if TRACE_ALLOCS - Log_Debug("PMem", "MM_DerefPhys: Free'd %P (%i free)", PAddr<<12, giPageCount-giPhysAlloc); - Proc_PrintBacktrace(); - #endif - //LOG("Freed 0x%x by %p\n", PAddr<<12, __builtin_return_address(0)); - giPhysAlloc --; - gaPageBitmap[ PAddr / 32 ] &= ~(1 << (PAddr&31)); - if(gaPageBitmap[ PAddr / 32 ] == 0) - gaSuperBitmap[ PAddr >> 10 ] &= ~(1 << ((PAddr >> 5)&31)); - - if( MM_GetPhysAddr( (tVAddr) &gaPageNodes[PAddr] ) ) - { - gaPageNodes[PAddr] = NULL; - // TODO: Free Node Page when fully unused - } - } - - // Release spinlock - Mutex_Release( &glPhysAlloc ); -} - -/** - * \fn int MM_GetRefCount(tPAddr Addr) - */ -int MM_GetRefCount(tPAddr PAddr) -{ - // Get page number - PAddr >>= 12; - - // We don't care about non-ram pages - if(PAddr >= giPageCount) return -1; - - if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) == 0 ) - return (gaPageBitmap[PAddr / 32] & (1 << PAddr%32)) ? 1 : 0; - - // Check if it is freed - return gaPageReferences[ PAddr ]; -} - -int MM_SetPageNode(tPAddr PAddr, void *Node) -{ - tVAddr block_addr; - - if( MM_GetRefCount(PAddr) == 0 ) return 1; - - PAddr /= PAGE_SIZE; - - block_addr = (tVAddr) &gaPageNodes[PAddr]; - block_addr &= ~(PAGE_SIZE-1); - - if( !MM_GetPhysAddr( block_addr ) ) - { - if( !MM_Allocate( block_addr ) ) { - Log_Warning("PMem", "Unable to allocate Node page"); - return -1; - } - memset( (void*)block_addr, 0, PAGE_SIZE ); - } - - gaPageNodes[PAddr] = Node; -// Log("gaPageNodes[0x%x] = %p", PAddr, Node); - return 0; -} - -int MM_GetPageNode(tPAddr PAddr, void **Node) -{ - if( MM_GetRefCount(PAddr) == 0 ) return 1; - - PAddr /= PAGE_SIZE; - if( !MM_GetPhysAddr( (tVAddr) &gaPageNodes[PAddr] ) ) { - *Node = NULL; - return 0; - } - *Node = gaPageNodes[PAddr]; - return 0; -} - diff --git a/Kernel/arch/x86/mm_virt.c b/Kernel/arch/x86/mm_virt.c deleted file mode 100644 index bd7b1dff..00000000 --- a/Kernel/arch/x86/mm_virt.c +++ /dev/null @@ -1,1151 +0,0 @@ -/* - * AcessOS Microkernel Version - * mm_virt.c - * - * Memory Map - * 0xE0 - Kernel Base - * 0xF0 - Kernel Stacks - * 0xFD - Fractals - * 0xFE - Unused - * 0xFF - System Calls / Kernel's User Code - */ -#define DEBUG 0 -#define SANITY 1 -#include -#include -#include -#include -#include -#include - -#define TAB 22 - -#define WORKER_STACKS 0x00100000 // Thread0 Only! -#define WORKER_STACK_SIZE MM_KERNEL_STACK_SIZE -#define WORKER_STACKS_END 0xB0000000 -#define NUM_WORKER_STACKS ((WORKER_STACKS_END-WORKER_STACKS)/WORKER_STACK_SIZE) - -#define PAE_PAGE_TABLE_ADDR 0xFC000000 // 16 MiB -#define PAE_PAGE_DIR_ADDR 0xFCFC0000 // 16 KiB -#define PAE_PAGE_PDPT_ADDR 0xFCFC3F00 // 32 bytes -#define PAE_TMP_PDPT_ADDR 0xFCFC3F20 // 32 bytes -#define PAE_TMP_DIR_ADDR 0xFCFE0000 // 16 KiB -#define PAE_TMP_TABLE_ADDR 0xFD000000 // 16 MiB - -#define PAGE_TABLE_ADDR 0xFC000000 -#define PAGE_DIR_ADDR 0xFC3F0000 -#define PAGE_CR3_ADDR 0xFC3F0FC0 -#define TMP_CR3_ADDR 0xFC3F0FC4 // Part of core instead of temp -#define TMP_DIR_ADDR 0xFC3F1000 // Same -#define TMP_TABLE_ADDR 0xFC400000 - -#define HW_MAP_ADDR 0xFE000000 -#define HW_MAP_MAX 0xFFEF0000 -#define NUM_HW_PAGES ((HW_MAP_MAX-HW_MAP_ADDR)/0x1000) -#define TEMP_MAP_ADDR 0xFFEF0000 // Allows 16 "temp" pages -#define NUM_TEMP_PAGES 16 -#define LAST_BLOCK_ADDR 0xFFFF0000 // Free space for kernel provided user code/ *(-1) protection - -#define PF_PRESENT 0x1 -#define PF_WRITE 0x2 -#define PF_USER 0x4 -#define PF_GLOBAL 0x80 -#define PF_COW 0x200 -#define PF_NOPAGE 0x400 - -#define INVLPG(addr) __asm__ __volatile__ ("invlpg (%0)"::"r"(addr)) - -#define GET_TEMP_MAPPING(cr3) do { \ - __ASM__("cli"); \ - __AtomicTestSetLoop( (Uint *)gpTmpCR3, cr3 | 3 ); \ -} while(0) -#define REL_TEMP_MAPPING() do { \ - *gpTmpCR3 = 0; \ - __ASM__("sti"); \ -} while(0) - -typedef Uint32 tTabEnt; - -// === IMPORTS === -extern char _UsertextEnd[], _UsertextBase[]; -extern Uint32 gaInitPageDir[1024]; -extern Uint32 gaInitPageTable[1024]; -extern void Threads_SegFault(tVAddr Addr); -extern void Error_Backtrace(Uint eip, Uint ebp); - -// === PROTOTYPES === -void MM_PreinitVirtual(void); -void MM_InstallVirtual(void); -void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs); -//void MM_DumpTables(tVAddr Start, tVAddr End); -//void MM_ClearUser(void); -tPAddr MM_DuplicatePage(tVAddr VAddr); - -// === GLOBALS === -#define gaPageTable ((tTabEnt*)PAGE_TABLE_ADDR) -#define gaPageDir ((tTabEnt*)PAGE_DIR_ADDR) -#define gaTmpTable ((tTabEnt*)TMP_TABLE_ADDR) -#define gaTmpDir ((tTabEnt*)TMP_DIR_ADDR) -#define gpPageCR3 ((tTabEnt*)PAGE_CR3_ADDR) -#define gpTmpCR3 ((tTabEnt*)TMP_CR3_ADDR) - -#define gaPAE_PageTable ((tTabEnt*)PAE_PAGE_TABLE_ADDR) -#define gaPAE_PageDir ((tTabEnt*)PAE_PAGE_DIR_ADDR) -#define gaPAE_MainPDPT ((tTabEnt*)PAE_PAGE_PDPT_ADDR) -#define gaPAE_TmpTable ((tTabEnt*)PAE_TMP_DIR_ADDR) -#define gaPAE_TmpDir ((tTabEnt*)PAE_TMP_DIR_ADDR) -#define gaPAE_TmpPDPT ((tTabEnt*)PAE_TMP_PDPT_ADDR) - int gbUsePAE = 0; -tMutex glTempMappings; -tMutex glTempFractal; -Uint32 gWorkerStacks[(NUM_WORKER_STACKS+31)/32]; - int giLastUsedWorker = 0; -struct sPageInfo { - void *Node; - tVAddr Base; - Uint64 Offset; - int Length; - int Flags; -} *gaMappedRegions; // sizeof = 24 bytes - -// === CODE === -/** - * \fn void MM_PreinitVirtual(void) - * \brief Maps the fractal mappings - */ -void MM_PreinitVirtual(void) -{ - gaInitPageDir[ PAGE_TABLE_ADDR >> 22 ] = ((tTabEnt)&gaInitPageDir - KERNEL_BASE) | 3; - INVLPG( PAGE_TABLE_ADDR ); -} - -/** - * \fn void MM_InstallVirtual(void) - * \brief Sets up the constant page mappings - */ -void MM_InstallVirtual(void) -{ - int i; - - // --- Pre-Allocate kernel tables - for( i = KERNEL_BASE>>22; i < 1024; i ++ ) - { - if( gaPageDir[ i ] ) continue; - // Skip stack tables, they are process unique - if( i > MM_KERNEL_STACKS >> 22 && i < MM_KERNEL_STACKS_END >> 22) { - gaPageDir[ i ] = 0; - continue; - } - // Preallocate table - gaPageDir[ i ] = MM_AllocPhys() | 3; - INVLPG( &gaPageTable[i*1024] ); - memset( &gaPageTable[i*1024], 0, 0x1000 ); - } - - // Unset kernel on the User Text pages - for( i = ((tVAddr)&_UsertextEnd-(tVAddr)&_UsertextBase+0xFFF)/4096; i--; ) { - MM_SetFlags( (tVAddr)&_UsertextBase + i*4096, 0, MM_PFLAG_KERNEL ); - } - - *gpTmpCR3 = 0; -} - -/** - * \brief Cleans up the SMP required mappings - */ -void MM_FinishVirtualInit(void) -{ - gaInitPageDir[ 0 ] = 0; -} - -/** - * \fn void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs) - * \brief Called on a page fault - */ -void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs) -{ - //ENTER("xAddr bErrorCode", Addr, ErrorCode); - - // -- Check for COW -- - if( gaPageDir [Addr>>22] & PF_PRESENT && gaPageTable[Addr>>12] & PF_PRESENT - && gaPageTable[Addr>>12] & PF_COW ) - { - tPAddr paddr; - if(MM_GetRefCount( gaPageTable[Addr>>12] & ~0xFFF ) == 1) - { - gaPageTable[Addr>>12] &= ~PF_COW; - gaPageTable[Addr>>12] |= PF_PRESENT|PF_WRITE; - } - else - { - //Log("MM_PageFault: COW - MM_DuplicatePage(0x%x)", Addr); - paddr = MM_DuplicatePage( Addr ); - MM_DerefPhys( gaPageTable[Addr>>12] & ~0xFFF ); - gaPageTable[Addr>>12] &= PF_USER; - gaPageTable[Addr>>12] |= paddr|PF_PRESENT|PF_WRITE; - } - -// Log_Debug("MMVirt", "COW for %p (%P)", Addr, gaPageTable[Addr>>12]); - - INVLPG( Addr & ~0xFFF ); - return; - } - - // Disable instruction tracing - __ASM__("pushf; andw $0xFEFF, 0(%esp); popf"); - Proc_GetCurThread()->bInstrTrace = 0; - - // If it was a user, tell the thread handler - if(ErrorCode & 4) { - Log_Warning("MMVirt", "User %s %s memory%s", - (ErrorCode&2?"write to":"read from"), - (ErrorCode&1?"bad/locked":"non-present"), - (ErrorCode&16?" (Instruction Fetch)":"") - ); - Log_Warning("MMVirt", "Instruction %04x:%08x accessed %p", Regs->cs, Regs->eip, Addr); - __ASM__("sti"); // Restart IRQs - #if 1 - Error_Backtrace(Regs->eip, Regs->ebp); - #endif - Threads_SegFault(Addr); - return ; - } - - Debug_KernelPanic(); - - // -- Check Error Code -- - if(ErrorCode & 8) - Warning("Reserved Bits Trashed!"); - else - { - Warning("Kernel %s %s memory%s", - (ErrorCode&2?"write to":"read from"), - (ErrorCode&1?"bad/locked":"non-present"), - (ErrorCode&16?" (Instruction Fetch)":"") - ); - } - - Log("CPU %i - Code at %p accessed %p", GetCPUNum(), Regs->eip, Addr); - // Print Stack Backtrace - Error_Backtrace(Regs->eip, Regs->ebp); - - #if 0 - Log("gaPageDir[0x%x] = 0x%x", Addr>>22, gaPageDir[Addr>>22]); - if( gaPageDir[Addr>>22] & PF_PRESENT ) - Log("gaPageTable[0x%x] = 0x%x", Addr>>12, gaPageTable[Addr>>12]); - #endif - //MM_DumpTables(0, -1); - - // Register Dump - Log("EAX %08x ECX %08x EDX %08x EBX %08x", Regs->eax, Regs->ecx, Regs->edx, Regs->ebx); - Log("ESP %08x EBP %08x ESI %08x EDI %08x", Regs->esp, Regs->ebp, Regs->esi, Regs->edi); - //Log("SS:ESP %04x:%08x", Regs->ss, Regs->esp); - Log("CS:EIP %04x:%08x", Regs->cs, Regs->eip); - Log("DS %04x ES %04x FS %04x GS %04x", Regs->ds, Regs->es, Regs->fs, Regs->gs); - { - Uint dr0, dr1; - __ASM__ ("mov %%dr0, %0":"=r"(dr0):); - __ASM__ ("mov %%dr1, %0":"=r"(dr1):); - Log("DR0 %08x DR1 %08x", dr0, dr1); - } - - Panic("Page Fault at 0x%x (Accessed 0x%x)", Regs->eip, Addr); -} - -/** - * \fn void MM_DumpTables(tVAddr Start, tVAddr End) - * \brief Dumps the layout of the page tables - */ -void MM_DumpTables(tVAddr Start, tVAddr End) -{ - tVAddr rangeStart = 0; - tPAddr expected = 0; - void *expected_node = NULL, *tmpnode = NULL; - tVAddr curPos; - Uint page; - const tPAddr MASK = ~0xF78; - - Start >>= 12; End >>= 12; - - #if 0 - Log("Directory Entries:"); - for(page = Start >> 10; - page < (End >> 10)+1; - page ++) - { - if(gaPageDir[page]) - { - Log(" 0x%08x-0x%08x :: 0x%08x", - page<<22, ((page+1)<<22)-1, - gaPageDir[page]&~0xFFF - ); - } - } - #endif - - Log("Table Entries:"); - for(page = Start, curPos = Start<<12; - page < End; - curPos += 0x1000, page++) - { - if( !(gaPageDir[curPos>>22] & PF_PRESENT) - || !(gaPageTable[page] & PF_PRESENT) - || (gaPageTable[page] & MASK) != expected - || (tmpnode=NULL,MM_GetPageNode(expected, &tmpnode), tmpnode != expected_node)) - { - if(expected) { - tPAddr orig = gaPageTable[rangeStart>>12]; - Log(" 0x%08x => 0x%08x - 0x%08x (%s%s%s%s%s) %p", - rangeStart, - orig & ~0xFFF, - curPos - rangeStart, - (orig & PF_NOPAGE ? "P" : "-"), - (orig & PF_COW ? "C" : "-"), - (orig & PF_GLOBAL ? "G" : "-"), - (orig & PF_USER ? "U" : "-"), - (orig & PF_WRITE ? "W" : "-"), - expected_node - ); - expected = 0; - } - if( !(gaPageDir[curPos>>22] & PF_PRESENT) ) continue; - if( !(gaPageTable[curPos>>12] & PF_PRESENT) ) continue; - - expected = (gaPageTable[page] & MASK); - MM_GetPageNode(expected, &expected_node); - rangeStart = curPos; - } - if(expected) expected += 0x1000; - } - - if(expected) { - tPAddr orig = gaPageTable[rangeStart>>12]; - Log("0x%08x => 0x%08x - 0x%08x (%s%s%s%s%s) %p", - rangeStart, - orig & ~0xFFF, - curPos - rangeStart, - (orig & PF_NOPAGE ? "p" : "-"), - (orig & PF_COW ? "C" : "-"), - (orig & PF_GLOBAL ? "G" : "-"), - (orig & PF_USER ? "U" : "-"), - (orig & PF_WRITE ? "W" : "-"), - expected_node - ); - expected = 0; - } -} - -/** - * \fn tPAddr MM_Allocate(tVAddr VAddr) - */ -tPAddr MM_Allocate(tVAddr VAddr) -{ - tPAddr paddr; - //ENTER("xVAddr", VAddr); - //__ASM__("xchg %bx,%bx"); - // Check if the directory is mapped - if( gaPageDir[ VAddr >> 22 ] == 0 ) - { - // Allocate directory - paddr = MM_AllocPhys(); - if( paddr == 0 ) { - Warning("MM_Allocate - Out of Memory (Called by %p)", __builtin_return_address(0)); - //LEAVE('i',0); - return 0; - } - // Map and mark as user (if needed) - gaPageDir[ VAddr >> 22 ] = paddr | 3; - if(VAddr < MM_USER_MAX) gaPageDir[ VAddr >> 22 ] |= PF_USER; - - INVLPG( &gaPageDir[ VAddr >> 22 ] ); - memsetd( &gaPageTable[ (VAddr >> 12) & ~0x3FF ], 0, 1024 ); - } - // Check if the page is already allocated - else if( gaPageTable[ VAddr >> 12 ] != 0 ) { - Warning("MM_Allocate - Allocating to used address (%p)", VAddr); - //LEAVE('X', gaPageTable[ VAddr >> 12 ] & ~0xFFF); - return gaPageTable[ VAddr >> 12 ] & ~0xFFF; - } - - // Allocate - paddr = MM_AllocPhys(); - //LOG("paddr = 0x%llx", paddr); - if( paddr == 0 ) { - Warning("MM_Allocate - Out of Memory when allocating at %p (Called by %p)", - VAddr, __builtin_return_address(0)); - //LEAVE('i',0); - return 0; - } - // Map - gaPageTable[ VAddr >> 12 ] = paddr | 3; - // Mark as user - if(VAddr < MM_USER_MAX) gaPageTable[ VAddr >> 12 ] |= PF_USER; - // Invalidate Cache for address - INVLPG( VAddr & ~0xFFF ); - - //LEAVE('X', paddr); - return paddr; -} - -/** - * \fn void MM_Deallocate(tVAddr VAddr) - */ -void MM_Deallocate(tVAddr VAddr) -{ - if( gaPageDir[ VAddr >> 22 ] == 0 ) { - Warning("MM_Deallocate - Directory not mapped"); - return; - } - - if(gaPageTable[ VAddr >> 12 ] == 0) { - Warning("MM_Deallocate - Page is not allocated"); - return; - } - - // Dereference page - MM_DerefPhys( gaPageTable[ VAddr >> 12 ] & ~0xFFF ); - // Clear page - gaPageTable[ VAddr >> 12 ] = 0; -} - -/** - * \fn tPAddr MM_GetPhysAddr(tVAddr Addr) - * \brief Checks if the passed address is accesable - */ -tPAddr MM_GetPhysAddr(tVAddr Addr) -{ - if( !(gaPageDir[Addr >> 22] & 1) ) - return 0; - if( !(gaPageTable[Addr >> 12] & 1) ) - return 0; - return (gaPageTable[Addr >> 12] & ~0xFFF) | (Addr & 0xFFF); -} - -/** - * \fn void MM_SetCR3(Uint CR3) - * \brief Sets the current process space - */ -void MM_SetCR3(Uint CR3) -{ - __ASM__("mov %0, %%cr3"::"r"(CR3)); -} - -/** - * \fn int MM_Map(tVAddr VAddr, tPAddr PAddr) - * \brief Map a physical page to a virtual one - */ -int MM_Map(tVAddr VAddr, tPAddr PAddr) -{ - //ENTER("xVAddr xPAddr", VAddr, PAddr); - // Sanity check - if( PAddr & 0xFFF || VAddr & 0xFFF ) { - Log_Warning("MM_Virt", "MM_Map - Physical or Virtual Addresses are not aligned"); - //LEAVE('i', 0); - return 0; - } - - // Align addresses - PAddr &= ~0xFFF; VAddr &= ~0xFFF; - - // Check if the directory is mapped - if( gaPageDir[ VAddr >> 22 ] == 0 ) - { - tPAddr tmp = MM_AllocPhys(); - if( tmp == 0 ) - return 0; - gaPageDir[ VAddr >> 22 ] = tmp | 3; - - // Mark as user - if(VAddr < MM_USER_MAX) gaPageDir[ VAddr >> 22 ] |= PF_USER; - - INVLPG( &gaPageTable[ (VAddr >> 12) & ~0x3FF ] ); - memsetd( &gaPageTable[ (VAddr >> 12) & ~0x3FF ], 0, 1024 ); - } - // Check if the page is already allocated - else if( gaPageTable[ VAddr >> 12 ] != 0 ) { - Warning("MM_Map - Allocating to used address"); - //LEAVE('i', 0); - return 0; - } - - // Map - gaPageTable[ VAddr >> 12 ] = PAddr | 3; - // Mark as user - if(VAddr < MM_USER_MAX) gaPageTable[ VAddr >> 12 ] |= PF_USER; - - //LOG("gaPageTable[ 0x%x ] = (Uint)%p = 0x%x", - // VAddr >> 12, &gaPageTable[ VAddr >> 12 ], gaPageTable[ VAddr >> 12 ]); - - // Reference - MM_RefPhys( PAddr ); - - //LOG("INVLPG( 0x%x )", VAddr); - INVLPG( VAddr ); - - //LEAVE('i', 1); - return 1; -} - -/** - * \brief Clear user's address space - */ -void MM_ClearUser(void) -{ - Uint i, j; - - for( i = 0; i < (MM_USER_MAX>>22); i ++ ) - { - // Check if directory is not allocated - if( !(gaPageDir[i] & PF_PRESENT) ) { - gaPageDir[i] = 0; - continue; - } - - // Deallocate tables - for( j = 0; j < 1024; j ++ ) - { - if( gaPageTable[i*1024+j] & 1 ) - MM_DerefPhys( gaPageTable[i*1024+j] & ~0xFFF ); - gaPageTable[i*1024+j] = 0; - } - - // Deallocate directory - MM_DerefPhys( gaPageDir[i] & ~0xFFF ); - gaPageDir[i] = 0; - INVLPG( &gaPageTable[i*1024] ); - } - INVLPG( gaPageDir ); -} - -/** - * \brief Deallocate an address space - */ -void MM_ClearSpace(Uint32 CR3) -{ - int i, j; - - if(CR3 == (*gpPageCR3 & ~0xFFF)) { - Log_Error("MMVirt", "Can't clear current address space"); - return ; - } - - if( MM_GetRefCount(CR3) > 1 ) { - MM_DerefPhys(CR3); - Log_Log("MMVirt", "CR3 %P is still referenced, not cleaning (but dereferenced)", CR3); - return ; - } - - Log_Debug("MMVirt", "Clearing out address space 0x%x from 0x%x", CR3, *gpPageCR3); - - GET_TEMP_MAPPING(CR3); - INVLPG( gaTmpDir ); - - for( i = 0; i < 1024; i ++ ) - { - Uint32 *table = &gaTmpTable[i*1024]; - if( !(gaTmpDir[i] & PF_PRESENT) ) - continue ; - - INVLPG( table ); - - if( i < 768 || (i > MM_KERNEL_STACKS >> 22 && i < MM_KERNEL_STACKS_END >> 22) ) - { - for( j = 0; j < 1024; j ++ ) - { - if( !(table[j] & 1) ) - continue; - MM_DerefPhys( table[j] & ~0xFFF ); - } - } - - if( i != (PAGE_TABLE_ADDR >> 22) ) - { - MM_DerefPhys( gaTmpDir[i] & ~0xFFF ); - } - } - - - MM_DerefPhys( CR3 ); - - REL_TEMP_MAPPING(); -} - -/** - * \fn tPAddr MM_Clone(void) - * \brief Clone the current address space - */ -tPAddr MM_Clone(int bNoUserCopy) -{ - Uint i, j; - tPAddr ret; - Uint page = 0; - tVAddr kStackBase = Proc_GetCurThread()->KernelStack - MM_KERNEL_STACK_SIZE; - void *tmp; - - // Create Directory Table - ret = MM_AllocPhys(); - if( ret == 0 ) { - return 0; - } - - // Map - GET_TEMP_MAPPING( ret ); - INVLPG( gaTmpDir ); - memsetd( gaTmpDir, 0, 1024 ); - - if( Threads_GetPID() != 0 && !bNoUserCopy ) - { - // Copy Tables - for( i = 0; i < 768; i ++) - { - // Check if table is allocated - if( !(gaPageDir[i] & PF_PRESENT) ) { - gaTmpDir[i] = 0; - page += 1024; - continue; - } - - // Allocate new table - gaTmpDir[i] = MM_AllocPhys() | (gaPageDir[i] & 7); - INVLPG( &gaTmpTable[page] ); - // Fill - for( j = 0; j < 1024; j ++, page++ ) - { - if( !(gaPageTable[page] & PF_PRESENT) ) { - gaTmpTable[page] = 0; - continue; - } - - // Refrence old page - MM_RefPhys( gaPageTable[page] & ~0xFFF ); - // Add to new table - if(gaPageTable[page] & PF_WRITE) { - gaTmpTable[page] = (gaPageTable[page] & ~PF_WRITE) | PF_COW; - gaPageTable[page] = (gaPageTable[page] & ~PF_WRITE) | PF_COW; - INVLPG( page << 12 ); - } - else - gaTmpTable[page] = gaPageTable[page]; - } - } - } - - // Map in kernel tables (and make fractal mapping) - for( i = 768; i < 1024; i ++ ) - { - // Fractal - if( i == (PAGE_TABLE_ADDR >> 22) ) { - gaTmpDir[ PAGE_TABLE_ADDR >> 22 ] = *gpTmpCR3; - continue; - } - if( i == (TMP_TABLE_ADDR >> 22) ) { - gaTmpDir[ TMP_TABLE_ADDR >> 22 ] = 0; - continue ; - } - - if( gaPageDir[i] == 0 ) { - gaTmpDir[i] = 0; - continue; - } - - //LOG("gaPageDir[%x/4] = 0x%x", i*4, gaPageDir[i]); - MM_RefPhys( gaPageDir[i] & ~0xFFF ); - gaTmpDir[i] = gaPageDir[i]; - } - - // Allocate kernel stack - for(i = MM_KERNEL_STACKS >> 22; i < MM_KERNEL_STACKS_END >> 22; i ++ ) - { - // Check if directory is allocated - if( (gaPageDir[i] & 1) == 0 ) { - gaTmpDir[i] = 0; - continue; - } - - // We don't care about other kernel stacks, just the current one - if( i != kStackBase >> 22 ) { - MM_DerefPhys( gaPageDir[i] & ~0xFFF ); - gaTmpDir[i] = 0; - continue; - } - - // Create a copy - gaTmpDir[i] = MM_AllocPhys() | 3; - INVLPG( &gaTmpTable[i*1024] ); - for( j = 0; j < 1024; j ++ ) - { - // Is the page allocated? If not, skip - if( !(gaPageTable[i*1024+j] & 1) ) { - gaTmpTable[i*1024+j] = 0; - continue; - } - - // We don't care about other kernel stacks - if( ((i*1024+j)*4096 & ~(MM_KERNEL_STACK_SIZE-1)) != kStackBase ) { - gaTmpTable[i*1024+j] = 0; - continue; - } - - // Allocate page - gaTmpTable[i*1024+j] = MM_AllocPhys() | 3; - - MM_RefPhys( gaTmpTable[i*1024+j] & ~0xFFF ); - - tmp = (void *) MM_MapTemp( gaTmpTable[i*1024+j] & ~0xFFF ); - memcpy( tmp, (void *)( (i*1024+j)*0x1000 ), 0x1000 ); - MM_FreeTemp( (Uint)tmp ); - } - } - - REL_TEMP_MAPPING(); - - //LEAVE('x', ret); - return ret; -} - -/** - * \fn tVAddr MM_NewKStack(void) - * \brief Create a new kernel stack - */ -tVAddr MM_NewKStack(void) -{ - tVAddr base; - Uint i; - for(base = MM_KERNEL_STACKS; base < MM_KERNEL_STACKS_END; base += MM_KERNEL_STACK_SIZE) - { - // Check if space is free - if(MM_GetPhysAddr(base) != 0) continue; - // Allocate - //for(i = MM_KERNEL_STACK_SIZE; i -= 0x1000 ; ) - for(i = 0; i < MM_KERNEL_STACK_SIZE; i += 0x1000 ) - { - if( MM_Allocate(base+i) == 0 ) - { - // On error, print a warning and return error - Warning("MM_NewKStack - Out of memory"); - // - Clean up - //for( i += 0x1000 ; i < MM_KERNEL_STACK_SIZE; i += 0x1000 ) - // MM_Deallocate(base+i); - return 0; - } - } - // Success -// Log("MM_NewKStack - Allocated %p", base + MM_KERNEL_STACK_SIZE); - return base+MM_KERNEL_STACK_SIZE; - } - // No stacks left - Log_Warning("MMVirt", "MM_NewKStack - No address space left"); - return 0; -} - -/** - * \fn tVAddr MM_NewWorkerStack() - * \brief Creates a new worker stack - */ -tVAddr MM_NewWorkerStack(Uint *StackContents, size_t ContentsSize) -{ - Uint base, addr; - tVAddr tmpPage; - tPAddr page; - - // TODO: Thread safety - // Find a free worker stack address - for(base = giLastUsedWorker; base < NUM_WORKER_STACKS; base++) - { - // Used block - if( gWorkerStacks[base/32] == -1 ) { - base += 31; base &= ~31; - base --; // Counteracted by the base++ - continue; - } - // Used stack - if( gWorkerStacks[base/32] & (1 << base) ) { - continue; - } - break; - } - if(base >= NUM_WORKER_STACKS) { - Warning("Uh-oh! Out of worker stacks"); - return 0; - } - - // It's ours now! - gWorkerStacks[base/32] |= (1 << base); - // Make life easier for later calls - giLastUsedWorker = base; - // We have one - base = WORKER_STACKS + base * WORKER_STACK_SIZE; - //Log(" MM_NewWorkerStack: base = 0x%x", base); - - // Set the temp fractals to TID0's address space - GET_TEMP_MAPPING( ((Uint)gaInitPageDir - KERNEL_BASE) ); - INVLPG( gaTmpDir ); - - // Check if the directory is mapped (we are assuming that the stacks - // will fit neatly in a directory) - //Log(" MM_NewWorkerStack: gaTmpDir[ 0x%x ] = 0x%x", base>>22, gaTmpDir[ base >> 22 ]); - if(gaTmpDir[ base >> 22 ] == 0) { - gaTmpDir[ base >> 22 ] = MM_AllocPhys() | 3; - INVLPG( &gaTmpTable[ (base>>12) & ~0x3FF ] ); - } - - // Mapping Time! - for( addr = 0; addr < WORKER_STACK_SIZE; addr += 0x1000 ) - { - page = MM_AllocPhys(); - gaTmpTable[ (base + addr) >> 12 ] = page | 3; - } - - // Release temporary fractal - REL_TEMP_MAPPING(); - - // NOTE: Max of 1 page - // `page` is the last allocated page from the previious for loop - tmpPage = MM_MapTemp( page ); - memcpy( (void*)( tmpPage + (0x1000 - ContentsSize) ), StackContents, ContentsSize); - MM_FreeTemp(tmpPage); - - //Log("MM_NewWorkerStack: RETURN 0x%x", base); - return base + WORKER_STACK_SIZE; -} - -/** - * \fn void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask) - * \brief Sets the flags on a page - */ -void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask) -{ - tTabEnt *ent; - if( !(gaPageDir[VAddr >> 22] & 1) ) return ; - if( !(gaPageTable[VAddr >> 12] & 1) ) return ; - - ent = &gaPageTable[VAddr >> 12]; - - // Read-Only - if( Mask & MM_PFLAG_RO ) - { - if( Flags & MM_PFLAG_RO ) { - *ent &= ~PF_WRITE; - } - else { - gaPageDir[VAddr >> 22] |= PF_WRITE; - *ent |= PF_WRITE; - } - } - - // Kernel - if( Mask & MM_PFLAG_KERNEL ) - { - if( Flags & MM_PFLAG_KERNEL ) { - *ent &= ~PF_USER; - } - else { - gaPageDir[VAddr >> 22] |= PF_USER; - *ent |= PF_USER; - } - } - - // Copy-On-Write - if( Mask & MM_PFLAG_COW ) - { - if( Flags & MM_PFLAG_COW ) { - *ent &= ~PF_WRITE; - *ent |= PF_COW; - } - else { - *ent &= ~PF_COW; - *ent |= PF_WRITE; - } - } - - //Log("MM_SetFlags: *ent = 0x%08x, gaPageDir[%i] = 0x%08x", - // *ent, VAddr >> 22, gaPageDir[VAddr >> 22]); -} - -/** - * \brief Get the flags on a page - */ -Uint MM_GetFlags(tVAddr VAddr) -{ - tTabEnt *ent; - Uint ret = 0; - - // Validity Check - if( !(gaPageDir[VAddr >> 22] & 1) ) return 0; - if( !(gaPageTable[VAddr >> 12] & 1) ) return 0; - - ent = &gaPageTable[VAddr >> 12]; - - // Read-Only - if( !(*ent & PF_WRITE) ) ret |= MM_PFLAG_RO; - // Kernel - if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL; - // Copy-On-Write - if( *ent & PF_COW ) ret |= MM_PFLAG_COW; - - return ret; -} - -/** - * \brief Check if the provided buffer is valid - * \return Boolean valid - */ -int MM_IsValidBuffer(tVAddr Addr, size_t Size) -{ - int bIsUser; - int dir, tab; - - Size += Addr & (PAGE_SIZE-1); - Addr &= ~(PAGE_SIZE-1); - - dir = Addr >> 22; - tab = Addr >> 12; - -// Debug("Addr = %p, Size = 0x%x, dir = %i, tab = %i", Addr, Size, dir, tab); - - if( !(gaPageDir[dir] & 1) ) return 0; - if( !(gaPageTable[tab] & 1) ) return 0; - - bIsUser = !!(gaPageTable[tab] & PF_USER); - - while( Size >= PAGE_SIZE ) - { - if( (tab & 1023) == 0 ) - { - dir ++; - if( !(gaPageDir[dir] & 1) ) return 0; - } - - if( !(gaPageTable[tab] & 1) ) return 0; - if( bIsUser && !(gaPageTable[tab] & PF_USER) ) return 0; - - tab ++; - Size -= PAGE_SIZE; - } - return 1; -} - -/** - * \fn tPAddr MM_DuplicatePage(tVAddr VAddr) - * \brief Duplicates a virtual page to a physical one - */ -tPAddr MM_DuplicatePage(tVAddr VAddr) -{ - tPAddr ret; - Uint temp; - int wasRO = 0; - - //ENTER("xVAddr", VAddr); - - // Check if mapped - if( !(gaPageDir [VAddr >> 22] & PF_PRESENT) ) return 0; - if( !(gaPageTable[VAddr >> 12] & PF_PRESENT) ) return 0; - - // Page Align - VAddr &= ~0xFFF; - - // Allocate new page - ret = MM_AllocPhys(); - if( !ret ) { - return 0; - } - - // Write-lock the page (to keep data constistent), saving its R/W state - wasRO = (gaPageTable[VAddr >> 12] & PF_WRITE ? 0 : 1); - gaPageTable[VAddr >> 12] &= ~PF_WRITE; - INVLPG( VAddr ); - - // Copy Data - temp = MM_MapTemp(ret); - memcpy( (void*)temp, (void*)VAddr, 0x1000 ); - MM_FreeTemp(temp); - - // Restore Writeable status - if(!wasRO) gaPageTable[VAddr >> 12] |= PF_WRITE; - INVLPG(VAddr); - - //LEAVE('X', ret); - return ret; -} - -/** - * \fn Uint MM_MapTemp(tPAddr PAddr) - * \brief Create a temporary memory mapping - * \todo Show Luigi Barone (C Lecturer) and see what he thinks - */ -tVAddr MM_MapTemp(tPAddr PAddr) -{ - int i; - - //ENTER("XPAddr", PAddr); - - PAddr &= ~0xFFF; - - //LOG("glTempMappings = %i", glTempMappings); - - for(;;) - { - Mutex_Acquire( &glTempMappings ); - - for( i = 0; i < NUM_TEMP_PAGES; i ++ ) - { - // Check if page used - if(gaPageTable[ (TEMP_MAP_ADDR >> 12) + i ] & 1) continue; - // Mark as used - gaPageTable[ (TEMP_MAP_ADDR >> 12) + i ] = PAddr | 3; - INVLPG( TEMP_MAP_ADDR + (i << 12) ); - //LEAVE('p', TEMP_MAP_ADDR + (i << 12)); - Mutex_Release( &glTempMappings ); - return TEMP_MAP_ADDR + (i << 12); - } - Mutex_Release( &glTempMappings ); - Threads_Yield(); // TODO: Use a sleep queue here instead - } -} - -/** - * \fn void MM_FreeTemp(tVAddr PAddr) - * \brief Free's a temp mapping - */ -void MM_FreeTemp(tVAddr VAddr) -{ - int i = VAddr >> 12; - //ENTER("xVAddr", VAddr); - - if(i >= (TEMP_MAP_ADDR >> 12)) - gaPageTable[ i ] = 0; - - //LEAVE('-'); -} - -/** - * \fn tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number) - * \brief Allocates a contigous number of pages - */ -tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number) -{ - int i, j; - - PAddr &= ~0xFFF; - - // Scan List - for( i = 0; i < NUM_HW_PAGES; i ++ ) - { - // Check if addr used - if( gaPageTable[ (HW_MAP_ADDR >> 12) + i ] & 1 ) - continue; - - // Check possible region - for( j = 0; j < Number && i + j < NUM_HW_PAGES; j ++ ) - { - // If there is an allocated page in the region we are testing, break - if( gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] & 1 ) break; - } - // Is it all free? - if( j == Number ) - { - // Allocate - for( j = 0; j < Number; j++ ) { - MM_RefPhys( PAddr + (j<<12) ); - gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] = (PAddr + (j<<12)) | 3; - } - return HW_MAP_ADDR + (i<<12); - } - } - // If we don't find any, return NULL - return 0; -} - -/** - * \fn tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr) - * \brief Allocates DMA physical memory - * \param Pages Number of pages required - * \param MaxBits Maximum number of bits the physical address can have - * \param PhysAddr Pointer to the location to place the physical address allocated - * \return Virtual address allocate - */ -tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr) -{ - tPAddr maxCheck = (1 << MaxBits); - tPAddr phys; - tVAddr ret; - - ENTER("iPages iMaxBits pPhysAddr", Pages, MaxBits, PhysAddr); - - // Sanity Check - if(MaxBits < 12 || !PhysAddr) { - LEAVE('i', 0); - return 0; - } - - // Bound - if(MaxBits >= PHYS_BITS) maxCheck = -1; - - // Fast Allocate - if(Pages == 1 && MaxBits >= PHYS_BITS) - { - phys = MM_AllocPhys(); - if( !phys ) { - *PhysAddr = 0; - LEAVE_RET('i', 0); - } - *PhysAddr = phys; - ret = MM_MapHWPages(phys, 1); - if(ret == 0) { - MM_DerefPhys(phys); - LEAVE('i', 0); - return 0; - } - LEAVE('x', ret); - return ret; - } - - // Slow Allocate - phys = MM_AllocPhysRange(Pages, MaxBits); - // - Was it allocated? - if(phys == 0) { - LEAVE('i', 0); - return 0; - } - - // Allocated successfully, now map - ret = MM_MapHWPages(phys, Pages); - if( ret == 0 ) { - // If it didn't map, free then return 0 - for(;Pages--;phys+=0x1000) - MM_DerefPhys(phys); - LEAVE('i', 0); - return 0; - } - - *PhysAddr = phys; - LEAVE('x', ret); - return ret; -} - -/** - * \fn void MM_UnmapHWPages(tVAddr VAddr, Uint Number) - * \brief Unmap a hardware page - */ -void MM_UnmapHWPages(tVAddr VAddr, Uint Number) -{ - int i, j; - - //Log_Debug("VirtMem", "MM_UnmapHWPages: (VAddr=0x%08x, Number=%i)", VAddr, Number); - - // Sanity Check - if(VAddr < HW_MAP_ADDR || VAddr+Number*0x1000 > HW_MAP_MAX) return; - - i = VAddr >> 12; - - Mutex_Acquire( &glTempMappings ); // Temp and HW share a directory, so they share a lock - - for( j = 0; j < Number; j++ ) - { - MM_DerefPhys( gaPageTable[ i + j ] & ~0xFFF ); - gaPageTable[ i + j ] = 0; - } - - Mutex_Release( &glTempMappings ); -} - diff --git a/Kernel/arch/x86/pci.c b/Kernel/arch/x86/pci.c deleted file mode 100644 index 7a0a2c11..00000000 --- a/Kernel/arch/x86/pci.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Acess2 - * arch/x86/pci.h - x86 PCI Bus Access - */ -#define DEBUG 0 -#include -#include - -// === CODE === -Uint32 PCI_CfgReadDWord(Uint32 Address) -{ - Address |= 0x80000000; - outd(0xCF8, Address); - return ind(0xCFC); -} - -void PCI_CfgWriteDWord(Uint32 Address, Uint32 Data) -{ - Address |= 0x80000000; - outd(0xCF8, Address); - outd(0xCFC, Data); -} diff --git a/Kernel/arch/x86/proc.asm b/Kernel/arch/x86/proc.asm deleted file mode 100644 index 8da79711..00000000 --- a/Kernel/arch/x86/proc.asm +++ /dev/null @@ -1,375 +0,0 @@ -; AcessOS Microkernel Version -; Start.asm - -[bits 32] - -%define SAVEFLAG_FPU 0x1 - -KERNEL_BASE equ 0xC0000000 - -KSTACK_USERSTATE_SIZE equ (4+8+1+5)*4 ; SRegs, GPRegs, CPU, IRET - -[section .text] - -[global NewTaskHeader] -NewTaskHeader: - mov eax, [esp] - mov dr0, eax - - mov eax, [esp+4] - add esp, 12 ; Thread, Function, Arg Count - call eax - - push eax ; Ret val - push 0 ; 0 = This Thread - call Threads_Exit - -[extern MM_Clone] -[global Proc_CloneInt] -Proc_CloneInt: - pusha - ; Save RSP - mov eax, [esp+0x20+4] - mov [eax], esp - push DWORD [esp+0x20+12] - call MM_Clone - add esp, 4 - ; Save CR3 - mov esi, [esp+0x20+8] - mov [esi], eax - ; Undo the pusha - add esp, 0x20 - mov eax, .newTask - ret -.newTask: - popa - xor eax, eax - ret - -[global SwitchTasks] -; + 4 = New RSP -; + 8 = Old RSP save loc -; +12 = New RIP -; +16 = Old RIP save loc -; +20 = CR3 -SwitchTasks: - pusha - - ; Old IP - mov eax, [esp+0x20+16] - test eax, eax - jz .nosave - mov DWORD [eax], .restore - ; Old SP - mov eax, [esp+0x20+8] - mov [eax], esp - -.nosave: - mov ecx, [esp+0x20+12] ; New IP - mov eax, [esp+0x20+20] ; New CR3 - mov esp, [esp+0x20+ 4] ; New SP - - test eax, eax - jz .setState - mov cr3, eax - invlpg [esp] - invlpg [esp+0x1000] -.setState: - jmp ecx - -.restore: - popa - xor eax, eax - ret - -[global Proc_InitialiseSSE] -Proc_InitialiseSSE: - mov eax, cr4 - or eax, (1 << 9)|(1 << 10) ; Set OSFXSR and OSXMMEXCPT - mov cr4, eax - mov eax, cr0 - and ax, ~(1 << 2) ; Clear EM - or eax, (1 << 1) ; Set MP - mov eax, cr0 - ret -[global Proc_DisableSSE] -Proc_DisableSSE: - mov eax, cr0 - or ax, 1 << 3 ; Set TS - mov cr0, eax - ret -[global Proc_EnableSSE] -Proc_EnableSSE: - mov eax, cr0 - and ax, ~(1 << 3) ; Clear TS - mov cr0, eax - ret - -[global Proc_SaveSSE] -Proc_SaveSSE: - mov eax, [esp+4] - fxsave [eax] - ret -[global Proc_RestoreSSE] -Proc_RestoreSSE: - mov eax, [esp+4] - fxrstor [eax] - ret - -%if USE_MP -[extern giMP_TimerCount] -[extern gpMP_LocalAPIC] -[extern Isr240.jmp] -[global SetAPICTimerCount] -SetAPICTimerCount: - pusha - push ds - push es - push fs - push gs - - mov ax, 0x10 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - - mov eax, [gpMP_LocalAPIC] - mov ecx, [eax+0x320] - test ecx, 0x00010000 - jz .setTime - mov DWORD [eax+0x380], 0xFFFFFFFF ; Set Initial Count - mov DWORD [eax+0x320], 0x000000F0 ; Enable the timer on IVT#0xEF (One Shot) - jmp .ret - -.setTime: - ; Get Timer Count - mov ecx, 0xFFFFFFFF - sub ecx, [eax+0x390] - mov DWORD [giMP_TimerCount], ecx - ; Disable APIC Timer - mov DWORD [eax+0x320], 0x000100EF - mov DWORD [eax+0x380], 0 - - ; Update Timer IRQ to the IRQ code - mov eax, SchedulerBase - sub eax, Isr240.jmp+5 - mov DWORD [Isr240.jmp+1], eax - - ;xchg bx, bx ; MAGIC BREAK -.ret: - mov dx, 0x20 - mov al, 0x20 - out dx, al ; ACK IRQ - pop gs - pop fs - pop es - pop ds - popa - add esp, 8 ; CPU ID / Error Code - iret -%endif -; -------------- -; Task Scheduler -; -------------- -[extern Proc_Scheduler] -[global SchedulerBase] -SchedulerBase: - pusha - push ds - push es - push fs - push gs - - pushf - and BYTE [esp+1], 0xFE ; Clear Trap Flag - popf - - mov eax, dr0 - push eax ; Debug Register 0, Current Thread - - mov ax, 0x10 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - - %if USE_MP - call GetCPUNum - mov ebx, eax - push eax ; Push as argument - %else - push 0 - %endif - - call Proc_Scheduler -[global scheduler_return] -scheduler_return: ; Used by some hackery in Proc_DumpThreadCPUState - - add esp, 4 ; Remove CPU Number (thread is poped later) - - %if USE_MP - test ebx, ebx - jnz .sendEOI - %endif - - mov al, 0x20 - out 0x20, al ; ACK IRQ - %if USE_MP - jmp .ret - -.sendEOI: - mov eax, DWORD [gpMP_LocalAPIC] - mov DWORD [eax+0x0B0], 0 - %endif -.ret: - pop eax ; Debug Register 0, Current Thread - mov dr0, eax - - pop gs - pop fs - pop es - pop ds - - popa - add esp, 4*2 ; CPU ID + Dummy error code - ; No Error code / int num - iret - -[extern Proc_Clone] -[extern Threads_Exit] -[global SpawnTask] -SpawnTask: - ; Call Proc_Clone with Flags=0 - xor eax, eax -; push eax - push eax - call Proc_Clone - add esp, 8 ; Remove arguments from stack - - test eax, eax - jnz .parent - - ; In child, so now set up stack frame - mov ebx, [esp+4] ; Child Function - mov edx, [esp+8] ; Argument - ; Child Function - push edx ; Argument - call ebx ; Function - ; Kill thread once done - push eax ; Exit Code - push 0 ; Kill this thread - call Threads_Exit ; Kill Thread - -.parent: - ret - -; void Proc_ReturnToUser(void *Method, Uint Parameter, tVAddr KernelStack) -; Calls a user fault handler -; -[global Proc_ReturnToUser] -[extern Proc_GetCurThread] -Proc_ReturnToUser: - push ebp - mov ebp, esp - ; [EBP+8]: handler to use - ; [EBP+12]: parameter - ; [EBP+16]: kernel stack top - - ; Get kernel stack - mov eax, [ebp+16] - sub eax, KSTACK_USERSTATE_SIZE - - ; - ; NOTE: This can cause corruption if the signal happens while the user - ; has called a kernel operation. - ; Good thing this can only be called on a user fault. - ; - - ; Validate user ESP - ; - Page Table - mov edx, [eax+KSTACK_USERSTATE_SIZE-12] ; User ESP is at top of kstack - 3*4 - mov ecx, edx - shr ecx, 22 - test BYTE [0xFC3F0000+ecx*4], 1 - jnz .justKillIt - ; - Page - mov ecx, edx - shr ecx, 12 - test BYTE [0xFC000000+ecx*4], 1 - jnz .justKillIt - ; Adjust - sub edx, 8 - ; - Page Table - mov ecx, edx - shr ecx, 22 - test BYTE [0xFC3F0000+ecx*4], 1 - jnz .justKillIt - ; - Page - mov ecx, edx - shr ecx, 12 - test BYTE [0xFC000000+ecx*4], 1 - jnz .justKillIt - - ; Get and alter User SP - mov edi, edx - mov edx, [ebp+12] ; Get parameter - mov [edi+4], edx ; save to user stack - mov [edi], DWORD User_Syscall_RetAndExit ; Return Address - - ; Restore Segment Registers - mov ax, 0x23 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - - push 0x23 ; SS - push edi ; ESP - push 0x202 ; EFLAGS (IP and Rsvd) - push 0x1B ; CS - mov eax, [ebp+8] ; Method to call - push eax ; EIP - - iret - - ; Just kill the bleeding thing - ; (I know it calls int 0xAC in kernel mode, but meh) -.justKillIt: - xor eax, eax - xor ebx, ebx - dec ebx ; EBX = -1 - int 0xAC - -[global GetCPUNum] -GetCPUNum: ; TODO: Store in debug registers - mov eax, dr1 - ret - -[extern GetEIP] -[global GetEIP_Sched] -[global GetEIP_Sched_ret] -GetEIP_Sched_ret equ GetEIP_Sched.ret -GetEIP_Sched: - call GetEIP -GetEIP_Sched.ret: - ret - -; Usermode code exported by the kernel -[section .usertext] -; Export a place for the user to jump to to call a syscall -; - Allows the kernel to change the method easily -User_Syscall: - xchg bx, bx ; MAGIC BREAKPOINT - int 0xAC - -; A place to return to and exit -User_Syscall_RetAndExit: - push eax - call User_Syscall_Exit -User_Syscall_Exit: - xor eax, eax - mov ebx, [esp+4] - int 0xAC - -; vim: ft=nasm ts=8 diff --git a/Kernel/arch/x86/proc.c b/Kernel/arch/x86/proc.c deleted file mode 100644 index 82a3f409..00000000 --- a/Kernel/arch/x86/proc.c +++ /dev/null @@ -1,1025 +0,0 @@ -/* - * AcessOS Microkernel Version - * proc.c - */ -#include -#include -#include -#include -#include -#include -#if USE_MP -# include -#endif -#include -#include - -// === FLAGS === -#define DEBUG_TRACE_SWITCH 0 -#define DEBUG_DISABLE_DOUBLEFAULT 1 -#define DEBUG_VERY_SLOW_PERIOD 0 - -// === CONSTANTS === -// Base is 1193182 -#define TIMER_BASE 1193182 -#if DEBUG_VERY_SLOW_PERIOD -# define TIMER_DIVISOR 1193 //~10Hz switch, with 10 quantum = 1s per thread -#else -# define TIMER_DIVISOR 11932 //~100Hz -#endif - -// === TYPES === -typedef struct sCPU -{ - Uint8 APICID; - Uint8 State; // 0: Unavaliable, 1: Idle, 2: Active - Uint16 Resvd; - tThread *Current; -} tCPU; - -// === IMPORTS === -extern tGDT gGDT[]; -extern tIDT gIDT[]; -extern void APWait(void); // 16-bit AP pause code -extern void APStartup(void); // 16-bit AP startup code -extern Uint GetEIP(void); // start.asm -extern Uint GetEIP_Sched(void); // proc.asm -extern void NewTaskHeader(tThread *Thread, void *Fcn, int nArgs, ...); // Actually takes cdecl args -extern Uint Proc_CloneInt(Uint *ESP, Uint32 *CR3, int bNoUserClone); -extern Uint32 gaInitPageDir[1024]; // start.asm -extern char Kernel_Stack_Top[]; -extern int giNumCPUs; -extern int giNextTID; -extern tThread gThreadZero; -extern tProcess gProcessZero; -extern void Isr8(void); // Double Fault -extern void Proc_ReturnToUser(tVAddr Handler, Uint Argument, tVAddr KernelStack); -extern char scheduler_return[]; // Return address in SchedulerBase -extern char IRQCommon[]; // Common IRQ handler code -extern char IRQCommon_handled[]; // IRQCommon call return location -extern char GetEIP_Sched_ret[]; // GetEIP call return location -extern void SwitchTasks(Uint NewSP, Uint *OldSP, Uint NewIP, Uint *OldIO, Uint CR3); -extern void Proc_InitialiseSSE(void); -extern void Proc_SaveSSE(Uint DestPtr); -extern void Proc_DisableSSE(void); - -// === PROTOTYPES === -//void ArchThreads_Init(void); -#if USE_MP -void MP_StartAP(int CPU); -void MP_SendIPIVector(int CPU, Uint8 Vector); -void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode); -#endif -void Proc_IdleThread(void *Ptr); -//void Proc_Start(void); -//tThread *Proc_GetCurThread(void); -void Proc_ChangeStack(void); -// int Proc_NewKThread(void (*Fcn)(void*), void *Data); -// int Proc_Clone(Uint *Err, Uint Flags); -Uint Proc_MakeUserStack(void); -//void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize); -void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) NORETURN; - int Proc_Demote(Uint *Err, int Dest, tRegs *Regs); -//void Proc_CallFaultHandler(tThread *Thread); -//void Proc_DumpThreadCPUState(tThread *Thread); -void Proc_Scheduler(int CPU); - -// === GLOBALS === -// --- Multiprocessing --- -#if USE_MP -volatile int giNumInitingCPUs = 0; -tMPInfo *gMPFloatPtr = NULL; -volatile Uint32 giMP_TimerCount; // Start Count for Local APIC Timer -tAPIC *gpMP_LocalAPIC = NULL; -Uint8 gaAPIC_to_CPU[256] = {0}; - int giProc_BootProcessorID = 0; -tTSS gaTSSs[MAX_CPUS]; // TSS Array -#endif -tCPU gaCPUs[MAX_CPUS] = { - {.Current = &gThreadZero} - }; -tTSS *gTSSs = NULL; // Pointer to TSS array -tTSS gTSS0 = {0}; -// --- Error Recovery --- -char gaDoubleFaultStack[1024] __attribute__ ((section(".padata"))); -tTSS gDoubleFault_TSS = { - .ESP0 = (Uint)&gaDoubleFaultStack[1024], - .SS0 = 0x10, - .CR3 = (Uint)gaInitPageDir - KERNEL_BASE, - .EIP = (Uint)Isr8, - .ESP = (Uint)&gaDoubleFaultStack[1024], - .CS = 0x08, .SS = 0x10, - .DS = 0x10, .ES = 0x10, - .FS = 0x10, .GS = 0x10, -}; - -// === CODE === -/** - * \fn void ArchThreads_Init(void) - * \brief Starts the process scheduler - */ -void ArchThreads_Init(void) -{ - Uint pos = 0; - - #if USE_MP - tMPTable *mptable; - - // Mark BSP as active - gaCPUs[0].State = 2; - - // -- Initialise Multiprocessing - // Find MP Floating Table - // - EBDA/Last 1Kib (640KiB) - for(pos = KERNEL_BASE|0x9F000; pos < (KERNEL_BASE|0xA0000); pos += 16) { - if( *(Uint*)(pos) == MPPTR_IDENT ) { - Log("Possible %p", pos); - if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue; - gMPFloatPtr = (void*)pos; - break; - } - } - // - Last KiB (512KiB base mem) - if(!gMPFloatPtr) { - for(pos = KERNEL_BASE|0x7F000; pos < (KERNEL_BASE|0x80000); pos += 16) { - if( *(Uint*)(pos) == MPPTR_IDENT ) { - Log("Possible %p", pos); - if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue; - gMPFloatPtr = (void*)pos; - break; - } - } - } - // - BIOS ROM - if(!gMPFloatPtr) { - for(pos = KERNEL_BASE|0xE0000; pos < (KERNEL_BASE|0x100000); pos += 16) { - if( *(Uint*)(pos) == MPPTR_IDENT ) { - Log("Possible %p", pos); - if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue; - gMPFloatPtr = (void*)pos; - break; - } - } - } - - // If the MP Table Exists, parse it - if(gMPFloatPtr) - { - int i; - tMPTable_Ent *ents; - #if DUMP_MP_TABLE - Log("gMPFloatPtr = %p", gMPFloatPtr); - Log("*gMPFloatPtr = {"); - Log("\t.Sig = 0x%08x", gMPFloatPtr->Sig); - Log("\t.MPConfig = 0x%08x", gMPFloatPtr->MPConfig); - Log("\t.Length = 0x%02x", gMPFloatPtr->Length); - Log("\t.Version = 0x%02x", gMPFloatPtr->Version); - Log("\t.Checksum = 0x%02x", gMPFloatPtr->Checksum); - Log("\t.Features = [0x%02x,0x%02x,0x%02x,0x%02x,0x%02x]", - gMPFloatPtr->Features[0], gMPFloatPtr->Features[1], - gMPFloatPtr->Features[2], gMPFloatPtr->Features[3], - gMPFloatPtr->Features[4] - ); - Log("}"); - #endif - - mptable = (void*)( KERNEL_BASE|gMPFloatPtr->MPConfig ); - #if DUMP_MP_TABLE - Log("mptable = %p", mptable); - Log("*mptable = {"); - Log("\t.Sig = 0x%08x", mptable->Sig); - Log("\t.BaseTableLength = 0x%04x", mptable->BaseTableLength); - Log("\t.SpecRev = 0x%02x", mptable->SpecRev); - Log("\t.Checksum = 0x%02x", mptable->Checksum); - Log("\t.OEMID = '%8c'", mptable->OemID); - Log("\t.ProductID = '%8c'", mptable->ProductID); - Log("\t.OEMTablePtr = %p'", mptable->OEMTablePtr); - Log("\t.OEMTableSize = 0x%04x", mptable->OEMTableSize); - Log("\t.EntryCount = 0x%04x", mptable->EntryCount); - Log("\t.LocalAPICMemMap = 0x%08x", mptable->LocalAPICMemMap); - Log("\t.ExtendedTableLen = 0x%04x", mptable->ExtendedTableLen); - Log("\t.ExtendedTableChecksum = 0x%02x", mptable->ExtendedTableChecksum); - Log("}"); - #endif - - gpMP_LocalAPIC = (void*)MM_MapHWPages(mptable->LocalAPICMemMap, 1); - - ents = mptable->Entries; - giNumCPUs = 0; - - for( i = 0; i < mptable->EntryCount; i ++ ) - { - int entSize = 0; - switch( ents->Type ) - { - case 0: // Processor - entSize = 20; - #if DUMP_MP_TABLE - Log("%i: Processor", i); - Log("\t.APICID = %i", ents->Proc.APICID); - Log("\t.APICVer = 0x%02x", ents->Proc.APICVer); - Log("\t.CPUFlags = 0x%02x", ents->Proc.CPUFlags); - Log("\t.CPUSignature = 0x%08x", ents->Proc.CPUSignature); - Log("\t.FeatureFlags = 0x%08x", ents->Proc.FeatureFlags); - #endif - - if( !(ents->Proc.CPUFlags & 1) ) { - Log("DISABLED"); - break; - } - - // Check if there is too many processors - if(giNumCPUs >= MAX_CPUS) { - giNumCPUs ++; // If `giNumCPUs` > MAX_CPUS later, it will be clipped - break; - } - - // Initialise CPU Info - gaAPIC_to_CPU[ents->Proc.APICID] = giNumCPUs; - gaCPUs[giNumCPUs].APICID = ents->Proc.APICID; - gaCPUs[giNumCPUs].State = 0; - giNumCPUs ++; - - // Set BSP Variable - if( ents->Proc.CPUFlags & 2 ) { - giProc_BootProcessorID = giNumCPUs-1; - } - - break; - - #if DUMP_MP_TABLE >= 2 - case 1: // Bus - entSize = 8; - Log("%i: Bus", i); - Log("\t.ID = %i", ents->Bus.ID); - Log("\t.TypeString = '%6C'", ents->Bus.TypeString); - break; - case 2: // I/O APIC - entSize = 8; - Log("%i: I/O APIC", i); - Log("\t.ID = %i", ents->IOAPIC.ID); - Log("\t.Version = 0x%02x", ents->IOAPIC.Version); - Log("\t.Flags = 0x%02x", ents->IOAPIC.Flags); - Log("\t.Addr = 0x%08x", ents->IOAPIC.Addr); - break; - case 3: // I/O Interrupt Assignment - entSize = 8; - Log("%i: I/O Interrupt Assignment", i); - Log("\t.IntType = %i", ents->IOInt.IntType); - Log("\t.Flags = 0x%04x", ents->IOInt.Flags); - Log("\t.SourceBusID = 0x%02x", ents->IOInt.SourceBusID); - Log("\t.SourceBusIRQ = 0x%02x", ents->IOInt.SourceBusIRQ); - Log("\t.DestAPICID = 0x%02x", ents->IOInt.DestAPICID); - Log("\t.DestAPICIRQ = 0x%02x", ents->IOInt.DestAPICIRQ); - break; - case 4: // Local Interrupt Assignment - entSize = 8; - Log("%i: Local Interrupt Assignment", i); - Log("\t.IntType = %i", ents->LocalInt.IntType); - Log("\t.Flags = 0x%04x", ents->LocalInt.Flags); - Log("\t.SourceBusID = 0x%02x", ents->LocalInt.SourceBusID); - Log("\t.SourceBusIRQ = 0x%02x", ents->LocalInt.SourceBusIRQ); - Log("\t.DestLocalAPICID = 0x%02x", ents->LocalInt.DestLocalAPICID); - Log("\t.DestLocalAPICIRQ = 0x%02x", ents->LocalInt.DestLocalAPICIRQ); - break; - default: - Log("%i: Unknown (%i)", i, ents->Type); - break; - #endif - } - ents = (void*)( (Uint)ents + entSize ); - } - - if( giNumCPUs > MAX_CPUS ) { - Warning("Too many CPUs detected (%i), only using %i of them", giNumCPUs, MAX_CPUS); - giNumCPUs = MAX_CPUS; - } - gTSSs = gaTSSs; - } - else { - Log("No MP Table was found, assuming uniprocessor\n"); - giNumCPUs = 1; - gTSSs = &gTSS0; - } - #else - giNumCPUs = 1; - gTSSs = &gTSS0; - #endif - - #if !DEBUG_DISABLE_DOUBLEFAULT - // Initialise Double Fault TSS - gGDT[5].BaseLow = (Uint)&gDoubleFault_TSS & 0xFFFF; - gGDT[5].BaseMid = (Uint)&gDoubleFault_TSS >> 16; - gGDT[5].BaseHi = (Uint)&gDoubleFault_TSS >> 24; - - // Set double fault IDT to use the new TSS - gIDT[8].OffsetLo = 0; - gIDT[8].CS = 5<<3; - gIDT[8].Flags = 0x8500; - gIDT[8].OffsetHi = 0; - #endif - - // Set timer frequency - outb(0x43, 0x34); // Set Channel 0, Low/High, Rate Generator - outb(0x40, TIMER_DIVISOR&0xFF); // Low Byte of Divisor - outb(0x40, (TIMER_DIVISOR>>8)&0xFF); // High Byte - - Log_Debug("Proc", "PIT Frequency %i.%03i Hz", - TIMER_BASE/TIMER_DIVISOR, - ((Uint64)TIMER_BASE*1000/TIMER_DIVISOR)%1000 - ); - - #if USE_MP - // Get the count setting for APIC timer - Log("Determining APIC Count"); - __asm__ __volatile__ ("sti"); - while( giMP_TimerCount == 0 ) __asm__ __volatile__ ("hlt"); - __asm__ __volatile__ ("cli"); - Log("APIC Count %i", giMP_TimerCount); - { - Uint64 freq = giMP_TimerCount; - freq *= TIMER_BASE; - freq /= TIMER_DIVISOR; - if( (freq /= 1000) < 2*1000) - Log("Bus Frequency %i KHz", freq); - else if( (freq /= 1000) < 2*1000) - Log("Bus Frequency %i MHz", freq); - else if( (freq /= 1000) < 2*1000) - Log("Bus Frequency %i GHz", freq); - else - Log("Bus Frequency %i THz", freq); - } - - // Initialise Normal TSS(s) - for(pos=0;pos> 16) & 0xFFFF; - gGDT[6+pos].BaseHi = ((Uint)(&gTSSs[pos])) >> 24; - #if USE_MP - } - #endif - - // Load the BSP's TSS - __asm__ __volatile__ ("ltr %%ax"::"a"(0x30)); - // Set Current Thread and CPU Number in DR0 and DR1 - __asm__ __volatile__ ("mov %0, %%db0"::"r"(&gThreadZero)); - __asm__ __volatile__ ("mov %0, %%db1"::"r"(0)); - - gaCPUs[0].Current = &gThreadZero; - gThreadZero.CurCPU = 0; - - gProcessZero.MemState.CR3 = (Uint)gaInitPageDir - KERNEL_BASE; - - // Create Per-Process Data Block - if( !MM_Allocate(MM_PPD_CFG) ) - { - Panic("OOM - No space for initial Per-Process Config"); - } - - // Initialise SSE support - Proc_InitialiseSSE(); - - // Change Stacks - Proc_ChangeStack(); -} - -#if USE_MP -/** - * \brief Start an AP - */ -void MP_StartAP(int CPU) -{ - Log_Log("Proc", "Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID); - - // Set location of AP startup code and mark for a warm restart - *(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APWait - (KERNEL_BASE|0xFFFF0); - *(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF; - outb(0x70, 0x0F); outb(0x71, 0x0A); // Set warm reset flag - MP_SendIPI(gaCPUs[CPU].APICID, 0, 5); // Init IPI - - // Delay - inb(0x80); inb(0x80); inb(0x80); inb(0x80); - - // TODO: Use a better address, preferably registered with the MM - // - MM_AllocDMA mabye? - // Create a far jump - *(Uint8*)(KERNEL_BASE|0x11000) = 0xEA; // Far JMP - *(Uint16*)(KERNEL_BASE|0x11001) = (Uint)&APStartup - (KERNEL_BASE|0xFFFF0); // IP - *(Uint16*)(KERNEL_BASE|0x11003) = 0xFFFF; // CS - // Send a Startup-IPI to make the CPU execute at 0x11000 (which we - // just filled) - MP_SendIPI(gaCPUs[CPU].APICID, 0x11, 6); // StartupIPI - - giNumInitingCPUs ++; -} - -void MP_SendIPIVector(int CPU, Uint8 Vector) -{ - MP_SendIPI(gaCPUs[CPU].APICID, Vector, 0); -} - -/** - * \brief Send an Inter-Processor Interrupt - * \param APICID Processor's Local APIC ID - * \param Vector Argument of some kind - * \param DeliveryMode Type of signal - * \note 3A 10.5 "APIC/Handling Local Interrupts" - */ -void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode) -{ - Uint32 val; - - // Hi - val = (Uint)APICID << 24; -// Log("%p = 0x%08x", &gpMP_LocalAPIC->ICR[1], val); - gpMP_LocalAPIC->ICR[1].Val = val; - // Low (and send) - val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF); -// Log("%p = 0x%08x", &gpMP_LocalAPIC->ICR[0], val); - gpMP_LocalAPIC->ICR[0].Val = val; -} -#endif - -void Proc_IdleThread(void *Ptr) -{ - tCPU *cpu = &gaCPUs[GetCPUNum()]; - cpu->Current->ThreadName = strdup("Idle Thread"); - Threads_SetPriority( cpu->Current, -1 ); // Never called randomly - cpu->Current->Quantum = 1; // 1 slice quantum - for(;;) { - __asm__ __volatile__ ("sti"); // Make sure interrupts are enabled - __asm__ __volatile__ ("hlt"); // Make sure interrupts are enabled - Proc_Reschedule(); - } -} - -/** - * \fn void Proc_Start(void) - * \brief Start process scheduler - */ -void Proc_Start(void) -{ - int tid; - #if USE_MP - int i; - #endif - - #if USE_MP - // Start APs - for( i = 0; i < giNumCPUs; i ++ ) - { - if(i) gaCPUs[i].Current = NULL; - - // Create Idle Task - tid = Proc_NewKThread(Proc_IdleThread, &gaCPUs[i]); - - // Start the AP - if( i != giProc_BootProcessorID ) { - MP_StartAP( i ); - } - } - - // BSP still should run the current task - gaCPUs[0].Current = &gThreadZero; - - // Start interrupts and wait for APs to come up - Log_Debug("Proc", "Waiting for APs to come up"); - __asm__ __volatile__ ("sti"); - while( giNumInitingCPUs ) __asm__ __volatile__ ("hlt"); - #else - // Create Idle Task - tid = Proc_NewKThread(Proc_IdleThread, &gaCPUs[0]); -// gaCPUs[0].IdleThread = Threads_GetThread(tid); - - // Set current task - gaCPUs[0].Current = &gThreadZero; - - // Start Interrupts (and hence scheduler) - __asm__ __volatile__("sti"); - #endif - MM_FinishVirtualInit(); -} - -/** - * \fn tThread *Proc_GetCurThread(void) - * \brief Gets the current thread - */ -tThread *Proc_GetCurThread(void) -{ - #if USE_MP - return gaCPUs[ GetCPUNum() ].Current; - #else - return gaCPUs[ 0 ].Current; - #endif -} - -/** - * \fn void Proc_ChangeStack(void) - * \brief Swaps the current stack for a new one (in the proper stack reigon) - */ -void Proc_ChangeStack(void) -{ - Uint esp, ebp; - Uint tmpEbp, oldEsp; - Uint curBase, newBase; - - __asm__ __volatile__ ("mov %%esp, %0":"=r"(esp)); - __asm__ __volatile__ ("mov %%ebp, %0":"=r"(ebp)); - - oldEsp = esp; - - // Create new KStack - newBase = MM_NewKStack(); - // Check for errors - if(newBase == 0) { - Panic("What the?? Unable to allocate space for initial kernel stack"); - return; - } - - curBase = (Uint)&Kernel_Stack_Top; - - LOG("curBase = 0x%x, newBase = 0x%x", curBase, newBase); - - // Get ESP as a used size - esp = curBase - esp; - LOG("memcpy( %p, %p, 0x%x )", (void*)(newBase - esp), (void*)(curBase - esp), esp ); - // Copy used stack - memcpy( (void*)(newBase - esp), (void*)(curBase - esp), esp ); - // Get ESP as an offset in the new stack - esp = newBase - esp; - // Adjust EBP - ebp = newBase - (curBase - ebp); - - // Repair EBPs & Stack Addresses - // Catches arguments also, but may trash stack-address-like values - for(tmpEbp = esp; tmpEbp < newBase; tmpEbp += 4) - { - if(oldEsp < *(Uint*)tmpEbp && *(Uint*)tmpEbp < curBase) - *(Uint*)tmpEbp += newBase - curBase; - } - - Proc_GetCurThread()->KernelStack = newBase; - - __asm__ __volatile__ ("mov %0, %%esp"::"r"(esp)); - __asm__ __volatile__ ("mov %0, %%ebp"::"r"(ebp)); -} - -void Proc_ClearProcess(tProcess *Process) -{ - MM_ClearSpace(Process->MemState.CR3); -} - -void Proc_ClearThread(tThread *Thread) -{ - if(Thread->SavedState.SSE) { - free(Thread->SavedState.SSE); - Thread->SavedState.SSE = NULL; - } -} - -tTID Proc_NewKThread(void (*Fcn)(void*), void *Data) -{ - Uint esp; - tThread *newThread, *cur; - - cur = Proc_GetCurThread(); - newThread = Threads_CloneTCB(0); - if(!newThread) return -1; - - // Create new KStack - newThread->KernelStack = MM_NewKStack(); - // Check for errors - if(newThread->KernelStack == 0) { - free(newThread); - return -1; - } - - esp = newThread->KernelStack; - *(Uint*)(esp-=4) = (Uint)Data; // Data (shadowed) - *(Uint*)(esp-=4) = 1; // Number of params - *(Uint*)(esp-=4) = (Uint)Fcn; // Function to call - *(Uint*)(esp-=4) = (Uint)newThread; // Thread ID - - newThread->SavedState.ESP = esp; - newThread->SavedState.EIP = (Uint)&NewTaskHeader; - newThread->SavedState.SSE = NULL; -// Log("New (KThread) %p, esp = %p", newThread->SavedState.EIP, newThread->SavedState.ESP); - -// MAGIC_BREAK(); - Threads_AddActive(newThread); - - return newThread->TID; -} - -/** - * \fn int Proc_Clone(Uint *Err, Uint Flags) - * \brief Clone the current process - */ -tPID Proc_Clone(Uint Flags) -{ - tThread *newThread; - tThread *cur = Proc_GetCurThread(); - Uint eip; - - // Sanity, please - if( !(Flags & CLONE_VM) ) { - Log_Error("Proc", "Proc_Clone: Don't leave CLONE_VM unset, use Proc_NewKThread instead"); - return -1; - } - - // New thread - newThread = Threads_CloneTCB(Flags); - if(!newThread) return -1; - - newThread->KernelStack = cur->KernelStack; - - // Clone state - eip = Proc_CloneInt(&newThread->SavedState.ESP, &newThread->Process->MemState.CR3, Flags & CLONE_NOUSER); - if( eip == 0 ) { - return 0; - } - newThread->SavedState.EIP = eip; - newThread->SavedState.SSE = NULL; - newThread->SavedState.bSSEModified = 0; - - // Check for errors - if( newThread->Process->MemState.CR3 == 0 ) { - Log_Error("Proc", "Proc_Clone: MM_Clone failed"); - Threads_Delete(newThread); - return -1; - } - - // Add the new thread to the run queue - Threads_AddActive(newThread); - return newThread->TID; -} - -/** - * \fn int Proc_SpawnWorker(void) - * \brief Spawns a new worker thread - */ -int Proc_SpawnWorker(void (*Fcn)(void*), void *Data) -{ - tThread *new; - Uint stack_contents[4]; - - // Create new thread - new = Threads_CloneThreadZero(); - if(!new) { - Warning("Proc_SpawnWorker - Out of heap space!\n"); - return -1; - } - - // Create the stack contents - stack_contents[3] = (Uint)Data; - stack_contents[2] = 1; - stack_contents[1] = (Uint)Fcn; - stack_contents[0] = (Uint)new; - - // Create a new worker stack (in PID0's address space) - new->KernelStack = MM_NewWorkerStack(stack_contents, sizeof(stack_contents)); - - // Save core machine state - new->SavedState.ESP = new->KernelStack - sizeof(stack_contents); - new->SavedState.EIP = (Uint)NewTaskHeader; - new->SavedState.SSE = NULL; - new->SavedState.bSSEModified = 0; - - // Mark as active - new->Status = THREAD_STAT_PREINIT; - Threads_AddActive( new ); - - return new->TID; -} - -/** - * \fn Uint Proc_MakeUserStack(void) - * \brief Creates a new user stack - */ -Uint Proc_MakeUserStack(void) -{ - int i; - Uint base = USER_STACK_TOP - USER_STACK_SZ; - - // Check Prospective Space - for( i = USER_STACK_SZ >> 12; i--; ) - if( MM_GetPhysAddr( base + (i<<12) ) != 0 ) - break; - - if(i != -1) return 0; - - // Allocate Stack - Allocate incrementally to clean up MM_Dump output - for( i = 0; i < USER_STACK_SZ/0x1000; i++ ) - { - if( !MM_Allocate( base + (i<<12) ) ) - { - Warning("OOM: Proc_MakeUserStack"); - return 0; - } - } - - return base + USER_STACK_SZ; -} - -void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) -{ - Uint *stack; - int i; - const char **envp = NULL; - Uint16 ss, cs; - - // Copy data to the user stack and free original buffer - stack = (void*)Proc_MakeUserStack(); - stack -= (DataSize+sizeof(*stack)-1)/sizeof(*stack); - memcpy( stack, ArgV, DataSize ); - free(ArgV); - - // Adjust Arguments and environment - if( DataSize ) - { - Uint delta = (Uint)stack - (Uint)ArgV; - ArgV = (const char**)stack; - for( i = 0; ArgV[i]; i++ ) ArgV[i] += delta; - envp = &ArgV[i+1]; - for( i = 0; envp[i]; i++ ) envp[i] += delta; - } - - // User Mode Segments - ss = 0x23; cs = 0x1B; - - // Arguments - *--stack = (Uint)envp; - *--stack = (Uint)ArgV; - *--stack = (Uint)ArgC; - *--stack = Base; - - Proc_StartProcess(ss, (Uint)stack, 0x202, cs, Entrypoint); -} - -void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) -{ - Uint *stack = (void*)Stack; - *--stack = SS; //Stack Segment - *--stack = Stack; //Stack Pointer - *--stack = Flags; //EFLAGS (Resvd (0x2) and IF (0x20)) - *--stack = CS; //Code Segment - *--stack = IP; //EIP - //PUSHAD - *--stack = 0xAAAAAAAA; // eax - *--stack = 0xCCCCCCCC; // ecx - *--stack = 0xDDDDDDDD; // edx - *--stack = 0xBBBBBBBB; // ebx - *--stack = 0xD1D1D1D1; // edi - *--stack = 0x54545454; // esp - NOT POPED - *--stack = 0x51515151; // esi - *--stack = 0xB4B4B4B4; // ebp - //Individual PUSHs - *--stack = SS; // ds - *--stack = SS; // es - *--stack = SS; // fs - *--stack = SS; // gs - - __asm__ __volatile__ ( - "mov %%eax,%%esp;\n\t" // Set stack pointer - "pop %%gs;\n\t" - "pop %%fs;\n\t" - "pop %%es;\n\t" - "pop %%ds;\n\t" - "popa;\n\t" - "iret;\n\t" : : "a" (stack)); - for(;;); -} - -/** - * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs) - * \brief Demotes a process to a lower permission level - * \param Err Pointer to user's errno - * \param Dest New Permission Level - * \param Regs Pointer to user's register structure - */ -int Proc_Demote(Uint *Err, int Dest, tRegs *Regs) -{ - int cpl = Regs->cs & 3; - // Sanity Check - if(Dest > 3 || Dest < 0) { - *Err = -EINVAL; - return -1; - } - - // Permission Check - if(cpl > Dest) { - *Err = -EACCES; - return -1; - } - - // Change the Segment Registers - Regs->cs = (((Dest+1)<<4) | Dest) - 8; - Regs->ss = ((Dest+1)<<4) | Dest; - // Check if the GP Segs are GDT, then change them - if(!(Regs->ds & 4)) Regs->ds = ((Dest+1)<<4) | Dest; - if(!(Regs->es & 4)) Regs->es = ((Dest+1)<<4) | Dest; - if(!(Regs->fs & 4)) Regs->fs = ((Dest+1)<<4) | Dest; - if(!(Regs->gs & 4)) Regs->gs = ((Dest+1)<<4) | Dest; - - return 0; -} - -/** - * \brief Calls a signal handler in user mode - * \note Used for signals - */ -void Proc_CallFaultHandler(tThread *Thread) -{ - // Rewinds the stack and calls the user function - // Never returns - Proc_ReturnToUser( Thread->FaultHandler, Thread->CurFaultNum, Thread->KernelStack ); - for(;;); -} - -void Proc_DumpThreadCPUState(tThread *Thread) -{ - if( Thread->CurCPU > -1 ) - { - int maxBacktraceDistance = 6; - tRegs *regs = NULL; - Uint32 *stack; - - if( Thread->CurCPU != GetCPUNum() ) { - Log(" Currently running"); - return ; - } - - // Backtrace to find the IRQ entrypoint - // - This will usually only be called by an IRQ, so this should - // work - __asm__ __volatile__ ("mov %%ebp, %0" : "=r" (stack)); - while( maxBacktraceDistance -- ) - { - // [ebp] = oldEbp - // [ebp+4] = retaddr - - if( stack[1] == (tVAddr)&IRQCommon_handled ) { - regs = (void*)stack[2]; - break; - } - - stack = (void*)stack[0]; - } - - if( !regs ) { - Log(" Unable to find IRQ Entry"); - return ; - } - - Log(" at %04x:%08x", regs->cs, regs->eip); - return ; - } - - tVAddr diffFromScheduler = Thread->SavedState.EIP - (tVAddr)SwitchTasks; - tVAddr diffFromClone = Thread->SavedState.EIP - (tVAddr)Proc_CloneInt; - tVAddr diffFromSpawn = Thread->SavedState.EIP - (tVAddr)NewTaskHeader; - - if( diffFromClone > 0 && diffFromClone < 40 ) // When I last checked, .newTask was at .+27 - { - Log(" Creating process"); - return ; - } - - if( diffFromSpawn == 0 ) - { - Log(" Creating thread"); - return ; - } - - if( diffFromScheduler > 0 && diffFromScheduler < 128 ) // When I last checked, GetEIP was at .+0x30 - { - // Scheduled out - Log(" At %04x:%08x", Thread->SavedState.UserCS, Thread->SavedState.UserEIP); - return ; - } - - Log(" Just created (unknown %p)", Thread->SavedState.EIP); -} - -void Proc_Reschedule(void) -{ - tThread *nextthread, *curthread; - int cpu = GetCPUNum(); - - // TODO: Wait for the lock? - if(IS_LOCKED(&glThreadListLock)) return; - - curthread = Proc_GetCurThread(); - - nextthread = Threads_GetNextToRun(cpu, curthread); - - if(!nextthread || nextthread == curthread) - return ; - - #if DEBUG_TRACE_SWITCH - // HACK: Ignores switches to the idle threads - if( nextthread->TID == 0 || nextthread->TID > giNumCPUs ) - { - LogF("\nSwitching CPU %i to %p (%i %s) - CR3 = 0x%x, EIP = %p, ESP = %p\n", - GetCPUNum(), - nextthread, nextthread->TID, nextthread->ThreadName, - nextthread->Process->MemState.CR3, - nextthread->SavedState.EIP, - nextthread->SavedState.ESP - ); - LogF("OldCR3 = %P\n", curthread->Process->MemState.CR3); - } - #endif - - // Update CPU state - gaCPUs[cpu].Current = nextthread; - gTSSs[cpu].ESP0 = nextthread->KernelStack-4; - __asm__ __volatile__("mov %0, %%db0\n\t" : : "r"(nextthread) ); - - // Save FPU/MMX/XMM/SSE state - if( curthread && curthread->SavedState.SSE ) - { - Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF ); - curthread->SavedState.bSSEModified = 0; - Proc_DisableSSE(); - } - - if( curthread ) - { - SwitchTasks( - nextthread->SavedState.ESP, &curthread->SavedState.ESP, - nextthread->SavedState.EIP, &curthread->SavedState.EIP, - nextthread->Process->MemState.CR3 - ); - } - else - { - SwitchTasks( - nextthread->SavedState.ESP, 0, - nextthread->SavedState.EIP, 0, - nextthread->Process->MemState.CR3 - ); - } - - return ; -} - -/** - * \fn void Proc_Scheduler(int CPU) - * \brief Swap current thread and clears dead threads - */ -void Proc_Scheduler(int CPU) -{ -#if 0 - tThread *thread; - - // If the spinlock is set, let it complete - if(IS_LOCKED(&glThreadListLock)) return; - - // Get current thread - thread = gaCPUs[CPU].Current; - - if( thread ) - { - tRegs *regs; - Uint ebp; - // Reduce remaining quantum and continue timeslice if non-zero - if( thread->Remaining-- ) - return; - // Reset quantum for next call - thread->Remaining = thread->Quantum; - - // TODO: Make this more stable somehow - __asm__ __volatile__("mov %%ebp, %0" : "=r" (ebp)); - regs = (tRegs*)(ebp+(2+2)*4); // EBP,Ret + CPU,CurThread - thread->SavedState.UserCS = regs->cs; - thread->SavedState.UserEIP = regs->eip; - - if(thread->bInstrTrace) { - regs->eflags |= 0x100; // Set TF - Log("%p De-scheduled", thread); - } - else - regs->eflags &= ~0x100; // Clear TF - } - - // TODO: Ack timer? - #if USE_MP - if( GetCPUNum() ) - gpMP_LocalAPIC->EOI.Val = 0; - else - #endif - outb(0x20, 0x20); - __asm__ __volatile__ ("sti"); - Proc_Reschedule(); -#endif -} - -// === EXPORTS === -EXPORT(Proc_SpawnWorker); diff --git a/Kernel/arch/x86/start.asm b/Kernel/arch/x86/start.asm deleted file mode 100644 index b6026de6..00000000 --- a/Kernel/arch/x86/start.asm +++ /dev/null @@ -1,285 +0,0 @@ -; AcessOS Microkernel Version -; Start.asm - -[bits 32] - -KERNEL_BASE equ 0xC0000000 -%define MAX_CPUS 16 - -[extern __load_addr] -[extern __bss_start] -[extern gKernelEnd] -[section .multiboot] -mboot: - ; Multiboot macros to make a few lines later more readable - MULTIBOOT_PAGE_ALIGN equ 1<<0 - MULTIBOOT_MEMORY_INFO equ 1<<1 - MULTIBOOT_HEADER_MAGIC equ 0x1BADB002 - MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO - MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) - - ; This is the GRUB Multiboot header. A boot signature - dd MULTIBOOT_HEADER_MAGIC - dd MULTIBOOT_HEADER_FLAGS - dd MULTIBOOT_CHECKSUM - dd mboot; - KERNEL_BASE ;Location of Multiboot Header - -; Multiboot 2 Header -;mboot2: -; MULTIBOOT2_HEADER_MAGIC equ 0xE85250D6 -; MULTIBOOT2_HEADER_ARCH equ 0 -; MULTIBOOT2_HEADER_LENGTH equ (mboot2_end-mboot2) -; MULTIBOOT2_CHECKSUM equ -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT2_HEADER_ARCH + MULTIBOOT2_HEADER_LENGTH) -; -; dd MULTIBOOT2_HEADER_MAGIC -; dd MULTIBOOT2_HEADER_ARCH -; dd MULTIBOOT2_HEADER_LENGTH -; dd MULTIBOOT2_CHECKSUM -; ; MBoot2 Address Header -; dw 2, 0 -; dd 8 + 16 -; dd mboot2 ; Location of Multiboot Header -; dd __load_addr - KERNEL_BASE ; Kernel Load base -; dd __bss_start - KERNEL_BASE ; Kernel Data End -; dd gKernelEnd - KERNEL_BASE ; Kernel BSS End -; ; MBoot2 Entry Point Tag -; dw 3, 0 -; dd 8 + 4 -; dd start - KERNEL_BASE -; ; MBoot2 Module Alignment Tag -; dw 6, 0 -; dd 12 ; ??? -; dd 0 ; Search me, seems it wants padding -; ; Terminator -; dw 0, 0 -; dd 8 -;mboot2_end: - -[section .text] -[extern kmain] -[global start] -start: - ; Just show we're here - mov WORD [0xB8000], 0x0741 ; 'A' - - ; Set up stack - mov esp, Kernel_Stack_Top - - ; Start Paging - mov ecx, gaInitPageDir - KERNEL_BASE - mov cr3, ecx - mov ecx, cr0 - or ecx, 0x80010000 ; PG and WP - mov cr0, ecx - - mov WORD [0xB8002], 0x0763 ; 'c' - - ; Set GDT - lgdt [gGDTPtr] - mov cx, 0x10 ; PL0 Data - mov ss, cx - mov ds, cx - mov es, cx - mov gs, cx - mov fs, cx - mov WORD [0xB8004], 0x0765 ; 'e' - jmp 0x08:.higher_half -.higher_half: - - mov WORD [0xB8006], 0x0773 ; 's' - mov WORD [0xB8008], 0x0773 ; 's' - - ; Call the kernel - push ebx ; Multiboot Info - push eax ; Multiboot Magic Value - mov WORD [0xB800A], 0x0732 ; '2' - call kmain - - ; Halt the Machine - cli -.hlt: - hlt - jmp .hlt - -; -; Multiprocessing AP Startup Code (Must be within 0 - 0x10FFF0) -; -%if USE_MP -[extern gIDTPtr] -[extern gpMP_LocalAPIC] -[extern giMP_TimerCount] -[extern gaAPIC_to_CPU] -[extern gaCPUs] -[extern giNumInitingCPUs] -[extern MM_NewKStack] -[extern Proc_InitialiseSSE] - -lGDTPtr: ; Local GDT Pointer - dw 3*8-1 - dd gGDT-KERNEL_BASE - -[bits 16] -[global APWait] -APWait: - ;xchg bx, bx -.hlt: - ;hlt - jmp .hlt -[extern Proc_Reschedule] -[global APStartup] -APStartup: - ;xchg bx, bx ; MAGIC BREAK! - ; Load initial GDT - mov ax, 0xFFFF - mov ds, ax - lgdt [DWORD ds:lGDTPtr-KERNEL_BASE-0xFFFF0] - ; Enable PMode in CR0 - mov eax, cr0 - or al, 1 - mov cr0, eax - ; Jump into PMode - jmp 08h:DWORD .ProtectedMode-KERNEL_BASE -[bits 32] -.ProtectedMode: - ; Load segment registers - mov ax, 0x10 - mov ss, ax - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - ; Start Paging - mov eax, gaInitPageDir - KERNEL_BASE - mov cr3, eax - mov eax, cr0 - or eax, 0x80010000 ; PG and WP - mov cr0, eax - ; Jump to higher half - lea eax, [.higherHalf] - jmp eax -.higherHalf: - ; Load True GDT & IDT - lgdt [gGDTPtr] - lidt [gIDTPtr] - - mov ebp, [gpMP_LocalAPIC] - mov ebx, [ebp+0x20] ; Read ID - shr ebx, 24 - ;xchg bx, bx ; MAGIC BREAK - ; BL is now local APIC ID - mov cl, BYTE [gaAPIC_to_CPU+ebx] - xor ebx, ebx - mov bl, cl - ; BL is now the CPU ID - mov dr1, ebx ; Save the CPU number to a debug register - ; Mark the CPU as up - mov BYTE [gaCPUs+ebx*8+1], 1 - ; Decrement the remaining CPU count - dec DWORD [giNumInitingCPUs] - - ; Create a stack - lea edx, [ebx+1] - shl edx, 5+2 ; *32 *4 - lea esp, [gInitAPStacks+edx] - call MM_NewKStack - mov esp, eax - - ; Set TSS - lea ecx, [ebx*8+0x30] - ltr cx - - ;xchg bx, bx ; MAGIC_BREAK - ; Enable Local APIC - mov DWORD [ebp+0x0F0], 0x000001EF ; Spurious Interrupt Vector (0xEF) - mov ecx, DWORD [giMP_TimerCount] - mov DWORD [ebp+0x380], ecx ; Set Initial Count - mov DWORD [ebp+0x320], 0x000200EE ; Enable timer on IVT#0xEE - mov DWORD [ebp+0x330], 0x000100E0 ; ##Enable thermal sensor on IVT#0xE0 - mov DWORD [ebp+0x340], 0x000100D0 ; ##Enable performance counters on IVT#0xD0 - mov DWORD [ebp+0x350], 0x000100D1 ; ##Enable LINT0 on IVT#0xD1 - mov DWORD [ebp+0x360], 0x000100D2 ; ##Enable LINT1 on IVT#0xD2 - mov DWORD [ebp+0x370], 0x000100E1 ; ##Enable Error on IVT#0xE1 - mov DWORD [ebp+0x0B0], 0 ; Send an EOI (just in case) - - ; Initialise SSE support - call Proc_InitialiseSSE - - ; CPU is now marked as initialised - -.hlt: - sti - call Proc_Reschedule - hlt - jmp .hlt -%endif - -[global GetEIP] -GetEIP: - mov eax, [esp] - ret - -; int CallWithArgArray(void *Ptr, int NArgs, Uint *Args) -; Call a function passing the array as arguments -[global CallWithArgArray] -CallWithArgArray: - push ebp - mov ebp, esp - mov ecx, [ebp+12] ; Get NArgs - mov edx, [ebp+16] - -.top: - mov eax, [edx+ecx*4-4] - push eax - loop .top - - mov eax, [ebp+8] - call eax - lea esp, [ebp] - pop ebp - ret - -[section .data] -; GDT -GDT_SIZE equ (1+2*2+1+MAX_CPUS)*8 -[global gGDT] -gGDT: - ; PL0 - Kernel - ; PL3 - User - dd 0x00000000, 0x00000000 ; 00 NULL Entry - dd 0x0000FFFF, 0x00CF9A00 ; 08 PL0 Code - dd 0x0000FFFF, 0x00CF9200 ; 10 PL0 Data - dd 0x0000FFFF, 0x00CFFA00 ; 18 PL3 Code - dd 0x0000FFFF, 0x00CFF200 ; 20 PL3 Data - dd 26*4-1, 0x00408900 ; 28 Double Fault TSS - times MAX_CPUS dd 26*4-1, 0x00408900 ; 30+ TSSes -[global gGDTPtr] -gGDTPtr: - dw GDT_SIZE-1 - dd gGDT - -[section .initpd] -[global gaInitPageDir] -[global gaInitPageTable] -align 4096 -gaInitPageDir: - dd gaInitPageTable-KERNEL_BASE+3 ; 0x000 - Low kernel - times 0x300-0x000-1 dd 0 - dd gaInitPageTable-KERNEL_BASE+3 ; 0xC00 - High kernel - times 0x3F0-0x300-1 dd 0 - dd gaInitPageDir-KERNEL_BASE+3 ; 0xFC0 - Fractal - times 0x400-0x3F0-1 dd 0 -align 4096 -gaInitPageTable: - %assign i 0 - %rep 1024 - dd i*0x1000+3 - %assign i i+1 - %endrep -[global Kernel_Stack_Top] -ALIGN 4096 - times 1024 dd 0 -Kernel_Stack_Top: -gInitAPStacks: - times 32*MAX_CPUS dd 0 - -; vim: ft=nasm ts=8 diff --git a/Kernel/arch/x86/time.c b/Kernel/arch/x86/time.c deleted file mode 100644 index 9b59b30b..00000000 --- a/Kernel/arch/x86/time.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Acess2 Kernel - * Timekeeping - * arch/x86/time.c - */ -#include - -// === MACROS === -#define TIMER_QUANTUM 100 -// 2^(15-rate), 14: 2Hz, 5: 1024Hz, 2: 8192Hz -// (Max: 14, Min: 2) - 14 = 2Hz, 13 = 4Hz, 12 = 8Hz, 11 = 16Hz 10 = 32Hz, 2 = 8192Hz -//#define TIMER_RATE 10 // 32 Hz -//#define TIMER_RATE 12 // 8 Hz -#define TIMER_RATE 14 // 2 Hz - Lowest -#define TIMER_FREQ (0x8000>>TIMER_RATE) //Hz -#define MS_PER_TICK_WHOLE (1000/(TIMER_FREQ)) -#define MS_PER_TICK_FRACT ((0x80000000*(1000%TIMER_FREQ))/TIMER_FREQ) - -// === IMPORTS === -extern volatile Sint64 giTimestamp; -extern volatile Uint64 giTicks; -extern volatile Uint64 giPartMiliseconds; -extern void Timer_CallTimers(void); - -// === GLOBALS === -volatile Uint64 giTime_TSCAtLastTick = 0; -volatile Uint64 giTime_TSCPerTick = 0; - -// === PROTOTYPES === -//Sint64 now(void); - int Time_Setup(void); -void Time_Interrupt(int IRQ, void *Ptr); -Uint64 Time_ReadTSC(void); - -// === CODE === -/** - * \fn Sint64 now() - * \brief Return the current timestamp - */ -Sint64 now(void) -{ - Uint64 tsc = Time_ReadTSC(); - tsc -= giTime_TSCAtLastTick; - tsc *= MS_PER_TICK_WHOLE; - if( giTime_TSCPerTick ) { - tsc /= giTime_TSCPerTick; - } - else - tsc = 0; - return giTimestamp + tsc; -} - -/** - * \fn int Time_Setup(void) - * \brief Sets the system time from the Realtime-Clock - */ -int Time_Setup(void) -{ - Uint8 val; - - Log_Log("Timer", "RTC Timer firing at %iHz (%i divisor), %i.0x%08x", - TIMER_FREQ, TIMER_RATE, MS_PER_TICK_WHOLE, MS_PER_TICK_FRACT); - - outb(0x70, inb(0x70)&0x7F); // Disable NMIs - __asm__ __volatile__ ("cli"); // Disable normal interrupts - - // Set IRQ8 firing rate - outb(0x70, 0x0A); // Set the index to register A - val = inb(0x71); // Get the current value of register A - val &= 0xF0; - val |= TIMER_RATE+1; - outb(0x70, 0x0A); // Reset index to A - outb(0x71, val); // Update the timer rate - - // Enable IRQ8 - outb(0x70, 0x0B); // Set the index to register B - val = inb(0x71); // Read the current value of register B - outb(0x70, 0x0B); // Set the index again (a read will reset the index to register D) - outb(0x71, val | 0x40); // Write the previous value or'd with 0x40. This turns on bit 6 of register D - - __asm__ __volatile__ ("sti"); // Re-enable normal interrupts - outb(0x70, inb(0x70)|0x80); // Re-enable NMIs - - // Install IRQ Handler - IRQ_AddHandler(8, Time_Interrupt, NULL); - - // Make sure the RTC actually fires - outb(0x70, 0x0C); // Select register C - inb(0x71); // Just throw away contents. - - return 0; -} - -/** - * \brief Called on the timekeeping IRQ - */ -void Time_Interrupt(int IRQ, void *Ptr) -{ - Uint64 curTSC = Time_ReadTSC(); - - if( giTime_TSCAtLastTick ) - { - giTime_TSCPerTick = curTSC - giTime_TSCAtLastTick; - } - giTime_TSCAtLastTick = curTSC; - - giTicks ++; - giTimestamp += MS_PER_TICK_WHOLE; - giPartMiliseconds += MS_PER_TICK_FRACT; - if(giPartMiliseconds > 0x80000000) { - giTimestamp ++; - giPartMiliseconds -= 0x80000000; - } - - Timer_CallTimers(); - - // Make sure the RTC Fires again - outb(0x70, 0x0C); // Select register C - inb(0x71); // Just throw away contents. -} - -Uint64 Time_ReadTSC(void) -{ - Uint32 a, d; - __asm__ __volatile__ ("rdtsc" : "=a" (a), "=d" (d)); - return ((Uint64)d << 32) | a; -} diff --git a/Kernel/arch/x86/vm8086.c b/Kernel/arch/x86/vm8086.c deleted file mode 100644 index 61348622..00000000 --- a/Kernel/arch/x86/vm8086.c +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Acess2 VM8086 Driver - * - By John Hodge (thePowersGang) - */ -#define DEBUG 0 -#include -#include -#include -#include -#include - -// === CONSTANTS === -#define VM8086_MAGIC_CS 0xFFFF -#define VM8086_MAGIC_IP 0x0010 -#define VM8086_STACK_SEG 0x9F00 -#define VM8086_STACK_OFS 0x0AFE -enum eVM8086_Opcodes -{ - VM8086_OP_PUSHF = 0x9C, - VM8086_OP_POPF = 0x9D, - VM8086_OP_INT_I = 0xCD, - VM8086_OP_IRET = 0xCF, - VM8086_OP_IN_AD = 0xEC, - VM8086_OP_IN_ADX = 0xED, - VM8086_OP_OUT_AD = 0xEE, - VM8086_OP_OUT_ADX = 0xEF -}; -#define VM8086_PAGES_PER_INST 4 - -#define VM8086_BLOCKSIZE 128 -#define VM8086_BLOCKCOUNT ((0x9F000-0x10000)/VM8086_BLOCKSIZE) - -// === TYPES === -struct sVM8086_InternalData -{ - struct { - Uint32 Bitmap; // 32 sections = 128 byte blocks - tVAddr VirtBase; - tPAddr PhysAddr; - } AllocatedPages[VM8086_PAGES_PER_INST]; -}; - -// === PROTOTYPES === - int VM8086_Install(char **Arguments); -void VM8086_GPF(tRegs *Regs); -//tVM8086 *VM8086_Init(void); - -// === GLOBALS === -MODULE_DEFINE(0, 0x100, VM8086, VM8086_Install, NULL, NULL); -tMutex glVM8086_Process; -tSemaphore gVM8086_TaskComplete; -tSemaphore gVM8086_TasksToDo; -tPID gVM8086_WorkerPID; -tTID gVM8086_CallingThread; -tVM8086 volatile * volatile gpVM8086_State = (void*)-1; // Set to -1 to avoid race conditions -Uint32 gaVM8086_MemBitmap[VM8086_BLOCKCOUNT/32]; - -// === FUNCTIONS === -int VM8086_Install(char **Arguments) -{ - tPID pid; - - Semaphore_Init(&gVM8086_TasksToDo, 0, 10, "VM8086", "TasksToDo"); - - // Lock to avoid race conditions - Mutex_Acquire( &glVM8086_Process ); - - // Create BIOS Call process - pid = Proc_Clone(CLONE_VM); - Log_Debug("VM8086", "pid = %i", pid); - if(pid == -1) - { - Log_Error("VM8086", "Unable to clone kernel into VM8086 worker"); - return MODULE_ERR_MISC; - } - if(pid == 0) - { - Uint * volatile stacksetup; // Initialising Stack - Uint16 * volatile rmstack; // Real Mode Stack - int i; - - Log_Debug("VM8086", "Initialising worker"); - - // Set Image Name - Threads_SetName("VM8086"); - - // Map ROM Area - for(i=0xA0;i<0x100;i++) { - MM_Map( i * 0x1000, i * 0x1000 ); - } - MM_Map( 0, 0 ); // IVT / BDA - // Map (but allow allocation) of 0x1000 - 0x9F000 - // - So much hack, it isn't funny - for(i=1;i<0x9F;i++) { - MM_Map( i * 0x1000, i * 0x1000 ); - MM_DerefPhys( i * 0x1000 ); // Above - while(MM_GetRefCount(i*0x1000)) - MM_DerefPhys( i * 0x1000 ); // Phys setup - } - MM_Map( 0x9F000, 0x9F000 ); // Stack / EBDA - // System Stack / Stub - if( MM_Allocate( 0x100000 ) == 0 ) { - Log_Error("VM8086", "Unable to allocate memory for stack/stub"); - gVM8086_WorkerPID = 0; - Threads_Exit(0, 1); - } - - *(Uint8*)(0x100000) = VM8086_OP_IRET; - *(Uint8*)(0x100001) = 0x07; // POP ES - *(Uint8*)(0x100002) = 0x1F; // POP DS - *(Uint8*)(0x100003) = 0xCB; // RET FAR - - rmstack = (Uint16*)(VM8086_STACK_SEG*16 + VM8086_STACK_OFS); - rmstack--; *rmstack = 0xFFFF; //CS - rmstack--; *rmstack = 0x0010; //IP - - // Setup Stack - stacksetup = (Uint*)0x101000; - stacksetup--; *stacksetup = VM8086_STACK_SEG; // GS - stacksetup--; *stacksetup = VM8086_STACK_SEG; // FS - stacksetup--; *stacksetup = VM8086_STACK_SEG; // DS - stacksetup--; *stacksetup = VM8086_STACK_SEG; // ES - stacksetup--; *stacksetup = VM8086_STACK_SEG; // SS - stacksetup--; *stacksetup = VM8086_STACK_OFS-2; // SP - stacksetup--; *stacksetup = 0x20202; // FLAGS - stacksetup--; *stacksetup = 0xFFFF; // CS - stacksetup--; *stacksetup = 0x10; // IP - stacksetup--; *stacksetup = 0xAAAA; // AX - stacksetup--; *stacksetup = 0xCCCC; // CX - stacksetup--; *stacksetup = 0xDDDD; // DX - stacksetup--; *stacksetup = 0xBBBB; // BX - stacksetup--; *stacksetup = 0x5454; // SP - stacksetup--; *stacksetup = 0xB4B4; // BP - stacksetup--; *stacksetup = 0x5151; // SI - stacksetup--; *stacksetup = 0xD1D1; // DI - stacksetup--; *stacksetup = 0x20|3; // DS - Kernel - stacksetup--; *stacksetup = 0x20|3; // ES - Kernel - stacksetup--; *stacksetup = 0x20|3; // FS - stacksetup--; *stacksetup = 0x20|3; // GS - __asm__ __volatile__ ( - "mov %%eax,%%esp;\n\t" // Set stack pointer - "pop %%gs;\n\t" - "pop %%fs;\n\t" - "pop %%es;\n\t" - "pop %%ds;\n\t" - "popa;\n\t" - "iret;\n\t" : : "a" (stacksetup)); - for(;;); // Shouldn't be reached - } - - gVM8086_WorkerPID = pid; - - // It's released when the GPF fires - Mutex_Acquire( &glVM8086_Process ); - Mutex_Release( &glVM8086_Process ); - - // Worker killed itself - if( gVM8086_WorkerPID != pid ) { - return MODULE_ERR_MISC; - } - - return MODULE_ERR_OK; -} - -void VM8086_GPF(tRegs *Regs) -{ - Uint8 opcode; - -// Log_Log("VM8086", "GPF - %04x:%04x", Regs->cs, Regs->eip); - - if(Regs->eip == VM8086_MAGIC_IP && Regs->cs == VM8086_MAGIC_CS - && Threads_GetPID() == gVM8086_WorkerPID) - { - if( gpVM8086_State == (void*)-1 ) { - Log_Log("VM8086", "Worker thread ready and waiting"); - gpVM8086_State = NULL; - Mutex_Release( &glVM8086_Process ); // Release lock obtained in VM8086_Install - } -// Log_Log("VM8086", "gpVM8086_State = %p, gVM8086_CallingThread = %i", -// gpVM8086_State, gVM8086_CallingThread); - if( gpVM8086_State ) { - gpVM8086_State->AX = Regs->eax; gpVM8086_State->CX = Regs->ecx; - gpVM8086_State->DX = Regs->edx; gpVM8086_State->BX = Regs->ebx; - gpVM8086_State->BP = Regs->ebp; - gpVM8086_State->SI = Regs->esi; gpVM8086_State->DI = Regs->edi; - gpVM8086_State->DS = Regs->ds; gpVM8086_State->ES = Regs->es; - gpVM8086_State = NULL; - // Wake the caller - Semaphore_Signal(&gVM8086_TaskComplete, 1); - } - - //Log_Log("VM8086", "Waiting for something to do"); - __asm__ __volatile__ ("sti"); - Semaphore_Wait(&gVM8086_TasksToDo, 1); - - //Log_Log("VM8086", "We have a task (%p)", gpVM8086_State); - Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = VM8086_MAGIC_CS; - Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = VM8086_MAGIC_IP; - Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->CS; - Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->IP; - Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->DS; - Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->ES; - - // Set Registers - Regs->eip = 0x11; Regs->cs = 0xFFFF; - Regs->eax = gpVM8086_State->AX; Regs->ecx = gpVM8086_State->CX; - Regs->edx = gpVM8086_State->DX; Regs->ebx = gpVM8086_State->BX; - Regs->esi = gpVM8086_State->SI; Regs->edi = gpVM8086_State->DI; - Regs->ebp = gpVM8086_State->BP; - Regs->ds = 0x23; Regs->es = 0x23; - Regs->fs = 0x23; Regs->gs = 0x23; - return ; - } - - opcode = *(Uint8*)( (Regs->cs*16) + (Regs->eip) ); - Regs->eip ++; - switch(opcode) - { - case VM8086_OP_PUSHF: //PUSHF - Regs->esp -= 2; - *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->eflags & 0xFFFF; - #if TRACE_EMU - Log_Debug("VM8086", "Emulated PUSHF"); - #endif - break; - case VM8086_OP_POPF: //POPF - // Changing IF is not allowed - Regs->eflags &= 0xFFFF0202; - Regs->eflags |= *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ); - Regs->esp += 2; - #if TRACE_EMU - Log_Debug("VM8086", "Emulated POPF"); - #endif - break; - - case VM8086_OP_INT_I: //INT imm8 - { - int id; - id = *(Uint8*)( Regs->cs*16 +(Regs->eip&0xFFFF)); - Regs->eip ++; - - Regs->esp -= 2; *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->cs; - Regs->esp -= 2; *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->eip; - - Regs->cs = *(Uint16*)(4*id + 2); - Regs->eip = *(Uint16*)(4*id); - #if TRACE_EMU - Log_Debug("VM8086", "Emulated INT 0x%x", id); - #endif - } - break; - - case VM8086_OP_IRET: //IRET - Regs->eip = *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ); Regs->esp += 2; - Regs->cs = *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ); Regs->esp += 2; - #if TRACE_EMU - Log_Debug("VM8086", "IRET to %04x:%04x", Regs->cs, Regs->eip); - #endif - break; - - - case VM8086_OP_IN_AD: //IN AL, DX - Regs->eax &= 0xFFFFFF00; - Regs->eax |= inb(Regs->edx&0xFFFF); - #if TRACE_EMU - Log_Debug("VM8086", "Emulated IN AL, DX (Port 0x%x)\n", Regs->edx&0xFFFF); - #endif - break; - case VM8086_OP_IN_ADX: //IN AX, DX - Regs->eax &= 0xFFFF0000; - Regs->eax |= inw(Regs->edx&0xFFFF); - #if TRACE_EMU - Log_Debug("VM8086", "Emulated IN AX, DX (Port 0x%x)\n", Regs->edx&0xFFFF); - #endif - break; - - case VM8086_OP_OUT_AD: //OUT DX, AL - outb(Regs->edx&0xFFFF, Regs->eax&0xFF); - #if TRACE_EMU - Log_Debug("VM8086", "Emulated OUT DX, AL (*0x%04x = 0x%02x)\n", Regs->edx&0xFFFF, Regs->eax&0xFF); - #endif - break; - case VM8086_OP_OUT_ADX: //OUT DX, AX - outw(Regs->edx&0xFFFF, Regs->eax&0xFFFF); - #if TRACE_EMU - Log_Debug("VM8086", "Emulated OUT DX, AX (*0x%04x = 0x%04x)\n", Regs->edx&0xFFFF, Regs->eax&0xFFFF); - #endif - break; - - // TODO: Decide on allowing VM8086 Apps to enable/disable interrupts - case 0xFA: //CLI - break; - case 0xFB: //STI - break; - - case 0x66: - opcode = *(Uint8*)( (Regs->cs*16) + (Regs->eip&0xFFFF)); - switch( opcode ) - { - case VM8086_OP_IN_ADX: //IN AX, DX - Regs->eax = ind(Regs->edx&0xFFFF); - #if TRACE_EMU - Log_Debug("VM8086", "Emulated IN EAX, DX (Port 0x%x)\n", Regs->edx&0xFFFF); - #endif - break; - case VM8086_OP_OUT_ADX: //OUT DX, AX - outd(Regs->edx&0xFFFF, Regs->eax); - #if TRACE_EMU - Log_Debug("VM8086", "Emulated OUT DX, EAX (*0x%04x = 0x%08x)\n", Regs->edx&0xFFFF, Regs->eax); - #endif - break; - default: - Log_Error("VM8086", "Error - Unknown opcode 66 %02x caused a GPF at %04x:%04x", - Regs->cs, Regs->eip, - opcode - ); - // Force an end to the call - Regs->cs = VM8086_MAGIC_CS; - Regs->eip = VM8086_MAGIC_IP; - break; - } - break; - - default: - Log_Error("VM8086", "Error - Unknown opcode %02x caused a GPF at %04x:%04x", - opcode, Regs->cs, Regs->eip); - // Force an end to the call - Regs->cs = VM8086_MAGIC_CS; - Regs->eip = VM8086_MAGIC_IP; - break; - } -} - -/** - * \brief Create an instance of the VM8086 Emulator - */ -tVM8086 *VM8086_Init(void) -{ - tVM8086 *ret; - ret = calloc( 1, sizeof(tVM8086) + sizeof(struct sVM8086_InternalData) ); - ret->Internal = (void*)((tVAddr)ret + sizeof(tVM8086)); - return ret; -} - -void VM8086_Free(tVM8086 *State) -{ - int i; - for( i = VM8086_PAGES_PER_INST; i --; ) - MM_UnmapHWPages( State->Internal->AllocatedPages[i].VirtBase, 1); - free(State); -} - -void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset) -{ - int i, j, base = 0; - int nBlocks, rem; - - Size = (Size + 127) & ~127; - nBlocks = Size / 128; - - if(Size > 4096) return NULL; - - for( i = 0; i < VM8086_PAGES_PER_INST; i++ ) - { - if( State->Internal->AllocatedPages[i].VirtBase == 0 ) continue; - - - //Log_Debug("VM8086", "AllocatedPages[%i].Bitmap = 0b%b", i, State->Internal->AllocatedPages[i].Bitmap); - - rem = nBlocks; - base = 0; - // Scan the bitmap for a free block - for( j = 0; j < 32; j++ ) { - if( State->Internal->AllocatedPages[i].Bitmap & (1 << j) ) { - base = j+1; - rem = nBlocks; - } - - rem --; - if(rem == 0) // Goodie, there's a gap - { - for( j = 0; j < nBlocks; j++ ) - State->Internal->AllocatedPages[i].Bitmap |= 1 << (base + j); - *Segment = State->Internal->AllocatedPages[i].PhysAddr / 16 + base * 8; - *Offset = 0; - LOG("Allocated at #%i,%04x", i, base*128); - LOG(" - %x:%x", *Segment, *Offset); - return (void*)( State->Internal->AllocatedPages[i].VirtBase + base * 128 ); - } - } - } - - // No pages with free space?, allocate a new one - for( i = 0; i < VM8086_PAGES_PER_INST; i++ ) - { - if( State->Internal->AllocatedPages[i].VirtBase == 0 ) break; - } - // Darn, we can't allocate any more - if( i == VM8086_PAGES_PER_INST ) { - Log_Warning("VM8086", "Out of pages in %p", State); - return NULL; - } - - State->Internal->AllocatedPages[i].VirtBase = MM_AllocDMA( - 1, 20, &State->Internal->AllocatedPages[i].PhysAddr); - State->Internal->AllocatedPages[i].Bitmap = 0; - - for( j = 0; j < nBlocks; j++ ) - State->Internal->AllocatedPages[i].Bitmap |= 1 << j; - LOG("AllocatedPages[%i].Bitmap = 0b%b", i, State->Internal->AllocatedPages[i].Bitmap); - *Segment = State->Internal->AllocatedPages[i].PhysAddr / 16; - *Offset = 0; - LOG(" - %x:%x", *Segment, *Offset); - return (void*) State->Internal->AllocatedPages[i].VirtBase; -} - -void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset) -{ - return (void*)( KERNEL_BASE + Segment*16 + Offset ); -} - -void VM8086_Int(tVM8086 *State, Uint8 Interrupt) -{ - State->IP = *(Uint16*)(KERNEL_BASE+4*Interrupt); - State->CS = *(Uint16*)(KERNEL_BASE+4*Interrupt+2); - -// Log_Debug("VM8086", "Software interrupt %i to %04x:%04x", Interrupt, State->CS, State->IP); - - Mutex_Acquire( &glVM8086_Process ); - - gpVM8086_State = State; - gVM8086_CallingThread = Threads_GetTID(); - Semaphore_Signal(&gVM8086_TasksToDo, 1); - - Semaphore_Wait(&gVM8086_TaskComplete, 1); - - Mutex_Release( &glVM8086_Process ); -} diff --git a/Kernel/arch/x86_64/Makefile b/Kernel/arch/x86_64/Makefile deleted file mode 100644 index a72216a3..00000000 --- a/Kernel/arch/x86_64/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -# -# Acess2 Kernel -# i386 Architecture Makefile -# arch/i386/Makefile - -MAX_CPUS := 4 - -AS_SUFFIX = asm - -CPPFLAGS := -DMAX_CPUS=$(MAX_CPUS) -D USE_MP=0 -CFLAGS := $(KERNEL_CFLAGS) -mno-sse -mno-mmx -ASFLAGS := -f elf64 -D MAX_CPUS=$(MAX_CPUS) -D USE_MP=0 -LDFLAGS := -nostdlib -nodefaultlibs - -ifeq ($(ARCH),amd64) - ASFLAGS += -D AMD64=1 - CPPFLAGS += -DAMD64=1 -else - ifeq ($(ARCH),x86_64) - ASFLAGS += -D AMD64=0 -D X86_64=1 - CPPFLAGS += -DAMD64=0 -DX86_64=1 - endif -endif - - -A_OBJ := start32.ao start64.ao desctab.ao proc.ao -A_OBJ += main.o lib.o proc.o mm_virt.o mm_phys.o -A_OBJ += kernelpanic.o errors.o time.o pci.o -A_OBJ += vm8086.o -# rme.o - -POSTBUILD = objcopy $(BIN) -F elf32-i386 $(BIN) diff --git a/Kernel/arch/x86_64/desctab.asm b/Kernel/arch/x86_64/desctab.asm deleted file mode 100644 index 6e8aa639..00000000 --- a/Kernel/arch/x86_64/desctab.asm +++ /dev/null @@ -1,438 +0,0 @@ -; -; -; -%include "arch/x86_64/include/common.inc.asm" -[BITS 64] - -[extern Log] -[extern gGDTPtr] -[extern gGDT] - -%define NUM_IRQ_CALLBACKS 4 - -MM_LOCALAPIC equ 0xFFFFFD0000000000 - -[section .text] -[global Desctab_Init] -Desctab_Init: - ; Save to make following instructions smaller - mov rdi, gIDT - - ; Set an IDT entry to a callback - %macro SETIDT 2 - mov rax, %2 - mov WORD [rdi + %1*16], ax - shr rax, 16 - mov WORD [rdi + %1*16 + 6], ax - shr rax, 16 - mov DWORD [rdi + %1*16 + 8], eax - ; Enable - mov ax, WORD [rdi + %1*16 + 4] - or ax, 0x8000 - mov WORD [rdi + %1*16 + 4], ax - %endmacro - - ; Install error handlers - %macro SETISR 1 - SETIDT %1, Isr%1 - %endmacro - - %assign i 0 - %rep 32 - SETISR i - %assign i i+1 - %endrep - - ; Install IRQs - SETIDT 0xF0, PIT_IRQ - SETIDT 0xF1, Irq1 - SETIDT 0xF2, Irq2 - SETIDT 0xF3, Irq3 - SETIDT 0xF4, Irq4 - SETIDT 0xF5, Irq5 - SETIDT 0xF6, Irq6 - SETIDT 0xF7, Irq7 - SETIDT 0xF8, Irq8 - SETIDT 0xF9, Irq9 - SETIDT 0xFA, Irq10 - SETIDT 0xFB, Irq11 - SETIDT 0xFC, Irq12 - SETIDT 0xFD, Irq13 - SETIDT 0xFE, Irq14 - SETIDT 0xFF, Irq15 - - ; Remap PIC - push rdx ; Save RDX - mov dx, 0x20 - mov al, 0x11 - out dx, al ; Init Command - mov dx, 0x21 - mov al, 0xF0 - out dx, al ; Offset (Start of IDT Range) - mov al, 0x04 - out dx, al ; IRQ connected to Slave (00000100b) = IRQ2 - mov al, 0x01 - out dx, al ; Set Mode - mov al, 0x00 - out dx, al ; Set Mode - - mov dx, 0xA0 - mov al, 0x11 - out dx, al ; Init Command - mov dx, 0xA1 - mov al, 0xF8 - out dx, al ; Offset (Start of IDT Range) - mov al, 0x02 - out dx, al ; IRQ Line connected to master - mov al, 0x01 - out dx, al ; Set Mode - mov dl, 0x00 - out dx, al ; Set Mode - pop rdx - - - ; Install IDT - mov rax, gIDTPtr - lidt [rax] - - ; Re-install GDT (in higher address space) - mov rax, gGDTPtr - mov rcx, gGDT - mov QWORD [rax+2], rcx - lgdt [rax] - - ; Start interrupts - sti - - ; Set IA32_LSTAR (RIP of handler) - mov ecx, 0xC0000082 ; IA32_LSTAR - mov eax, SyscallStub - 0xFFFFFFFF00000000 - mov edx, 0xFFFFFFFF - wrmsr - ; Set IA32_FMASK (flags mask) - mov ecx, 0xC0000084 - rdmsr - mov eax, ~0x202 - wrmsr - ; Set IA32_STAR (Kernel/User CS) - mov ecx, 0xC0000081 - rdmsr - mov edx, 0x8 | (0x1B << 16) ; Kernel CS (and Kernel DS/SS - 8), User CS - wrmsr - - ret - -; int IRQ_AddHandler(int IRQ, void (*Handler)(int IRQ), void *Ptr) -; Return Values: -; 0 on Success -; -1 on an invalid IRQ Number -; -2 when no slots are avaliable -[global IRQ_AddHandler] -IRQ_AddHandler: - ; RDI - IRQ Number - ; RSI - Callback - ; RDX - Ptr - - ; Check for RDI >= 16 - cmp rdi, 16 - jb .numOK - xor rax, rax - dec rax - jmp .ret -.numOK: - - ; Get handler base into RAX - lea rax, [rdi*4] - mov rcx, gaIRQ_Handlers - lea rax, [rcx+rax*8] - - ; Find a free callback slot - %rep NUM_IRQ_CALLBACKS - mov rcx, [rax] - test rcx, rcx - jz .assign - add rax, 8 - %endrep - ; None found, return -2 - xor rax, rax - dec rax - dec rax - jmp .ret - - ; Assign the IRQ Callback -.assign: - ; A little bit of debug - push rdi - push rsi - push rax - push rdx - sub rsp, 8 - mov rcx, rdi ; IRQ Number - mov rdx, rsi ; Callback - mov rsi, rax ; Pointer - mov rdi, csIRQ_Assigned - call Log - add rsp, 8 - pop rdx - pop rax - pop rsi - pop rdi - - ; Assign and return - mov [rax], rsi - add rax, gaIRQ_DataPtrs - gaIRQ_Handlers - mov [rax], rdx - xor rax, rax - -.ret: - ret - -[section .rodata] -csIRQ_Assigned: - db "IRQ %p := %p (IRQ %i)",0 -csIRQ_Fired: - db "IRQ %i fired",0 -[section .text] - -%macro ISR_NOERRNO 1 -Isr%1: - push QWORD 0 - push QWORD %1 - jmp ErrorCommon -%endmacro -%macro ISR_ERRNO 1 -Isr%1: - push QWORD %1 - jmp ErrorCommon -%endmacro - -ISR_NOERRNO 0; 0: Divide By Zero Exception -ISR_NOERRNO 1; 1: Debug Exception -ISR_NOERRNO 2; 2: Non Maskable Interrupt Exception -ISR_NOERRNO 3; 3: Int 3 Exception -ISR_NOERRNO 4; 4: INTO Exception -ISR_NOERRNO 5; 5: Out of Bounds Exception -ISR_NOERRNO 6; 6: Invalid Opcode Exception -ISR_NOERRNO 7; 7: Coprocessor Not Available Exception -ISR_ERRNO 8; 8: Double Fault Exception (With Error Code!) -ISR_NOERRNO 9; 9: Coprocessor Segment Overrun Exception -ISR_ERRNO 10; 10: Bad TSS Exception (With Error Code!) -ISR_ERRNO 11; 11: Segment Not Present Exception (With Error Code!) -ISR_ERRNO 12; 12: Stack Fault Exception (With Error Code!) -ISR_ERRNO 13; 13: General Protection Fault Exception (With Error Code!) -ISR_ERRNO 14; 14: Page Fault Exception (With Error Code!) -ISR_NOERRNO 15; 15: Reserved Exception -ISR_NOERRNO 16; 16: Floating Point Exception -ISR_NOERRNO 17; 17: Alignment Check Exception -ISR_NOERRNO 18; 18: Machine Check Exception -ISR_NOERRNO 19; 19: Reserved -ISR_NOERRNO 20; 20: Reserved -ISR_NOERRNO 21; 21: Reserved -ISR_NOERRNO 22; 22: Reserved -ISR_NOERRNO 23; 23: Reserved -ISR_NOERRNO 24; 24: Reserved -ISR_NOERRNO 25; 25: Reserved -ISR_NOERRNO 26; 26: Reserved -ISR_NOERRNO 27; 27: Reserved -ISR_NOERRNO 28; 28: Reserved -ISR_NOERRNO 29; 29: Reserved -ISR_NOERRNO 30; 30: Reserved -ISR_NOERRNO 31; 31: Reserved - -[extern Error_Handler] -[global ErrorCommon] -ErrorCommon: - PUSH_GPR - push gs - push fs - ;PUSH_FPU - ;PUSH_XMM - - mov rdi, rsp -; xchg bx, bx - call Error_Handler - - ;POP_XMM - ;POP_FPU - pop fs - pop gs - POP_GPR - add rsp, 2*8 - iretq - -%macro DEFIRQ 1 -Irq%1: - push 0 - push %1 - jmp IrqCommon -%endmacro - -%assign i 0 -%rep 16 -DEFIRQ i -%assign i i+1 -%endrep - -[global IrqCommon] -IrqCommon: - PUSH_GPR - push gs - push fs - -; mov rdi, csIRQ_Fired -; mov rsi, [rsp+(16+2)*8] -; call Log - - mov ebx, [rsp+(16+2)*8] ; Get interrupt number (16 GPRS + 2 SRs) - shl ebx, 2 ; *4 - mov rax, gaIRQ_Handlers - lea rbx, [rax+rbx*8] - - ; Check all callbacks - sub rsp, 8 ; Shadow of argument - %assign i 0 - %rep NUM_IRQ_CALLBACKS - ; Get callback address - mov rax, [rbx] - test rax, rax ; Check if it exists - jz .skip.%[i] - ; Set RDI to IRQ number - mov rdi, [rsp+(16+2+1)*8] ; Get IRQ number - mov rsi, [rbx-gaIRQ_Handlers+gaIRQ_DataPtrs] - call rax ; Call -.skip.%[i]: - add rbx, 8 ; Next! - %assign i i+1 - %endrep - add rsp, 8 - - ; ACK - mov al, 0x20 - mov rdi, [rsp+(16+2)*8] ; Get IRQ number - cmp rdi, 8 - jb .skipAckSecondary - out 0xA0, al -.skipAckSecondary: - out 0x20, al - - pop fs - pop gs - POP_GPR - add rsp, 8*2 - iretq - -[extern Time_UpdateTimestamp] - -%if USE_MP -[global APIC_Timer_IRQ] -APIC_Timer_IRQ: - PUSH_GPR - push gs - push fs - - ; TODO: What to do? - - mov eax, DWORD [gpMP_LocalAPIC] - mov DWORD [eax+0x0B0], 0 - - pop fs - pop gs - POP_GPR - iretq -%endif - -[global PIT_IRQ] -PIT_IRQ: - PUSH_GPR - ;PUSH_FPU - ;PUSH_XMM - - call Time_UpdateTimestamp - - %if 0 -[section .rodata] -csUserSS: db "User SS: 0x%x",0 -[section .text] - mov rdi, csUserSS - mov rsi, [rsp+0x80+0x20] - call Log - %endif - - ; Send EOI - mov al, 0x20 - out 0x20, al ; ACK IRQ - - ;POP_XMM - ;POP_FPU - POP_GPR - iretq - -[extern ci_offsetof_tThread_KernelStack] -[extern SyscallHandler] -[global SyscallStub] -SyscallStub: - mov rbp, dr0 - mov ebx, [rel ci_offsetof_tThread_KernelStack] - mov rbp, [rbp+rbx] ; Get kernel stack - xchg rbp, rsp ; Swap stacks - - push rbp ; Save User RSP - push rcx ; RIP - push r11 ; RFLAGS - - ; RDI - ; RSI - ; RDX - ; R10 (RCX for non syscall) - ; R8 - ; R9 - sub rsp, (6+2)*8 - mov [rsp+0x00], rax ; Number -; mov [rsp+0x08], rax ; Errno (output only) - mov [rsp+0x10], rdi ; Arg1 - mov [rsp+0x18], rsi ; Arg2 - mov [rsp+0x20], rdx ; Arg3 - mov [rsp+0x28], r10 ; Arg4 - mov [rsp+0x30], r8 ; Arg5 - mov [rsp+0x38], r9 ; Arg6 - - mov rdi, rsp - sub rsp, 8 - call SyscallHandler - - %if 0 -[section .rodata] -csSyscallReturn: db "Syscall Return: 0x%x",0 -[section .text] - mov rdi, csSyscallReturn - mov rsi, [rsp+0+8] - call Log - %endif - - add rsp, 8 - mov ebx, [rsp+8] ; Get errno - mov rax, [rsp+0] ; Get return - add rsp, (6+2)*8 - - pop r11 - pop rcx - pop rsp ; Change back to user stack - ; TODO: Determine if user is 64 or 32 bit - - db 0x48 ; REX, nasm doesn't have a sysretq opcode - sysret - -[section .data] -gIDT: - ; 64-bit Interrupt Gate, CS = 0x8, IST0 (Disabled) - times 256 dd 0x00080000, 0x00000E00, 0, 0 -gIDTPtr: - dw 256*16-1 - dq gIDT - -gaIRQ_Handlers: - times 16*NUM_IRQ_CALLBACKS dq 0 -gaIRQ_DataPtrs: - times 16*NUM_IRQ_CALLBACKS dq 0 - -; vim: ft=nasm diff --git a/Kernel/arch/x86_64/errors.c b/Kernel/arch/x86_64/errors.c deleted file mode 100644 index f7da7f9f..00000000 --- a/Kernel/arch/x86_64/errors.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Acess2 x86_64 Project - * - Error Handling - */ -#include -#include -#include -#include // Needed for SSE handling - -#define MAX_BACKTRACE 6 - -// === IMPORTS === -extern int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs); -extern void Error_Backtrace(Uint IP, Uint BP); -extern void Proc_EnableSSE(void); -extern void Proc_RestoreSSE(Uint32 Data); - -// === PROTOTYPES === -void Error_Handler(tRegs *Regs); - -// === GLOBALS == -const char * const csaERROR_NAMES[] = { - "Divide By Zero", "Debug", "NMI Exception", "INT3", - "INTO", "Out of Bounds", "Invalid Opcode", "Coprocessor not avaliable", - "Double Fault", "Coprocessor Segment Overrun", "Bad TSS", "Segment Not Present", - "Stack Fault Exception", "GPF", "#PF", "Reserved", - "Floating Point Exception", "Alignment Check Exception", "Machine Check Exception", "Reserved", - "Reserved", "Reserved", "Reserved", "Reserved", - "Reserved", "Reserved", "Reserved", "Reserved", - "Reserved", "Reserved", "Reserved", "Reserved" - }; - -// === CODE === -void Error_Handler(tRegs *Regs) -{ - Uint cr; - - if( Regs->IntNum == 7 ) - { - tThread *thread = Proc_GetCurThread(); - if(!thread->SavedState.bSSEModified) - { - Proc_EnableSSE(); - if(!thread->SavedState.SSE) - thread->SavedState.SSE = malloc(sizeof(tSSEState) + 0xF); - else - Proc_RestoreSSE( ((Uint)thread->SavedState.SSE + 0xF) & ~0xF ); - thread->SavedState.bSSEModified = 1; -// __asm__ __volatile__ ("sti"); - return ; - } - // oops, SSE enabled but a #NM, bad news - } - - if( Regs->IntNum == 14 ) { - __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr)); - if( MM_PageFault(cr, Regs->ErrorCode, Regs) == 0 ) - return ; - } - else { - Debug_KernelPanic(); - - Error_Backtrace(Regs->RIP, Regs->RBP); - } - - Log("CPU Error %x, Code: 0x%x", Regs->IntNum, Regs->ErrorCode); - Log(" - %s", csaERROR_NAMES[Regs->IntNum]); - Log(" CS:RIP = 0x%04x:%016llx", Regs->CS, Regs->RIP); - Log(" SS:RSP = 0x%04x:%016llx", Regs->SS, Regs->RSP); - Log(" RFLAGS = 0x%016llx", Regs->RFlags); - - Log(" RAX %016llx RCX %016llx RDX %016llx RBX %016llx", - Regs->RAX, Regs->RCX, Regs->RDX, Regs->RBX); - Log(" RSP %016llx RBP %016llx RSI %016llx RDI %016llx", - Regs->RSP, Regs->RBP, Regs->RSP, Regs->RDI); - Log(" R8 %016llx R9 %016llx R10 %016llx R11 %016llx", - Regs->R8, Regs->R9, Regs->R10, Regs->R11); - Log(" R12 %016llx R13 %016llx R14 %016llx R15 %016llx", - Regs->R12, Regs->R13, Regs->R14, Regs->R15); - Log(" FS %04x GS %04x", Regs->FS, Regs->GS); - - - // Control Registers - __asm__ __volatile__ ("mov %%cr0, %0":"=r"(cr)); - Warning(" CR0 0x%08x", cr); - __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr)); - Warning(" CR2 0x%016llx", cr); - __asm__ __volatile__ ("mov %%cr3, %0":"=r"(cr)); - Warning(" CR3 0x%016llx", cr); - __asm__ __volatile__ ("mov %%cr4, %0":"=r"(cr)); - Warning(" CR4 0x%08x", cr); - - switch( Regs->IntNum ) - { - case 6: // #UD - Warning(" Offending bytes: %02x %02x %02x %02x", - *(Uint8*)(Regs->RIP+0), *(Uint8*)(Regs->RIP+1), - *(Uint8*)(Regs->RIP+2), *(Uint8*)(Regs->RIP+3) - ); - break; - } - - __asm__ __volatile__ ("cli"); - for(;;) - __asm__ __volatile__ ("hlt"); -} - -/** - * \fn void Error_Backtrace(Uint eip, Uint ebp) - * \brief Unrolls the stack to trace execution - * \param eip Current Instruction Pointer - * \param ebp Current Base Pointer (Stack Frame) - */ -void Error_Backtrace(Uint IP, Uint BP) -{ - int i = 0; - - //if(eip < 0xC0000000 && eip > 0x1000) - //{ - // LogF("Backtrace: User - 0x%x\n", eip); - // return; - //} - - if( IP > USER_MAX && IP < MM_KERNEL_CODE - && (MM_MODULE_MIN > IP || IP > MM_MODULE_MAX) - ) - { - LogF("Backtrace: Data Area - %p\n", IP); - return; - } - - //str = Debug_GetSymbol(eip, &delta); - //if(str == NULL) - LogF("Backtrace: %p", IP); - //else - // LogF("Backtrace: %s+0x%x", str, delta); - if( !MM_GetPhysAddr(BP) ) - { - LogF("\nBacktrace: Invalid BP, stopping\n"); - return; - } - - - while( MM_GetPhysAddr(BP) && MM_GetPhysAddr(BP+8+7) && i < MAX_BACKTRACE ) - { - //str = Debug_GetSymbol(*(Uint*)(ebp+4), &delta); - //if(str == NULL) - LogF(" >> 0x%llx", ((Uint*)BP)[1]); - //else - // LogF(" >> %s+0x%x", str, delta); - BP = ((Uint*)BP)[0]; - i++; - } - LogF("\n"); -} diff --git a/Kernel/arch/x86_64/include/arch.h b/Kernel/arch/x86_64/include/arch.h deleted file mode 100644 index bde7abda..00000000 --- a/Kernel/arch/x86_64/include/arch.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Acess2 x86-64 Architecure Module - * - By John Hodge (thePowersGang) - */ -#ifndef _ARCH_H_ -#define _ARCH_H_ - -//#include -#define USER_MAX 0x00007FFF##FFFFF000 -#define KERNEL_BASE 0xFFFFFFFF##80000000 -#define BITS 64 -#define PAGE_SIZE 0x1000 - -#define STACKED_LOCKS 2 // 0: No, 1: Per-CPU, 2: Per-Thread -#define LOCK_DISABLE_INTS 0 - -#define INVLPTR ((void*)0x0FFFFFFFFFFFFFFFULL) - -//#define INT_MAX 0x7FFFFFFF -//#define UINT_MAX 0xFFFFFFFF - -// === Core Types === -typedef signed char Sint8; -typedef unsigned char Uint8; -typedef signed short Sint16; -typedef unsigned short Uint16; -typedef signed int Sint32; -typedef unsigned int Uint32; -#if __WORDSIZE == 64 -typedef signed long int Sint64; -typedef unsigned long int Uint64; -#else -typedef signed long long int Sint64; -typedef unsigned long long int Uint64; -#endif - -typedef Sint64 Sint; -typedef Uint64 Uint; -typedef Uint64 tPAddr; -typedef Uint64 tVAddr; - -typedef Uint64 size_t; -typedef char BOOL; - -#define __ASM__ __asm__ __volatile__ - -// === MACROS === -/** - * \brief Halt the CPU - */ -#define HALT() __asm__ __volatile__ ("sti;\n\thlt") -/** - * \brief Fire a magic breakpoint (bochs) - */ -#define MAGIC_BREAK() __asm__ __volatile__ ("xchg %bx, %bx") - -// Systemcall Registers -// TODO: Fix this structure -typedef struct sSyscallRegs -{ - union { - Uint Num; - Uint Return; - }; // RAX - Uint Error; // RBX - Uint Arg1; // RDI - Uint Arg2; // RSI - Uint Arg3; // RDX - Uint Arg4; // RCX - Uint Arg5; // R8 - Uint Arg6; // R9 - Uint _Flags; - Uint _IP; - Uint StackPointer; // RSP - -} tSyscallRegs; - -/** - * \brief Short Spinlock structure - */ -struct sShortSpinlock { - #if STACKED_LOCKS == 2 - volatile void *Lock; //!< Lock value - #else - volatile int Lock; //!< Lock value - #endif - - #if LOCK_DISABLE_INTS - int IF; //!< Interrupt state on call to SHORTLOCK - #endif - #if STACKED_LOCKS - int Depth; - #endif -}; - -// === FUNCTIONS === -extern int IS_LOCKED(struct sShortSpinlock *Lock); -extern int CPU_HAS_LOCK(struct sShortSpinlock *Lock); -extern void SHORTLOCK(struct sShortSpinlock *Lock); -extern void SHORTREL(struct sShortSpinlock *Lock); - -extern void Debug_PutCharDebug(char ch); -extern void Debug_PutStringDebug(const char *Str); - -// TODO: Move this to acess.h -extern tPAddr MM_AllocateZero(tVAddr VAddr); - -#endif - diff --git a/Kernel/arch/x86_64/include/arch_config.h b/Kernel/arch/x86_64/include/arch_config.h deleted file mode 100644 index c9f47b0d..00000000 --- a/Kernel/arch/x86_64/include/arch_config.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - */ -#ifndef _ARCH_CONFIG_H_ -#define _ARCH_CONFIG_H_ - - -#define PIT_TIMER_BASE_N 3579545 -#define PIT_TIMER_BASE_D 3 -// PIT Ticks at 1.1931816666666 MHz -// Base is 1193182 HZ -#define PIT_TIMER_DIVISOR 11931 //~100Hz - -#endif diff --git a/Kernel/arch/x86_64/include/common.inc.asm b/Kernel/arch/x86_64/include/common.inc.asm deleted file mode 100644 index d2301777..00000000 --- a/Kernel/arch/x86_64/include/common.inc.asm +++ /dev/null @@ -1,50 +0,0 @@ - -%define INITIAL_KSTACK_SIZE 8 - -%macro SAVE_GPR 1 - mov [%1-0x08], r15 - mov [%1-0x10], r14 - mov [%1-0x18], r13 - mov [%1-0x20], r12 - mov [%1-0x28], r11 - mov [%1-0x30], r10 - mov [%1-0x38], r9 - mov [%1-0x40], r8 - mov [%1-0x48], rdi - mov [%1-0x50], rsi - mov [%1-0x58], rbp - mov [%1-0x60], rsp - mov [%1-0x68], rbx - mov [%1-0x70], rdx - mov [%1-0x78], rcx - mov [%1-0x80], rax -%endmacro - -%macro PUSH_GPR 0 - SAVE_GPR rsp - sub rsp, 0x80 -%endmacro - -%macro RESTORE_GPR 1 - mov r15, [%1-0x08] - mov r14, [%1-0x10] - mov r13, [%1-0x18] - mov r12, [%1-0x20] - mov r11, [%1-0x28] - mov r10, [%1-0x30] - mov r9, [%1-0x38] - mov r8, [%1-0x40] - mov rdi, [%1-0x48] - mov rsi, [%1-0x50] - mov rbp, [%1-0x58] - ;mov rsp, [%1-0x60] - mov rbx, [%1-0x68] - mov rdx, [%1-0x70] - mov rcx, [%1-0x78] - mov rax, [%1-0x80] -%endmacro - -%macro POP_GPR 0 - add rsp, 0x80 - RESTORE_GPR rsp -%endmacro diff --git a/Kernel/arch/x86_64/include/desctab.h b/Kernel/arch/x86_64/include/desctab.h deleted file mode 100644 index de3996c6..00000000 --- a/Kernel/arch/x86_64/include/desctab.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - */ -#ifndef _DESCTAB_H_ -#define _DESCTAB_H_ - -typedef struct { - union - { - struct - { - Uint16 LimitLow; - Uint16 BaseLow; - Uint8 BaseMid; - Uint8 Access; - struct { - unsigned LimitHi: 4; - unsigned Flags: 4; - } __attribute__ ((packed)); - Uint8 BaseHi; - }; - Uint32 DWord[2]; - }; -} __attribute__ ((packed)) tGDT; - -typedef struct { - Uint16 OffsetLo; - Uint16 CS; - Uint16 Flags; // 0-2: IST, 3-7: 0, 8-11: Type, 12: 0, 13-14: DPL, 15: Present - Uint16 OffsetMid; - Uint32 OffsetHi; - Uint32 Reserved; -} __attribute__ ((packed)) tIDT; - -typedef struct { - Uint32 Rsvd1; - - Uint64 RSP0; - Uint64 RSP1; - Uint64 RSP2; - - Uint32 Rsvd2[2]; - - // Interrupt Stack Table Pointers - Uint64 IST1; - Uint64 IST2; - Uint64 IST3; - Uint64 IST4; - Uint64 IST5; - Uint64 IST6; - Uint64 IST7; - - Uint32 Rsvd3[2]; - Uint16 Rsvd4; - Uint16 IOMapBase; -} __attribute__ ((packed)) tTSS; - -#endif diff --git a/Kernel/arch/x86_64/include/mm_virt.h b/Kernel/arch/x86_64/include/mm_virt.h deleted file mode 100644 index 6cf25f02..00000000 --- a/Kernel/arch/x86_64/include/mm_virt.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Acess2 x86_64 Architecture Code - * - * This file is published under the terms of the Acess Licence. - * See the file COPYING for more details - * - * vmem.h - Virtual Memory Functions & Definitions - */ -#ifndef _VMEM_H_ -#define _VMEM_H_ - -#include - -#define PAGE_SIZE 0x1000 - -// === Memory Location Definitions === -/* - * Userland - Lower Half - * Kernel land - Upper Half - * - * START ADDRESS END ADDRESS BITS SIZE NAME - * 0x00000000 00000000 - 0x00007FFF FFFFFFFF 47 128 TiB User Space - * 0x00008000 00000000 - 0xFFFF7FFF FFFFFFFF --- SIGN EXTENSION NULL ZONE - * 0xFFFF8000 00000000 - 0xFFFFFFFF FFFFFFFF 47 128 TiB Kernel Range - * 8000 00000000 - 9000 00000000 42 16 TiB Kernel Heap - * 9000 00000000 - 9800 00000000 43 8 TiB Module Space - * 9800 00000000 - 9A00 00000000 41 2 TiB Kernel VFS - * ---- GAP ---- 6 TiB - * A000 00000000 - B000 00000000 44 16 TiB Kernel Stacks - * C000 00000000 - D000 00000000 44 16 TiB Hardware Mappings - * D000 00000000 - D080 00000000 39 512 GiB Per-Process Data - * D080 00000000 - D100 00000000 39 512 GiB Kernel Supplied User Code - * ---- GAP ---- 15 TiB - * E000 00000000 - E800 00000000 43 8 TiB Physical Page Nodes (2**40 pages * 8 bytes) - * E800 00000000 - EC00 00000000 42 4 TiB Physical Page Reference Counts (2**40 pg * 4 bytes) - * EC00 00000000 - EC80 00000000 39 512 GiB Physical Page Bitmap (1 page per bit) - * EC80 00000000 - ED00 00000000 39 512 GiB Physical Page DblAlloc Bitmap (1 page per bit) - * ED00 00000000 - ED00 80000000 31 2 GiB Physical Page Super Bitmap (64 pages per bit) - * ---- GAP ---- 9 TiB - * FE00 00000000 - FE80 00000000 39 512 GiB Fractal Mapping (PML4 508) - * FE80 00000000 - FF00 00000000 39 512 GiB Temp Fractal Mapping - * FF00 00000000 - FF80 00000000 39 512 GiB Temporary page mappings - * FF80 00000000 - FF80 80000000 31 2 GiB Local APIC - * ---- GAP ---- 506 GiB - * FFFF 00000000 - FFFF 80000000 31 2 GiB User Code - * FFFF 80000000 - FFFF FFFFFFFF 31 2 GiB Kernel code / data - */ - -#define MM_USER_MIN 0x00000000##00010000 -#define USER_LIB_MAX 0x00007000##00000000 -#define USER_STACK_PREALLOC 0x00000000##00002000 // 8 KiB -#define USER_STACK_SZ 0x00000000##00020000 // 64 KiB -#define USER_STACK_TOP 0x00008000##00000000 -#define MM_KERNEL_RANGE 0xFFFF8000##00000000 -#define MM_KHEAP_BASE (MM_KERNEL_RANGE|(0x8000##00000000)) -#define MM_KHEAP_MAX (MM_KERNEL_RANGE|(0x9000##00000000)) -#define MM_MODULE_MIN (MM_KERNEL_RANGE|(0x9000##00000000)) -#define MM_MODULE_MAX (MM_KERNEL_RANGE|(0x9800##00000000)) -#define MM_KERNEL_VFS (MM_KERNEL_RANGE|(0x9800##00000000)) -#define MM_KSTACK_BASE (MM_KERNEL_RANGE|(0xA000##00000000)) -#define MM_KSTACK_TOP (MM_KERNEL_RANGE|(0xB000##00000000)) - -#define MM_HWMAP_BASE (MM_KERNEL_RANGE|(0xC000##00000000)) -#define MM_HWMAP_TOP (MM_KERNEL_RANGE|(0xD000##00000000)) -#define MM_PPD_BASE (MM_KERNEL_RANGE|(0xD000##00000000)) -#define MM_PPD_CFG MM_PPD_BASE -#define MM_PPD_HANDLES (MM_KERNEL_RANGE|(0xD008##00000000)) -#define MM_USER_CODE (MM_KERNEL_RANGE|(0xD080##00000000)) - -#define MM_PAGE_NODES (MM_KERNEL_RANGE|(0xE000##00000000)) -#define MM_PAGE_COUNTS (MM_KERNEL_RANGE|(0xE800##00000000)) -#define MM_PAGE_BITMAP (MM_KERNEL_RANGE|(0xEC00##00000000)) -#define MM_PAGE_DBLBMP (MM_KERNEL_RANGE|(0xEC00##00000000)) -#define MM_PAGE_SUPBMP (MM_KERNEL_RANGE|(0xED00##00000000)) - -#define MM_FRACTAL_BASE (MM_KERNEL_RANGE|(0xFE00##00000000)) -#define MM_TMPFRAC_BASE (MM_KERNEL_RANGE|(0xFE80##00000000)) -#define MM_TMPMAP_BASE (MM_KERNEL_RANGE|(0xFF00##00000000)) -#define MM_TMPMAP_END (MM_KERNEL_RANGE|(0xFF80##00000000)) -#define MM_LOCALAPIC (MM_KERNEL_RANGE|(0xFF80##00000000)) -#define MM_KERNEL_CODE (MM_KERNEL_RANGE|(0xFFFF##80000000)) - - -// === FUNCTIONS === -void MM_FinishVirtualInit(void); -tVAddr MM_NewKStack(void); -tVAddr MM_Clone(void); -tVAddr MM_NewWorkerStack(void *StackData, size_t StackSize); - -#endif diff --git a/Kernel/arch/x86_64/include/proc.h b/Kernel/arch/x86_64/include/proc.h deleted file mode 100644 index dad8f061..00000000 --- a/Kernel/arch/x86_64/include/proc.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Acess2 x86_64 Port - * - * proc.h - Process/Thread management code - */ -#ifndef _PROC_H_ -#define _PROC_H_ - -#include - -// Register Structure -// TODO: Rebuild once IDT code is done -typedef struct { - // MMX - // FPU - Uint FS, GS; - - Uint RAX, RCX, RDX, RBX; - Uint KernelRSP, RBP, RSI, RDI; - Uint R8, R9, R10, R11; - Uint R12, R13, R14, R15; - - Uint IntNum, ErrorCode; - Uint RIP, CS; - Uint RFlags, RSP, SS; -} tRegs; - -/** - * \brief Memory State for thread handler - */ -typedef struct sMemoryState -{ - tPAddr CR3; -} tMemoryState; - -// 512 bytes, 16 byte aligned -typedef struct sSSEState -{ - char data[512]; -} tSSEState; - -/** - * \brief Task state for thread handler - */ -typedef struct sTaskState -{ - Uint RIP, RSP; - Uint64 UserRIP, UserCS; - tSSEState *SSE; - int bSSEModified; -} tTaskState; - -// === CONSTANTS === -#define KERNEL_STACK_SIZE 0x8000 // 32 KiB -//#define KERNEL_STACK_SIZE 0x10000 // 64 KiB - -#endif - diff --git a/Kernel/arch/x86_64/include/vm8086.h b/Kernel/arch/x86_64/include/vm8086.h deleted file mode 100644 index 7c8dd47d..00000000 --- a/Kernel/arch/x86_64/include/vm8086.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Acess2 VM8086 BIOS Interface - * - By John Hodge (thePowersGang) - * - * vm8086.h - * - Core Header - */ -#ifndef _VM80806_H_ -#define _VM80806_H_ - -// === TYPES === -/** - * \note Semi-opaque - Past \a .IP, the implementation may add any data - * it needs to the state. - */ -typedef struct sVM8086 -{ - Uint16 AX, CX, DX, BX; - Uint16 BP, SP, SI, DI; - - Uint16 SS, DS, ES; - - Uint16 CS, IP; - - struct sVM8086_InternalData *Internal; -} tVM8086; - -// === FUNCTIONS === -/** - * \brief Create an instance of the VM8086 Emulator - * \note Do not free this pointer with ::free, instead use ::VM8086_Free - * \return Pointer to a tVM8086 structure, this structure may be larger than - * tVM8086 due to internal data. - */ -extern tVM8086 *VM8086_Init(void); -/** - * \brief Free an allocated tVM8086 structure - * \param State Emulator state to free - */ -extern void VM8086_Free(tVM8086 *State); -/** - * \brief Allocate a piece of memory in the emulated address space and - * return a host and emulated pointer to it. - * \param State Emulator state - * \param Size Size of memory block - * \param Segment Pointer to location to store the allocated memory's segment - * \param Offset Pointet to location to store the allocated memory's offset - * \return Host pointer to the allocated memory - */ -extern void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset); -/** - * \brief Gets a pointer to a piece of emulated memory - * \todo Only 1 machine page is garenteed to be contiguous - * \param State Emulator State - * \param Segment Source Segment - * \param Offset Source Offset - * \return Host pointer to the emulated memory - */ -extern void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset); -/** - * \brief Calls a real-mode interrupt described by the current state of the IVT. - * \param State Emulator State - * \param Interrupt BIOS Interrupt to call - */ -extern void VM8086_Int(tVM8086 *State, Uint8 Interrupt); - -#endif diff --git a/Kernel/arch/x86_64/kernelpanic.c b/Kernel/arch/x86_64/kernelpanic.c deleted file mode 100644 index 1ce8ae70..00000000 --- a/Kernel/arch/x86_64/kernelpanic.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Acess2 x86_64 port - * - Kernel Panic output - */ -#include - -// === PROTOTYPES === -void KernelPanic_SetMode(void); -void KernelPanic_PutChar(char ch); - -// === GLOBALS === -Uint16 *gpKernelPanic_Buffer = (void*)( KERNEL_BASE|0xB8000 ); - int giKernelPanic_CurPos = 0; - -// === CODE === -void KernelPanic_SetMode(void) -{ - giKernelPanic_CurPos = 0; -} - -void KernelPanic_PutChar(char ch) -{ - switch(ch) - { - case '\n': - giKernelPanic_CurPos += 80; - case '\r': - giKernelPanic_CurPos /= 80; - giKernelPanic_CurPos *= 80; - break; - - default: - if(' ' <= ch && ch <= 0x7F) - gpKernelPanic_Buffer[giKernelPanic_CurPos] = 0x4F00|ch; - giKernelPanic_CurPos ++; - break; - } -} diff --git a/Kernel/arch/x86_64/lib.c b/Kernel/arch/x86_64/lib.c deleted file mode 100644 index 5b9609a6..00000000 --- a/Kernel/arch/x86_64/lib.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - */ -#include -#include - -#define DEBUG_TO_E9 1 -#define DEBUG_TO_SERIAL 1 -#define SERIAL_PORT 0x3F8 -#define GDB_SERIAL_PORT 0x2F8 - - -// === IMPORTS === -extern int GetCPUNum(void); -extern void *Proc_GetCurThread(void); - -// === GLOBALS === - int gbDebug_SerialSetup = 0; - int gbGDB_SerialSetup = 0; - -// === PROTOTYPEs === - int putDebugChar(char ch); - -// === CODE === -/** - * \brief Determine if a short spinlock is locked - * \param Lock Lock pointer - */ -int IS_LOCKED(struct sShortSpinlock *Lock) -{ - return !!Lock->Lock; -} - -/** - * \brief Check if the current CPU has the lock - * \param Lock Lock pointer - */ -int CPU_HAS_LOCK(struct sShortSpinlock *Lock) -{ - #if STACKED_LOCKS == 1 - return Lock->Lock == GetCPUNum() + 1; - #elif STACKED_LOCKS == 2 - return Lock->Lock == Proc_GetCurThread(); - #else - return 0; - #endif -} - -/** - * \brief Acquire a Short Spinlock - * \param Lock Lock pointer - * - * This type of mutex should only be used for very short sections of code, - * or in places where a Mutex_* would be overkill, such as appending - * an element to linked list (usually two assignement lines in C) - * - * \note This type of lock halts interrupts, so ensure that no timing - * functions are called while it is held. As a matter of fact, spend as - * little time as possible with this lock held - * \note If \a STACKED_LOCKS is set, this type of spinlock can be nested - */ -void SHORTLOCK(struct sShortSpinlock *Lock) -{ - int v = 1; - #if LOCK_DISABLE_INTS - int IF; - #endif - #if STACKED_LOCKS == 1 - int cpu = GetCPUNum() + 1; - #elif STACKED_LOCKS == 2 - void *thread = Proc_GetCurThread(); - #endif - - #if LOCK_DISABLE_INTS - // Save interrupt state and clear interrupts - __ASM__ ("pushf;\n\tpop %0" : "=r"(IF)); - IF &= 0x200; // AND out all but the interrupt flag - #endif - - #if STACKED_LOCKS == 1 - if( Lock->Lock == cpu ) { - Lock->Depth ++; - return ; - } - #elif STACKED_LOCKS == 2 - if( Lock->Lock == thread ) { - Lock->Depth ++; - return ; - } - #endif - - // Wait for another CPU to release - while(v) { - // CMPXCHG: - // If r/m32 == EAX, set ZF and set r/m32 = r32 - // Else, clear ZF and set EAX = r/m32 - #if STACKED_LOCKS == 1 - __ASM__("lock cmpxchgl %2, (%3)" - : "=a"(v) - : "a"(0), "r"(cpu), "r"(&Lock->Lock) - ); - #elif STACKED_LOCKS == 2 - __ASM__("lock cmpxchgq %2, (%3)" - : "=a"(v) - : "a"(0), "r"(thread), "r"(&Lock->Lock) - ); - #else - __ASM__("xchgl %0, (%2)":"=a"(v):"a"(1),"D"(&Lock->Lock)); - #endif - - #if LOCK_DISABLE_INTS - if( v ) __ASM__("sti"); // Re-enable interrupts - #endif - } - - #if LOCK_DISABLE_INTS - __ASM__("cli"); - Lock->IF = IF; - #endif -} -/** - * \brief Release a short lock - * \param Lock Lock pointer - */ -void SHORTREL(struct sShortSpinlock *Lock) -{ - #if STACKED_LOCKS - if( Lock->Depth ) { - Lock->Depth --; - return ; - } - #endif - - #if LOCK_DISABLE_INTS - // Lock->IF can change anytime once Lock->Lock is zeroed - if(Lock->IF) { - Lock->Lock = 0; - __ASM__ ("sti"); - } - else { - Lock->Lock = 0; - } - #else - Lock->Lock = 0; - #endif -} - -// === DEBUG IO === -#if USE_GDB_STUB -int putDebugChar(char ch) -{ - if(!gbGDB_SerialSetup) { - outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts - outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) - outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud - outb(GDB_SERIAL_PORT + 1, 0x00); // (base is (hi byte) - outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit (8N1) - outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it - outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set - gbDebug_SerialSetup = 1; - } - while( (inb(GDB_SERIAL_PORT + 5) & 0x20) == 0 ); - outb(GDB_SERIAL_PORT, ch); - return 0; -} -int getDebugChar(void) -{ - if(!gbGDB_SerialSetup) { - outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts - outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) - outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud - outb(GDB_SERIAL_PORT + 1, 0x00); // (hi byte) - outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit - outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it - outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set - gbDebug_SerialSetup = 1; - } - while( (inb(GDB_SERIAL_PORT + 5) & 1) == 0) ; - return inb(GDB_SERIAL_PORT); -} -#endif - -void Debug_PutCharDebug(char ch) -{ - #if DEBUG_TO_E9 - __asm__ __volatile__ ( "outb %%al, $0xe9" :: "a"(((Uint8)ch)) ); - #endif - - #if DEBUG_TO_SERIAL - if(!gbDebug_SerialSetup) { - outb(SERIAL_PORT + 1, 0x00); // Disable all interrupts - outb(SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) - outb(SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud - outb(SERIAL_PORT + 1, 0x00); // (hi byte) - outb(SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit - outb(SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it - outb(SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set - gbDebug_SerialSetup = 1; - } - while( (inb(SERIAL_PORT + 5) & 0x20) == 0 ); - outb(SERIAL_PORT, ch); - #endif -} - -void Debug_PutStringDebug(const char *String) -{ - while(*String) - Debug_PutCharDebug(*String++); -} - -// === PORT IO === -void outb(Uint16 Port, Uint8 Data) -{ - __asm__ __volatile__ ("outb %%al, %%dx"::"d"(Port),"a"(Data)); -} -void outw(Uint16 Port, Uint16 Data) -{ - __asm__ __volatile__ ("outw %%ax, %%dx"::"d"(Port),"a"(Data)); -} -void outd(Uint16 Port, Uint32 Data) -{ - __asm__ __volatile__ ("outl %%eax, %%dx"::"d"(Port),"a"(Data)); -} -Uint8 inb(Uint16 Port) -{ - Uint8 ret; - __asm__ __volatile__ ("inb %%dx, %%al":"=a"(ret):"d"(Port)); - return ret; -} -Uint16 inw(Uint16 Port) -{ - Uint16 ret; - __asm__ __volatile__ ("inw %%dx, %%ax":"=a"(ret):"d"(Port)); - return ret; -} -Uint32 ind(Uint16 Port) -{ - Uint32 ret; - __asm__ __volatile__ ("inl %%dx, %%eax":"=a"(ret):"d"(Port)); - return ret; -} - -// === Endianness === -/* -Uint32 BigEndian32(Uint32 Value) -{ - Uint32 ret; - ret = (Value >> 24); - ret |= ((Value >> 16) & 0xFF) << 8; - ret |= ((Value >> 8) & 0xFF) << 16; - ret |= ((Value >> 0) & 0xFF) << 24; - return ret; -} - -Uint16 BigEndian16(Uint16 Value) -{ - return (Value>>8)|(Value<<8); -} -*/ - -// === Memory Manipulation === -int memcmp(const void *__dest, const void *__src, size_t __count) -{ - if( ((tVAddr)__dest & 7) != ((tVAddr)__src & 7) ) { - const Uint8 *src = __src, *dst = __dest; - while(__count) - { - if( *src != *dst ) - return *dst - *src; - src ++; dst ++; __count --; - } - return 0; - } - else { - const Uint8 *src = __src; - const Uint8 *dst = __dest; - const Uint64 *src64, *dst64; - - while( (tVAddr)src & 7 && __count ) { - if( *src != *dst ) - return *dst - *src; - dst ++; src ++; __count --; - } - - src64 = (void*)src; - dst64 = (void*)dst; - - while( __count >= 8 ) - { - if( *src64 != *dst64 ) - { - src = (void*)src64; - dst = (void*)dst64; - if(src[0] != dst[0]) return dst[0]-src[0]; - if(src[1] != dst[1]) return dst[1]-src[1]; - if(src[2] != dst[2]) return dst[2]-src[2]; - if(src[3] != dst[3]) return dst[3]-src[3]; - if(src[4] != dst[4]) return dst[4]-src[4]; - if(src[5] != dst[5]) return dst[5]-src[5]; - if(src[6] != dst[6]) return dst[6]-src[6]; - if(src[7] != dst[7]) return dst[7]-src[7]; - return -1; // This should never happen - } - __count -= 8; - src64 ++; - dst64 ++; - } - - src = (void*)src64; - dst = (void*)dst64; - while( __count-- ) - { - if(*dst != *src) return *dst - *src; - dst ++; - src ++; - } - } - return 0; -} - -void *memcpy(void *__dest, const void *__src, size_t __count) -{ - tVAddr dst = (tVAddr)__dest, src = (tVAddr)__src; - if( (dst & 7) != (src & 7) ) - { - __asm__ __volatile__ ("rep movsb" : : "D"(dst),"S"(src),"c"(__count)); - } - else - { - while( (src & 7) && __count ) { - *(char*)dst++ = *(char*)src++; - __count --; - } - - __asm__ __volatile__ ("rep movsq" : "=D"(dst),"=S"(src) : "0"(dst),"1"(src),"c"(__count/8)); - __count = __count & 7; - while( __count-- ) - *(char*)dst++ = *(char*)src++; - } - return __dest; -} - -void *memset(void *__dest, int __val, size_t __count) -{ - if( __val != 0 || ((tVAddr)__dest & 7) != 0 ) - __asm__ __volatile__ ("rep stosb" : : "D"(__dest),"a"(__val),"c"(__count)); - else { - Uint8 *dst = __dest; - - __asm__ __volatile__ ("rep stosq" : : "D"(dst),"a"(0),"c"(__count/8)); - dst += __count & ~7; - __count = __count & 7; - while( __count-- ) - *dst++ = 0; - } - return __dest; -} - -void *memsetd(void *__dest, Uint32 __val, size_t __count) -{ - __asm__ __volatile__ ("rep stosl" : : "D"(__dest),"a"(__val),"c"(__count)); - return __dest; -} - -Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem) -{ - Uint64 ret, rem; - __asm__ __volatile__( - "div %4" - : "=a" (ret), "=d" (rem) - : "a" ( Num ), "d" (0), "r" (Den) - ); - if(Rem) *Rem = rem; - return ret; -} - diff --git a/Kernel/arch/x86_64/link.ld b/Kernel/arch/x86_64/link.ld deleted file mode 100644 index 49fe7ae3..00000000 --- a/Kernel/arch/x86_64/link.ld +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Acess2 x86_64 Kernel - * Linker Script - */ - -/* _kernel_base = 0xFFFF800000000000; */ -/* -2 GiB */ -_kernel_base = 0xFFFFFFFF80000000; - -/* -OUTPUT_FORMAT(elf32-i386) -OUTPUT_ARCH(i386:x86-64) -*/ -OUTPUT_FORMAT(elf64-x86-64) -ENTRY(start) - -SECTIONS { - . = 0x100000; - gKernelBase = .; - . += SIZEOF_HEADERS; - __load_addr = .; - .multiboot : AT(ADDR(.multiboot)) { - *(.multiboot) - } - - . += _kernel_base; - - .text ALIGN(0x1000): AT(ADDR(.text) - _kernel_base) { - *(.text) - } - - .usertext ALIGN(0x1000): AT(ADDR(.usertext) - _kernel_base) { - _UsertextBase = .; - *(.usertext) - _UsertextEnd = .; - } - - .rodata ALIGN(0x1000): AT(ADDR(.rodata) - _kernel_base) { - *(.initpd) - *(.rodata .rodata.*) - *(.rdata) - - . = ALIGN(0x10); - gKernelModules = .; - *(KMODULES) - gKernelModulesEnd = .; - . = ALIGN(0x10); - gKernelSymbols = .; - *(KEXPORT) - gKernelSymbolsEnd = .; - } - - .data ALIGN (0x1000) : AT(ADDR(.data) - _kernel_base) { - *(.padata) - *(.data) - } - - __bss_start = .; - .bss : AT(ADDR(.bss) - _kernel_base) { - *(COMMON) - *(.bss) - } - gKernelEnd = (. + 0xFFF)&0xFFFFFFFFFFFFF000; -} diff --git a/Kernel/arch/x86_64/main.c b/Kernel/arch/x86_64/main.c deleted file mode 100644 index 3532c809..00000000 --- a/Kernel/arch/x86_64/main.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Acess2 x86_64 Project - */ -#include -#include -#include - -// === IMPORTS === -extern void Desctab_Init(void); -extern void MM_InitVirt(void); -extern void Heap_Install(void); -extern void Threads_Init(void); -extern int Time_Setup(void); -extern void System_Init(char *Commandline); - -extern void MM_InitPhys_Multiboot(tMBoot_Info *MBoot); - -// === PROTOTYPES === -void kmain(Uint MbMagic, void *MbInfoPtr); - -// === GLOBALS == -char *gsBootCmdLine = NULL; - -// === CODE === -void kmain(Uint MbMagic, void *MbInfoPtr) -{ - tMBoot_Info *mbInfo; - - LogF("Acess2 x86_64 v"EXPAND_STR(KERNEL_VERSION)"\n"); - LogF(" Build %i, Git Hash %s\n", BUILD_NUM, gsGitHash); - - Desctab_Init(); - - MM_InitVirt(); - *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'C'; - - switch(MbMagic) - { - // Multiboot 1 - case MULTIBOOT_MAGIC: - // Adjust Multiboot structure address - mbInfo = (void*)( (Uint)MbInfoPtr + KERNEL_BASE ); - gsBootCmdLine = (char*)( (Uint)mbInfo->CommandLine + KERNEL_BASE); - MM_InitPhys_Multiboot( mbInfo ); // Set up physical memory manager - break; - default: - Panic("Multiboot magic invalid %08x, expected %08x\n", - MbMagic, MULTIBOOT_MAGIC); - return ; - } - - Log("gsBootCmdLine = '%s'", gsBootCmdLine); - - *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'D'; - Heap_Install(); - - *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'E'; - Threads_Init(); - - Time_Setup(); - *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'F'; - - // Load Virtual Filesystem - Log_Log("Arch", "Starting VFS..."); - VFS_Init(); - - *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'Z'; - - // Pass on to Independent Loader - Log_Log("Arch", "Starting system"); - System_Init(gsBootCmdLine); - - // Sleep forever (sleeping beauty) - for(;;) - Threads_Sleep(); -} - -void Arch_LoadBootModules(void) -{ - -} - -void StartupPrint(const char *String) -{ - -} diff --git a/Kernel/arch/x86_64/mm_phys.c b/Kernel/arch/x86_64/mm_phys.c deleted file mode 100644 index c2c215b3..00000000 --- a/Kernel/arch/x86_64/mm_phys.c +++ /dev/null @@ -1,641 +0,0 @@ -/* - * Acess2 x86_64 Port - * - * Physical Memory Manager - */ -#define DEBUG 0 -#include -#include -#include - -#define TRACE_REF 0 - -enum eMMPhys_Ranges -{ - MM_PHYS_16BIT, // Does anything need this? - MM_PHYS_20BIT, // Real-Mode - MM_PHYS_24BIT, // ISA DMA - MM_PHYS_32BIT, // x86 Hardware - MM_PHYS_MAX, // Doesn't care - NUM_MM_PHYS_RANGES -}; - -// === IMPORTS === -extern char gKernelBase[]; -extern char gKernelEnd[]; - -// === PROTOTYPES === -void MM_InitPhys_Multiboot(tMBoot_Info *MBoot); -//tPAddr MM_AllocPhysRange(int Num, int Bits); -//tPAddr MM_AllocPhys(void); -//void MM_RefPhys(tPAddr PAddr); -//void MM_DerefPhys(tPAddr PAddr); - int MM_int_GetRangeID( tPAddr Addr ); - -// === MACROS === -#define PAGE_ALLOC_TEST(__page) (gaMainBitmap[(__page)>>6] & (1ULL << ((__page)&63))) -#define PAGE_ALLOC_SET(__page) do{gaMainBitmap[(__page)>>6] |= (1ULL << ((__page)&63));}while(0) -#define PAGE_ALLOC_CLEAR(__page) do{gaMainBitmap[(__page)>>6] &= ~(1ULL << ((__page)&63));}while(0) -//#define PAGE_MULTIREF_TEST(__page) (gaMultiBitmap[(__page)>>6] & (1ULL << ((__page)&63))) -//#define PAGE_MULTIREF_SET(__page) do{gaMultiBitmap[(__page)>>6] |= 1ULL << ((__page)&63);}while(0) -//#define PAGE_MULTIREF_CLEAR(__page) do{gaMultiBitmap[(__page)>>6] &= ~(1ULL << ((__page)&63));}while(0) - -// === GLOBALS === -tMutex glPhysicalPages; -Uint64 *gaSuperBitmap = (void*)MM_PAGE_SUPBMP; // 1 bit = 64 Pages, 16 MiB per Word -Uint64 *gaMainBitmap = (void*)MM_PAGE_BITMAP; // 1 bit = 1 Page, 256 KiB per Word -Uint64 *gaMultiBitmap = (void*)MM_PAGE_DBLBMP; // Each bit means that the page is being used multiple times -Uint32 *gaiPageReferences = (void*)MM_PAGE_COUNTS; // Reference Counts -void **gapPageNodes = (void*)MM_PAGE_NODES; // Reference Counts -tPAddr giFirstFreePage; // First possibly free page -Uint64 giPhysRangeFree[NUM_MM_PHYS_RANGES]; // Number of free pages in each range -Uint64 giPhysRangeFirst[NUM_MM_PHYS_RANGES]; // First free page in each range -Uint64 giPhysRangeLast[NUM_MM_PHYS_RANGES]; // Last free page in each range -Uint64 giMaxPhysPage = 0; // Maximum Physical page -// Only used in init, allows the init code to provide pages for use by -// the allocator before the bitmaps exist. -// 3 entries because the are three calls to MM_AllocPhys in MM_Map -#define NUM_STATIC_ALLOC 3 -tPAddr gaiStaticAllocPages[NUM_STATIC_ALLOC] = {0}; - -// === CODE === -/** - * \brief Initialise the physical memory map using a Multiboot 1 map - */ -void MM_InitPhys_Multiboot(tMBoot_Info *MBoot) -{ - tMBoot_MMapEnt *mmapStart; - tMBoot_MMapEnt *ent; - Uint64 maxAddr = 0; - int numPages, superPages; - int i; - Uint64 base, size; - tVAddr vaddr; - tPAddr paddr, firstFreePage; - - ENTER("pMBoot=%p", MBoot); - - // Scan the physical memory map - // Looking for the top of physical memory - mmapStart = (void *)( KERNEL_BASE | MBoot->MMapAddr ); - LOG("mmapStart = %p", mmapStart); - ent = mmapStart; - while( (Uint)ent < (Uint)mmapStart + MBoot->MMapLength ) - { - // Adjust for the size of the entry - ent->Size += 4; - LOG("ent={Type:%i,Base:0x%x,Length:%x", - ent->Type, ent->Base, ent->Length); - - // If entry is RAM and is above `maxAddr`, change `maxAddr` - if(ent->Type == 1 && ent->Base + ent->Length > maxAddr) - maxAddr = ent->Base + ent->Length; - - // Go to next entry - ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size ); - } - - // Did we find a valid end? - if(maxAddr == 0) { - // No, darn, let's just use the HighMem hack - giMaxPhysPage = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value - } - else { - // Goodie, goodie gumdrops - giMaxPhysPage = maxAddr >> 12; - } - LOG("giMaxPhysPage = 0x%x", giMaxPhysPage); - - // Find a contigous section of memory to hold it in - // - Starting from the end of the kernel - // - We also need a region for the super bitmap - superPages = ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12; - numPages = (giMaxPhysPage + 7) / 8; - numPages = (numPages + 0xFFF) >> 12; - LOG("numPages = %i, superPages = %i", numPages, superPages); - if(maxAddr == 0) - { - int todo = numPages*2 + superPages; - // Ok, naieve allocation, just put it after the kernel - // - Allocated Bitmap - vaddr = MM_PAGE_BITMAP; - paddr = (tPAddr)&gKernelEnd - KERNEL_BASE; - while(todo ) - { - // Allocate statics - for( i = 0; i < NUM_STATIC_ALLOC; i++) { - if(gaiStaticAllocPages[i] != 0) continue; - gaiStaticAllocPages[i] = paddr; - paddr += 0x1000; - } - - MM_Map(vaddr, paddr); - vaddr += 0x1000; - paddr += 0x1000; - - todo --; - - if( todo == numPages + superPages ) - vaddr = MM_PAGE_DBLBMP; - if( todo == superPages ) - vaddr = MM_PAGE_SUPBMP; - } - } - // Scan for a nice range - else - { - int todo = numPages*2 + superPages; - paddr = 0; - vaddr = MM_PAGE_BITMAP; - // Scan! - for( - ent = mmapStart; - (Uint)ent < (Uint)mmapStart + MBoot->MMapLength; - ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size ) - ) - { - int avail; - - // RAM only please - if( ent->Type != 1 ) - continue; - - // Let's not put it below the kernel, shall we? - if( ent->Base + ent->Size < (tPAddr)&gKernelBase ) - continue; - - LOG("%x <= %x && %x > %x", - ent->Base, (tPAddr)&gKernelBase, - ent->Base + ent->Size, (tPAddr)&gKernelEnd - KERNEL_BASE - ); - // Check if the kernel is in this range - if( ent->Base <= (tPAddr)&gKernelBase - && ent->Base + ent->Length > (tPAddr)&gKernelEnd - KERNEL_BASE ) - { - avail = ent->Length >> 12; - avail -= ((tPAddr)&gKernelEnd - KERNEL_BASE - ent->Base) >> 12; - paddr = (tPAddr)&gKernelEnd - KERNEL_BASE; - } - // No? then we can use all of the block - else - { - avail = ent->Length >> 12; - paddr = ent->Base; - } - - Log("MM_InitPhys_Multiboot: paddr=0x%x, avail=0x%x pg", paddr, avail); - - // Map - while( todo && avail --) - { - // Static Allocations - for( i = 0; i < NUM_STATIC_ALLOC && avail; i++) { - if(gaiStaticAllocPages[i] != 0) continue; - gaiStaticAllocPages[i] = paddr; - paddr += 0x1000; - avail --; - } - if(!avail) break; - - // Map - MM_Map(vaddr, paddr); - todo --; - vaddr += 0x1000; - paddr += 0x1000; - - // Alter the destination address when needed - if(todo == superPages+numPages) - vaddr = MM_PAGE_DBLBMP; - if(todo == superPages) - vaddr = MM_PAGE_SUPBMP; - } - - // Fast quit if there's nothing left to allocate - if( !todo ) break; - } - } - // Save the current value of paddr to simplify the allocation later - firstFreePage = paddr; - - LOG("Clearing multi bitmap"); - // Fill the bitmaps - memset(gaMultiBitmap, 0, (numPages<<12)/8); - // - initialise to one, then clear the avaliable areas - memset(gaMainBitmap, -1, (numPages<<12)/8); - memset(gaSuperBitmap, -1, (numPages<<12)/(8*64)); - LOG("Setting main bitmap"); - // - Clear all Type=1 areas - LOG("Clearing valid regions"); - for( - ent = mmapStart; - (Uint)ent < (Uint)mmapStart + MBoot->MMapLength; - ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size ) - ) - { - // Check if the type is RAM - if(ent->Type != 1) continue; - - // Main bitmap - base = ent->Base >> 12; - size = ent->Size >> 12; - - if(base & 63) { - Uint64 val = -1LL << (base & 63); - gaMainBitmap[base / 64] &= ~val; - size -= (base & 63); - base += 64 - (base & 63); - } - memset( &gaMainBitmap[base / 64], 0, size/8 ); - if( size & 7 ) { - Uint64 val = -1LL << (size & 7); - val <<= (size/8)&7; - gaMainBitmap[base / 64] &= ~val; - } - - // Super Bitmap - base = ent->Base >> 12; - size = ent->Size >> 12; - size = (size + (base & 63) + 63) >> 6; - base = base >> 6; - if(base & 63) { - Uint64 val = -1LL << (base & 63); - gaSuperBitmap[base / 64] &= ~val; -// size -= (base & 63); -// base += 64 - (base & 63); - } - } - - // Reference the used pages - base = (tPAddr)&gKernelBase >> 12; - size = firstFreePage >> 12; - memset( &gaMainBitmap[base / 64], -1, size/8 ); - if( size & 7 ) { - Uint64 val = -1LL << (size & 7); - val <<= (size/8)&7; - gaMainBitmap[base / 64] |= val; - } - - // Free the unused static allocs - for( i = 0; i < NUM_STATIC_ALLOC; i++) { - if(gaiStaticAllocPages[i] != 0) - continue; - gaMainBitmap[ gaiStaticAllocPages[i] >> (12+6) ] - &= ~(1LL << ((gaiStaticAllocPages[i]>>12)&63)); - } - - // Fill the super bitmap - LOG("Filling super bitmap"); - memset(gaSuperBitmap, 0, superPages<<12); - for( base = 0; base < (size+63)/64; base ++) - { - if( gaMainBitmap[ base ] + 1 == 0 ) - gaSuperBitmap[ base/64 ] |= 1LL << (base&63); - } - - // Set free page counts - for( base = 1; base < giMaxPhysPage; base ++ ) - { - int rangeID; - // Skip allocated - if( gaMainBitmap[ base >> 6 ] & (1LL << (base&63)) ) continue; - - // Get range ID - rangeID = MM_int_GetRangeID( base << 12 ); - - // Increment free page count - giPhysRangeFree[ rangeID ] ++; - - // Check for first free page in range - if(giPhysRangeFirst[ rangeID ] == 0) - giPhysRangeFirst[ rangeID ] = base; - // Set last (when the last free page is reached, this won't be - // updated anymore, hence will be correct) - giPhysRangeLast[ rangeID ] = base; - } - - LEAVE('-'); -} - -/** - * \brief Allocate a contiguous range of physical pages with a maximum - * bit size of \a MaxBits - * \param Pages Number of pages to allocate - * \param MaxBits Maximum size of the physical address - * \note If \a MaxBits is <= 0, any sized address is used (with preference - * to higher addresses) - */ -tPAddr MM_AllocPhysRange(int Pages, int MaxBits) -{ - tPAddr addr, ret; - int rangeID; - int nFree = 0, i; - - ENTER("iPages iBits", Pages, MaxBits); - - if( MaxBits <= 0 || MaxBits >= 64 ) // Speedup for the common case - rangeID = MM_PHYS_MAX; - else - rangeID = MM_int_GetRangeID( (1LL << MaxBits) - 1 ); - - LOG("rangeID = %i", rangeID); - - Mutex_Acquire(&glPhysicalPages); - - // Check if the range actually has any free pages - while(giPhysRangeFree[rangeID] == 0 && rangeID) - rangeID --; - - LOG("rangeID = %i", rangeID); - - // What the? Oh, man. No free pages - if(giPhysRangeFree[rangeID] == 0) { - Mutex_Release(&glPhysicalPages); - // TODO: Page out - // ATM. Just Warning - Warning(" MM_AllocPhysRange: Out of free pages"); - Log_Warning("Arch", - "Out of memory (unable to fulfil request for %i pages), zero remaining", - Pages - ); - LEAVE('i', 0); - return 0; - } - - // Check if there is enough in the range - if(giPhysRangeFree[rangeID] >= Pages) - { - LOG("{%i,0x%x -> 0x%x}", - giPhysRangeFree[rangeID], - giPhysRangeFirst[rangeID], giPhysRangeLast[rangeID] - ); - // Do a cheap scan, scanning upwards from the first free page in - // the range - nFree = 0; - addr = giPhysRangeFirst[ rangeID ]; - while( addr <= giPhysRangeLast[ rangeID ] ) - { - //Log(" MM_AllocPhysRange: addr = 0x%x", addr); - // Check the super bitmap - if( gaSuperBitmap[addr >> (6+6)] + 1 == 0 ) { - LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr); - nFree = 0; - addr += 1LL << (6+6); - addr &= ~0xFFF; // (1LL << 6+6) - 1 - continue; - } - // Check page block (64 pages) - if( gaMainBitmap[addr >> 6] + 1 == 0) { - LOG("nFree = %i = 0 (main) (0x%x)", nFree, addr); - nFree = 0; - addr += 1LL << (6); - addr &= ~0x3F; - continue; - } - // Check individual page - if( gaMainBitmap[addr >> 6] & (1LL << (addr & 63)) ) { - LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr); - nFree = 0; - addr ++; - continue; - } - nFree ++; - addr ++; - LOG("nFree(%i) == %i (0x%x)", nFree, Pages, addr); - if(nFree == Pages) - break; - } - LOG("nFree = %i", nFree); - // If we don't find a contiguous block, nFree will not be equal - // to Num, so we set it to zero and do the expensive lookup. - if(nFree != Pages) nFree = 0; - } - - if( !nFree ) - { - // Oops. ok, let's do an expensive check (scan down the list - // until a free range is found) -// nFree = 1; -// addr = giPhysRangeLast[ rangeID ]; - // TODO: Expensive Check - Mutex_Release(&glPhysicalPages); - // TODO: Page out - // ATM. Just Warning - Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Pages); - Log_Warning("Arch", - "Out of memory (unable to fulfil request for %i pages)", - Pages - ); - LEAVE('i', 0); - return 0; - } - LOG("nFree = %i, addr = 0x%08x", nFree, addr); - - // Mark pages as allocated - addr -= Pages; - for( i = 0; i < Pages; i++, addr++ ) - { - gaMainBitmap[addr >> 6] |= 1LL << (addr & 63); - if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[addr] ) ) - gaiPageReferences[addr] = 1; -// Log("page %P refcount = %i", MM_GetRefCount(addr<<12)); - rangeID = MM_int_GetRangeID(addr << 12); - giPhysRangeFree[ rangeID ] --; - LOG("%x == %x", addr, giPhysRangeFirst[ rangeID ]); - if(addr == giPhysRangeFirst[ rangeID ]) - giPhysRangeFirst[ rangeID ] += 1; - } - addr -= Pages; - ret = addr; // Save the return address - - // Update super bitmap - Pages += addr & (64-1); - addr &= ~(64-1); - Pages = (Pages + (64-1)) & ~(64-1); - for( i = 0; i < Pages/64; i++ ) - { - if( gaMainBitmap[ addr >> 6 ] + 1 == 0 ) - gaSuperBitmap[addr>>12] |= 1LL << ((addr >> 6) & 63); - } - - Mutex_Release(&glPhysicalPages); - #if TRACE_REF - Log("MM_AllocPhysRange: ret = %P (Ref %i)", ret << 12, MM_GetRefCount(ret<<12)); - #endif - LEAVE('x', ret << 12); - return ret << 12; -} - -/** - * \brief Allocate a single physical page, with no preference as to address - * size. - */ -tPAddr MM_AllocPhys(void) -{ - int i; - - // Hack to allow allocation during setup - for(i = 0; i < NUM_STATIC_ALLOC; i++) { - if( gaiStaticAllocPages[i] ) { - tPAddr ret = gaiStaticAllocPages[i]; - gaiStaticAllocPages[i] = 0; - Log("MM_AllocPhys: Return %P, static alloc %i", ret, i); - return ret; - } - } - - return MM_AllocPhysRange(1, -1); -} - -/** - * \brief Reference a physical page - */ -void MM_RefPhys(tPAddr PAddr) -{ - Uint64 page = PAddr >> 12; - - if( page > giMaxPhysPage ) return ; - - if( PAGE_ALLOC_TEST(page) ) - { - tVAddr ref_base = ((tVAddr)&gaiPageReferences[ page ]) & ~0xFFF; - // Allocate reference page - if( !MM_GetPhysAddr(ref_base) ) - { - const int pages_per_refpage = PAGE_SIZE/sizeof(gaiPageReferences[0]); - int i; - int page_base = page / pages_per_refpage * pages_per_refpage; - if( !MM_Allocate( ref_base ) ) { - Log_Error("Arch", "Out of memory when allocating reference count page"); - return ; - } - // Fill block - Log("Allocated references for %P-%P", page_base << 12, (page_base+pages_per_refpage)<<12); - for( i = 0; i < pages_per_refpage; i ++ ) { - int pg = page_base + i; - gaiPageReferences[pg] = !!PAGE_ALLOC_TEST(pg); - } - } - gaiPageReferences[page] ++; - } - else - { - // Allocate - PAGE_ALLOC_SET(page); - if( gaMainBitmap[page >> 6] + 1 == 0 ) - gaSuperBitmap[page>> 12] |= 1LL << ((page >> 6) & 63); - if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[page] ) ) - gaiPageReferences[page] = 1; - } - - #if TRACE_REF - Log("MM_RefPhys: %P referenced (%i)", page << 12, MM_GetRefCount(page << 12)); - #endif -} - -/** - * \brief Dereference a physical page - */ -void MM_DerefPhys(tPAddr PAddr) -{ - Uint64 page = PAddr >> 12; - - if( PAddr >> 12 > giMaxPhysPage ) return ; - - if( MM_GetPhysAddr( (tVAddr) &gaiPageReferences[page] ) ) - { - gaiPageReferences[ page ] --; - if( gaiPageReferences[ page ] == 0 ) - PAGE_ALLOC_CLEAR(page); - } - else - PAGE_ALLOC_CLEAR(page); - - // Update the free counts if the page was freed - if( !PAGE_ALLOC_TEST(page) ) - { - int rangeID; - rangeID = MM_int_GetRangeID( PAddr ); - giPhysRangeFree[ rangeID ] ++; - if( giPhysRangeFirst[rangeID] > page ) - giPhysRangeFirst[rangeID] = page; - if( giPhysRangeLast[rangeID] < page ) - giPhysRangeLast[rangeID] = page; - } - - // If the bitmap entry is not -1, unset the bit in the super bitmap - if(gaMainBitmap[ page >> 6 ] + 1 != 0 ) { - gaSuperBitmap[page >> 12] &= ~(1LL << ((page >> 6) & 63)); - } - - #if TRACE_REF - Log("Page %P dereferenced (%i)", page << 12, MM_GetRefCount(page << 12)); - #endif -} - -int MM_GetRefCount( tPAddr PAddr ) -{ - PAddr >>= 12; - - if( PAddr > giMaxPhysPage ) return 0; - - if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[PAddr] ) ) { - return gaiPageReferences[PAddr]; - } - - if( PAGE_ALLOC_TEST(PAddr) ) - { - return 1; - } - return 0; -} - -/** - * \brief Takes a physical address and returns the ID of its range - * \param Addr Physical address of page - * \return Range ID from eMMPhys_Ranges - */ -int MM_int_GetRangeID( tPAddr Addr ) -{ - if(Addr >> 32) - return MM_PHYS_MAX; - else if(Addr >> 24) - return MM_PHYS_32BIT; - else if(Addr >> 20) - return MM_PHYS_24BIT; - else if(Addr >> 16) - return MM_PHYS_20BIT; - else - return MM_PHYS_16BIT; -} - -int MM_SetPageNode(tPAddr PAddr, void *Node) -{ - tPAddr page = PAddr >> 12; - tVAddr node_page = ((tVAddr)&gapPageNodes[page]) & ~(PAGE_SIZE-1); - -// if( !MM_GetRefCount(PAddr) ) return 1; - - if( !MM_GetPhysAddr(node_page) ) { - if( !MM_Allocate(node_page) ) - return -1; - memset( (void*)node_page, 0, PAGE_SIZE ); - } - - gapPageNodes[page] = Node; - return 0; -} - -int MM_GetPageNode(tPAddr PAddr, void **Node) -{ -// if( !MM_GetRefCount(PAddr) ) return 1; - PAddr >>= 12; - - if( !MM_GetPhysAddr( (tVAddr)&gapPageNodes[PAddr] ) ) { - *Node = NULL; - return 0; - } - - *Node = gapPageNodes[PAddr]; - return 0; -} - diff --git a/Kernel/arch/x86_64/mm_virt.c b/Kernel/arch/x86_64/mm_virt.c deleted file mode 100644 index 89aeaa15..00000000 --- a/Kernel/arch/x86_64/mm_virt.c +++ /dev/null @@ -1,1110 +0,0 @@ -/* - * Acess2 x86_64 Port - * - * Virtual Memory Manager - */ -#define DEBUG 0 -#include -#include -#include -#include -#include - -// === DEBUG OPTIONS === -#define TRACE_COW 0 - -// === CONSTANTS === -#define PHYS_BITS 52 // TODO: Move out -#define VIRT_BITS 48 - -#define PML4_SHIFT 39 -#define PDP_SHIFT 30 -#define PDIR_SHIFT 21 -#define PTAB_SHIFT 12 - -#define PADDR_MASK 0x7FFFFFFF##FFFFF000 -#define PAGE_MASK ((1LL << 36)-1) -#define TABLE_MASK ((1LL << 27)-1) -#define PDP_MASK ((1LL << 18)-1) -#define PML4_MASK ((1LL << 9)-1) - -#define PF_PRESENT 0x001 -#define PF_WRITE 0x002 -#define PF_USER 0x004 -#define PF_LARGE 0x080 -#define PF_GLOBAL 0x100 -#define PF_COW 0x200 -#define PF_PAGED 0x400 -#define PF_NX 0x80000000##00000000 - -// === MACROS === -#define PAGETABLE(idx) (*((Uint64*)MM_FRACTAL_BASE+((idx)&PAGE_MASK))) -#define PAGEDIR(idx) PAGETABLE((MM_FRACTAL_BASE>>12)+((idx)&TABLE_MASK)) -#define PAGEDIRPTR(idx) PAGEDIR((MM_FRACTAL_BASE>>21)+((idx)&PDP_MASK)) -#define PAGEMAPLVL4(idx) PAGEDIRPTR((MM_FRACTAL_BASE>>30)+((idx)&PML4_MASK)) - -#define TMPCR3() PAGEMAPLVL4(MM_TMPFRAC_BASE>>39) -#define TMPTABLE(idx) (*((Uint64*)MM_TMPFRAC_BASE+((idx)&PAGE_MASK))) -#define TMPDIR(idx) PAGETABLE((MM_TMPFRAC_BASE>>12)+((idx)&TABLE_MASK)) -#define TMPDIRPTR(idx) PAGEDIR((MM_TMPFRAC_BASE>>21)+((idx)&PDP_MASK)) -#define TMPMAPLVL4(idx) PAGEDIRPTR((MM_TMPFRAC_BASE>>30)+((idx)&PML4_MASK)) - -#define INVLPG(__addr) __asm__ __volatile__ ("invlpg (%0)"::"r"(__addr)) -#define INVLPG_ALL() __asm__ __volatile__ ("mov %cr3,%rax;\n\tmov %rax,%cr3;") -#define INVLPG_GLOBAL() __asm__ __volatile__ ("mov %cr4,%rax;\n\txorl $0x80, %eax;\n\tmov %rax,%cr4;\n\txorl $0x80, %eax;\n\tmov %rax,%cr4") - -// === CONSTS === -//tPAddr * const gaPageTable = MM_FRACTAL_BASE; - -// === IMPORTS === -extern void Error_Backtrace(Uint IP, Uint BP); -extern tPAddr gInitialPML4[512]; -extern void Threads_SegFault(tVAddr Addr); -extern char _UsertextBase[]; - -// === PROTOTYPES === -void MM_InitVirt(void); -//void MM_FinishVirtualInit(void); -void MM_int_ClonePageEnt( Uint64 *Ent, void *NextLevel, tVAddr Addr, int bTable ); - int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs); -void MM_int_DumpTablesEnt(tVAddr RangeStart, size_t Length, tPAddr Expected); -//void MM_DumpTables(tVAddr Start, tVAddr End); - int MM_GetPageEntryPtr(tVAddr Addr, BOOL bTemp, BOOL bAllocate, BOOL bLargePage, tPAddr **Pointer); - int MM_MapEx(tVAddr VAddr, tPAddr PAddr, BOOL bTemp, BOOL bLarge); -// int MM_Map(tVAddr VAddr, tPAddr PAddr); -void MM_Unmap(tVAddr VAddr); -void MM_int_ClearTableLevel(tVAddr VAddr, int LevelBits, int MaxEnts); -//void MM_ClearUser(void); - int MM_GetPageEntry(tVAddr Addr, tPAddr *Phys, Uint *Flags); - -// === GLOBALS === -tMutex glMM_TempFractalLock; -tPAddr gMM_ZeroPage; - -// === CODE === -void MM_InitVirt(void) -{ -// Log_Debug("MMVirt", "&PAGEMAPLVL4(0) = %p", &PAGEMAPLVL4(0)); -// MM_DumpTables(0, -1L); -} - -void MM_FinishVirtualInit(void) -{ - PAGEMAPLVL4(0) = 0; -} - -/** - * \brief Clone a page from an entry - * \param Ent Pointer to the entry in the PML4/PDP/PD/PT - * \param NextLevel Pointer to contents of the entry - * \param Addr Dest address - * \note Used in COW - */ -void MM_int_ClonePageEnt( Uint64 *Ent, void *NextLevel, tVAddr Addr, int bTable ) -{ - tPAddr curpage = *Ent & PADDR_MASK; - int bCopied = 0; - - if( MM_GetRefCount( curpage ) <= 0 ) { - Log_KernelPanic("MMVirt", "Page %P still marked COW, but unreferenced", curpage); - } - if( MM_GetRefCount( curpage ) == 1 ) - { - *Ent &= ~PF_COW; - *Ent |= PF_PRESENT|PF_WRITE; - #if TRACE_COW - Log_Debug("MMVirt", "COW ent at %p (%p) only %P", Ent, NextLevel, curpage); - #endif - } - else - { - void *tmp; - tPAddr paddr; - - if( !(paddr = MM_AllocPhys()) ) { - Threads_SegFault(Addr); - return ; - } - - ASSERT(paddr != curpage); - - tmp = (void*)MM_MapTemp(paddr); - memcpy( tmp, NextLevel, 0x1000 ); - MM_FreeTemp( (tVAddr)tmp ); - - #if TRACE_COW - Log_Debug("MMVirt", "COW ent at %p (%p) from %P to %P", Ent, NextLevel, curpage, paddr); - #endif - - MM_DerefPhys( curpage ); - *Ent &= PF_USER; - *Ent |= paddr|PF_PRESENT|PF_WRITE; - - bCopied = 1; - } - INVLPG( (tVAddr)NextLevel ); - - // Mark COW on contents if it's a PDPT, Dir or Table - if(bTable) - { - Uint64 *dp = NextLevel; - int i; - for( i = 0; i < 512; i ++ ) - { - if( !(dp[i] & PF_PRESENT) ) - continue; - - if( bCopied ) - MM_RefPhys( dp[i] & PADDR_MASK ); - if( dp[i] & PF_WRITE ) { - dp[i] &= ~PF_WRITE; - dp[i] |= PF_COW; - } - } - } -} - -/* - * \brief Called on a page fault - */ -int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs) -{ -// Log_Debug("MMVirt", "Addr = %p, ErrorCode = %x", Addr, ErrorCode); - - // Catch reserved bits first - if( ErrorCode & 0x8 ) - { - Log_Warning("MMVirt", "Reserved bits trashed!"); - Log_Warning("MMVirt", "PML4 Ent = %P", PAGEMAPLVL4(Addr>>39)); - if( !(PAGEMAPLVL4(Addr>>39) & PF_PRESENT) ) goto print_done; - Log_Warning("MMVirt", "PDP Ent = %P", PAGEDIRPTR(Addr>>30)); - if( !(PAGEDIRPTR(Addr>>30) & PF_PRESENT) ) goto print_done; - Log_Warning("MMVirt", "PDir Ent = %P", PAGEDIR(Addr>>21)); - if( !(PAGEDIR(Addr>>21) & PF_PRESENT) ) goto print_done; - Log_Warning("MMVirt", "PTable Ent = %P", PAGETABLE(Addr>>12)); - if( !(PAGETABLE(Addr>>12) & PF_PRESENT) ) goto print_done; - print_done: - - for(;;); - } - - // TODO: Implement Copy-on-Write - #if 1 - if( PAGEMAPLVL4(Addr>>39) & PF_PRESENT - && PAGEDIRPTR (Addr>>30) & PF_PRESENT - && PAGEDIR (Addr>>21) & PF_PRESENT - && PAGETABLE (Addr>>12) & PF_PRESENT ) - { - // PML4 Entry - if( PAGEMAPLVL4(Addr>>39) & PF_COW ) - { - tPAddr *dp = &PAGEDIRPTR((Addr>>39)*512); - MM_int_ClonePageEnt( &PAGEMAPLVL4(Addr>>39), dp, Addr, 1 ); -// MM_DumpTables(Addr>>39 << 39, (((Addr>>39) + 1) << 39) - 1); - } - // PDP Entry - if( PAGEDIRPTR(Addr>>30) & PF_COW ) - { - tPAddr *dp = &PAGEDIR( (Addr>>30)*512 ); - MM_int_ClonePageEnt( &PAGEDIRPTR(Addr>>30), dp, Addr, 1 ); -// MM_DumpTables(Addr>>30 << 30, (((Addr>>30) + 1) << 30) - 1); - } - // PD Entry - if( PAGEDIR(Addr>>21) & PF_COW ) - { - tPAddr *dp = &PAGETABLE( (Addr>>21)*512 ); - MM_int_ClonePageEnt( &PAGEDIR(Addr>>21), dp, Addr, 1 ); -// MM_DumpTables(Addr>>21 << 21, (((Addr>>21) + 1) << 21) - 1); - } - // PT Entry - if( PAGETABLE(Addr>>12) & PF_COW ) - { - MM_int_ClonePageEnt( &PAGETABLE(Addr>>12), (void*)(Addr & ~0xFFF), Addr, 0 ); - INVLPG( Addr & ~0xFFF ); - return 0; - } - } - #endif - - // If it was a user, tell the thread handler - if(ErrorCode & 4) { - Warning("User %s %s memory%s", - (ErrorCode&2?"write to":"read from"), - (ErrorCode&1?"bad/locked":"non-present"), - (ErrorCode&16?" (Instruction Fetch)":"") - ); - Warning("User Pagefault: Instruction at %04x:%p accessed %p", - Regs->CS, Regs->RIP, Addr); - __asm__ __volatile__ ("sti"); // Restart IRQs - Error_Backtrace(Regs->RIP, Regs->RBP); - Threads_SegFault(Addr); - return 0; - } - - // Kernel #PF - Debug_KernelPanic(); - // -- Check Error Code -- - if(ErrorCode & 8) - Warning("Reserved Bits Trashed!"); - else - { - Warning("Kernel %s %s memory%s", - (ErrorCode&2?"write to":"read from"), - (ErrorCode&1?"bad/locked":"non-present"), - (ErrorCode&16?" (Instruction Fetch)":"") - ); - } - - Log("Thread %i - Code at %p accessed %p", Threads_GetTID(), Regs->RIP, Addr); - // Print Stack Backtrace - Error_Backtrace(Regs->RIP, Regs->RBP); - - MM_DumpTables(0, -1); - - return 1; -} - -void MM_int_DumpTablesEnt(tVAddr RangeStart, size_t Length, tPAddr Expected) -{ - #define CANOICAL(addr) ((addr)&0x800000000000?(addr)|0xFFFF000000000000:(addr)) - LogF("%016llx => ", CANOICAL(RangeStart)); -// LogF("%6llx %6llx %6llx %016llx => ", -// MM_GetPhysAddr( (tVAddr)&PAGEDIRPTR(RangeStart>>30) ), -// MM_GetPhysAddr( (tVAddr)&PAGEDIR(RangeStart>>21) ), -// MM_GetPhysAddr( (tVAddr)&PAGETABLE(RangeStart>>12) ), -// CANOICAL(RangeStart) -// ); - if( gMM_ZeroPage && (PAGETABLE(RangeStart>>12) & PADDR_MASK) == gMM_ZeroPage ) - LogF("%13s", "zero" ); - else - LogF("%13llx", PAGETABLE(RangeStart>>12) & PADDR_MASK ); - LogF(" : 0x%6llx (%c%c%c%c%c%c)\r\n", - Length, - (Expected & PF_GLOBAL ? 'G' : '-'), - (Expected & PF_NX ? '-' : 'x'), - (Expected & PF_PAGED ? 'p' : '-'), - (Expected & PF_COW ? 'C' : '-'), - (Expected & PF_USER ? 'U' : '-'), - (Expected & PF_WRITE ? 'W' : '-') - ); - #undef CANOICAL -} - -/** - * \brief Dumps the layout of the page tables - */ -void MM_DumpTables(tVAddr Start, tVAddr End) -{ - const tPAddr FIXED_BITS = PF_PRESENT|PF_WRITE|PF_USER|PF_COW|PF_PAGED|PF_NX|PF_GLOBAL; - const tPAddr CHANGEABLE_BITS = ~FIXED_BITS & 0xFFF; - const tPAddr MASK = ~CHANGEABLE_BITS; // Physical address and access bits - tVAddr rangeStart = 0; - tPAddr expected = CHANGEABLE_BITS; // CHANGEABLE_BITS is used because it's not a vaild value - tVAddr curPos; - Uint page; - tPAddr expected_pml4 = PF_WRITE|PF_USER; - tPAddr expected_pdp = PF_WRITE|PF_USER; - tPAddr expected_pd = PF_WRITE|PF_USER; - - Log("Table Entries: (%p to %p)", Start, End); - - End &= (1L << 48) - 1; - - Start >>= 12; End >>= 12; - - for(page = Start, curPos = Start<<12; - page < End; - curPos += 0x1000, page++) - { - //Debug("&PAGEMAPLVL4(%i page>>27) = %p", page>>27, &PAGEMAPLVL4(page>>27)); - //Debug("&PAGEDIRPTR(%i page>>18) = %p", page>>18, &PAGEDIRPTR(page>>18)); - //Debug("&PAGEDIR(%i page>>9) = %p", page>>9, &PAGEDIR(page>>9)); - //Debug("&PAGETABLE(%i page) = %p", page, &PAGETABLE(page)); - - // End of a range - if(!(PAGEMAPLVL4(page>>27) & PF_PRESENT) - || (PAGEMAPLVL4(page>>27) & FIXED_BITS) != expected_pml4 - || !(PAGEDIRPTR(page>>18) & PF_PRESENT) - || (PAGEDIRPTR(page>>18) & FIXED_BITS) != expected_pdp - || !(PAGEDIR(page>>9) & PF_PRESENT) - || (PAGEDIR(page>>9) & FIXED_BITS) != expected_pd - || !(PAGETABLE(page) & PF_PRESENT) - || (PAGETABLE(page) & MASK) != expected) - { - if(expected != CHANGEABLE_BITS) - { - // Merge - expected &= expected_pml4 | ~(PF_WRITE|PF_USER); - expected &= expected_pdp | ~(PF_WRITE|PF_USER); - expected &= expected_pd | ~(PF_WRITE|PF_USER); - expected |= expected_pml4 & PF_NX; - expected |= expected_pdp & PF_NX; - expected |= expected_pd & PF_NX; - Log("expected (pml4 = %x, pdp = %x, pd = %x)", - expected_pml4, expected_pdp, expected_pd); - // Dump - MM_int_DumpTablesEnt( rangeStart, curPos - rangeStart, expected ); - expected = CHANGEABLE_BITS; - } - - if( curPos == 0x800000000000L ) - curPos = 0xFFFF800000000000L; - - if( !(PAGEMAPLVL4(page>>27) & PF_PRESENT) ) { - page += (1 << 27) - 1; - curPos += (1L << 39) - 0x1000; - continue; - } - if( !(PAGEDIRPTR(page>>18) & PF_PRESENT) ) { - page += (1 << 18) - 1; - curPos += (1L << 30) - 0x1000; - continue; - } - if( !(PAGEDIR(page>>9) & PF_PRESENT) ) { - page += (1 << 9) - 1; - curPos += (1L << 21) - 0x1000; - continue; - } - if( !(PAGETABLE(page) & PF_PRESENT) ) continue; - - expected = (PAGETABLE(page) & MASK); - expected_pml4 = (PAGEMAPLVL4(page>>27) & FIXED_BITS); - expected_pdp = (PAGEDIRPTR (page>>18) & FIXED_BITS); - expected_pd = (PAGEDIR (page>> 9) & FIXED_BITS); - rangeStart = curPos; - } - if(gMM_ZeroPage && (expected & PADDR_MASK) == gMM_ZeroPage ) - expected = expected; - else if(expected != CHANGEABLE_BITS) - expected += 0x1000; - } - - if(expected != CHANGEABLE_BITS) { - // Merge - - // Dump - MM_int_DumpTablesEnt( rangeStart, curPos - rangeStart, expected ); - expected = 0; - } -} - -/** - * \brief Get a pointer to a page entry - * \param Addr Virtual Address - * \param bTemp Use the Temporary fractal mapping - * \param bAllocate Allocate entries - * \param bLargePage Request a large page - * \param Pointer Location to place the calculated pointer - * \return Page size, or -ve on error - */ -int MM_GetPageEntryPtr(tVAddr Addr, BOOL bTemp, BOOL bAllocate, BOOL bLargePage, tPAddr **Pointer) -{ - tPAddr *pmlevels[4]; - tPAddr tmp; - int i, size; - - #define BITMASK(bits) ( (1LL << (bits))-1 ) - - if( bTemp ) - { - pmlevels[3] = &TMPTABLE(0); // Page Table - pmlevels[2] = &TMPDIR(0); // PDIR - pmlevels[1] = &TMPDIRPTR(0); // PDPT - pmlevels[0] = &TMPMAPLVL4(0); // PML4 - } - else - { - pmlevels[3] = (void*)MM_FRACTAL_BASE; // Page Table - pmlevels[2] = &pmlevels[3][(MM_FRACTAL_BASE>>12)&BITMASK(VIRT_BITS-12)]; // PDIR - pmlevels[1] = &pmlevels[2][(MM_FRACTAL_BASE>>21)&BITMASK(VIRT_BITS-21)]; // PDPT - pmlevels[0] = &pmlevels[1][(MM_FRACTAL_BASE>>30)&BITMASK(VIRT_BITS-30)]; // PML4 - } - - // Mask address - Addr &= (1ULL << 48)-1; - - for( size = 39, i = 0; size > 12; size -= 9, i ++ ) - { - Uint64 *ent = &pmlevels[i][Addr >> size]; -// INVLPG( &pmlevels[i][ (Addr >> ADDR_SIZES[i]) & - - // Check for a free large page slot - // TODO: Better support with selectable levels - if( (Addr & ((1ULL << size)-1)) == 0 && bLargePage ) - { - if(Pointer) *Pointer = ent; - return size; - } - // Allocate an entry if required - if( !(*ent & PF_PRESENT) ) - { - if( !bAllocate ) return -4; // If allocation is not requested, error - if( !(tmp = MM_AllocPhys()) ) return -2; - *ent = tmp | 3; - if( Addr < 0x800000000000 ) - *ent |= PF_USER; - INVLPG( &pmlevels[i+1][ (Addr>>size)*512 ] ); - memset( &pmlevels[i+1][ (Addr>>size)*512 ], 0, 0x1000 ); - LOG("Init PML%i ent 0x%x %p with %P (*ent = %P)", 4 - i, - Addr>>size, (Addr>>size) << size, tmp, *ent); - } - // Catch large pages - else if( *ent & PF_LARGE ) - { - // Alignment - if( (Addr & ((1ULL << size)-1)) != 0 ) return -3; - if(Pointer) *Pointer = ent; - return size; // Large page warning - } - } - - // And, set the page table entry - if(Pointer) *Pointer = &pmlevels[i][Addr >> size]; - return size; -} - -/** - * \brief Map a physical page to a virtual one - * \param VAddr Target virtual address - * \param PAddr Physical address of page - * \param bTemp Use tempoary mappings - * \param bLarge Treat as a large page - */ -int MM_MapEx(tVAddr VAddr, tPAddr PAddr, BOOL bTemp, BOOL bLarge) -{ - tPAddr *ent; - int rv; - - ENTER("pVAddr PPAddr", VAddr, PAddr); - - // Get page pointer (Allow allocating) - rv = MM_GetPageEntryPtr(VAddr, bTemp, 1, bLarge, &ent); - if(rv < 0) LEAVE_RET('i', 0); - - if( *ent & 1 ) LEAVE_RET('i', 0); - - *ent = PAddr | 3; - - if( VAddr < 0x800000000000 ) - *ent |= PF_USER; - - INVLPG( VAddr ); - - LEAVE('i', 1); - return 1; -} - -/** - * \brief Map a physical page to a virtual one - * \param VAddr Target virtual address - * \param PAddr Physical address of page - */ -int MM_Map(tVAddr VAddr, tPAddr PAddr) -{ - return MM_MapEx(VAddr, PAddr, 0, 0); -} - -/** - * \brief Removed a mapped page - */ -void MM_Unmap(tVAddr VAddr) -{ - // Check PML4 - if( !(PAGEMAPLVL4(VAddr >> 39) & 1) ) return ; - // Check PDP - if( !(PAGEDIRPTR(VAddr >> 30) & 1) ) return ; - // Check Page Dir - if( !(PAGEDIR(VAddr >> 21) & 1) ) return ; - - PAGETABLE(VAddr >> PTAB_SHIFT) = 0; - INVLPG( VAddr ); -} - -/** - * \brief Allocate a block of memory at the specified virtual address - */ -tPAddr MM_Allocate(tVAddr VAddr) -{ - tPAddr ret; - - ENTER("xVAddr", VAddr); - - // Ensure the tables are allocated before the page (keeps things neat) - MM_GetPageEntryPtr(VAddr, 0, 1, 0, NULL); - - // Allocate the page - ret = MM_AllocPhys(); - LOG("ret = %x", ret); - if(!ret) LEAVE_RET('i', 0); - - if( !MM_Map(VAddr, ret) ) - { - Warning("MM_Allocate: Unable to map. Strange, we should have errored earlier"); - MM_DerefPhys(ret); - LEAVE('i'); - return 0; - } - - LEAVE('X', ret); - return ret; -} - -tPAddr MM_AllocateZero(tVAddr VAddr) -{ - tPAddr ret = gMM_ZeroPage; - - MM_GetPageEntryPtr(VAddr, 0, 1, 0, NULL); - - if(!gMM_ZeroPage) { - ret = gMM_ZeroPage = MM_AllocPhys(); - MM_RefPhys(ret); // Don't free this please - MM_Map(VAddr, ret); - memset((void*)VAddr, 0, 0x1000); - } - else { - MM_Map(VAddr, ret); - } - MM_RefPhys(ret); // Refernce for this map - MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW); - return ret; -} - -/** - * \brief Deallocate a page at a virtual address - */ -void MM_Deallocate(tVAddr VAddr) -{ - tPAddr phys; - - phys = MM_GetPhysAddr(VAddr); - if(!phys) return ; - - MM_Unmap(VAddr); - - MM_DerefPhys(phys); -} - -/** - * \brief Get the page table entry of a virtual address - * \param Addr Virtual Address - * \param Phys Location to put the physical address - * \param Flags Flags on the entry (set to zero if unmapped) - * \return Size of the entry (in address bits) - 12 = 4KiB page - */ -int MM_GetPageEntry(tVAddr Addr, tPAddr *Phys, Uint *Flags) -{ - tPAddr *ptr; - int ret; - - if(!Phys || !Flags) return 0; - - ret = MM_GetPageEntryPtr(Addr, 0, 0, 0, &ptr); - if( ret < 0 ) return 0; - - *Phys = *ptr & PADDR_MASK; - *Flags = *ptr & 0xFFF; - return ret; -} - -/** - * \brief Get the physical address of a virtual location - */ -tPAddr MM_GetPhysAddr(tVAddr Addr) -{ - tPAddr *ptr; - int ret; - - ret = MM_GetPageEntryPtr(Addr, 0, 0, 0, &ptr); - if( ret < 0 ) return 0; - - if( !(*ptr & 1) ) return 0; - - return (*ptr & PADDR_MASK) | (Addr & 0xFFF); -} - -/** - * \brief Sets the flags on a page - */ -void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask) -{ - tPAddr *ent; - int rv; - - // Get pointer - rv = MM_GetPageEntryPtr(VAddr, 0, 0, 0, &ent); - if(rv < 0) return ; - - // Ensure the entry is valid - if( !(*ent & 1) ) return ; - - // Read-Only - if( Mask & MM_PFLAG_RO ) - { - if( Flags & MM_PFLAG_RO ) { - *ent &= ~PF_WRITE; - } - else { - *ent |= PF_WRITE; - } - } - - // Kernel - if( Mask & MM_PFLAG_KERNEL ) - { - if( Flags & MM_PFLAG_KERNEL ) { - *ent &= ~PF_USER; - } - else { - *ent |= PF_USER; - } - } - - // Copy-On-Write - if( Mask & MM_PFLAG_COW ) - { - if( Flags & MM_PFLAG_COW ) { - *ent &= ~PF_WRITE; - *ent |= PF_COW; - INVLPG_ALL(); - } - else { - *ent &= ~PF_COW; - *ent |= PF_WRITE; - } - } - - // Execute - if( Mask & MM_PFLAG_EXEC ) - { - if( Flags & MM_PFLAG_EXEC ) { - *ent &= ~PF_NX; - } - else { - *ent |= PF_NX; - } - } -} - -/** - * \brief Get the flags applied to a page - */ -Uint MM_GetFlags(tVAddr VAddr) -{ - tPAddr *ent; - int rv, ret = 0; - - rv = MM_GetPageEntryPtr(VAddr, 0, 0, 0, &ent); - if(rv < 0) return 0; - - if( !(*ent & 1) ) return 0; - - // Read-Only - if( !(*ent & PF_WRITE) ) ret |= MM_PFLAG_RO; - // Kernel - if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL; - // Copy-On-Write - if( *ent & PF_COW ) ret |= MM_PFLAG_COW; - // Execute - if( !(*ent & PF_NX) ) ret |= MM_PFLAG_EXEC; - - return ret; -} - -/** - * \brief Check if the provided buffer is valid - * \return Boolean valid - */ -int MM_IsValidBuffer(tVAddr Addr, size_t Size) -{ - int bIsUser; - Uint64 pml4, pdp, dir, tab; - - Size += Addr & (PAGE_SIZE-1); - Addr &= ~(PAGE_SIZE-1); - Addr &= ((1UL << 48)-1); // Clap to address space - - pml4 = Addr >> 39; - pdp = Addr >> 30; - dir = Addr >> 21; - tab = Addr >> 12; - - if( !(PAGEMAPLVL4(pml4) & 1) ) return 0; - if( !(PAGEDIRPTR(pdp) & 1) ) return 0; - if( !(PAGEDIR(dir) & 1) ) return 0; - if( !(PAGETABLE(tab) & 1) ) return 0; - - bIsUser = !!(PAGETABLE(tab) & PF_USER); - - while( Size >= PAGE_SIZE ) - { - if( (tab & 511) == 0 ) - { - dir ++; - if( ((dir >> 9) & 511) == 0 ) - { - pdp ++; - if( ((pdp >> 18) & 511) == 0 ) - { - pml4 ++; - if( !(PAGEMAPLVL4(pml4) & 1) ) return 0; - } - if( !(PAGEDIRPTR(pdp) & 1) ) return 0; - } - if( !(PAGEDIR(dir) & 1) ) return 0; - } - - if( !(PAGETABLE(tab) & 1) ) return 0; - if( bIsUser && !(PAGETABLE(tab) & PF_USER) ) return 0; - - tab ++; - Size -= PAGE_SIZE; - } - return 1; -} - -// --- Hardware Mappings --- -/** - * \brief Map a range of hardware pages - */ -tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number) -{ - tVAddr ret; - int num; - - //TODO: Add speedups (memory of first possible free) - for( ret = MM_HWMAP_BASE; ret < MM_HWMAP_TOP; ret += 0x1000 ) - { - for( num = Number; num -- && ret < MM_HWMAP_TOP; ret += 0x1000 ) - { - if( MM_GetPhysAddr(ret) != 0 ) break; - } - if( num >= 0 ) continue; - -// Log_Debug("MMVirt", "Mapping %i pages to %p (base %P)", Number, ret-Number*0x1000, PAddr); - - PAddr += 0x1000 * Number; - - while( Number -- ) - { - ret -= 0x1000; - PAddr -= 0x1000; - MM_Map(ret, PAddr); - MM_RefPhys(PAddr); - } - - return ret; - } - - Log_Error("MM", "MM_MapHWPages - No space for %i pages", Number); - return 0; -} - -/** - * \brief Free a range of hardware pages - */ -void MM_UnmapHWPages(tVAddr VAddr, Uint Number) -{ -// Log_KernelPanic("MM", "TODO: Implement MM_UnmapHWPages"); - while( Number -- ) - { - MM_DerefPhys( MM_GetPhysAddr(VAddr) ); - MM_Unmap(VAddr); - VAddr += 0x1000; - } -} - - -/** - * \fn tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr) - * \brief Allocates DMA physical memory - * \param Pages Number of pages required - * \param MaxBits Maximum number of bits the physical address can have - * \param PhysAddr Pointer to the location to place the physical address allocated - * \return Virtual address allocate - */ -tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr) -{ - tPAddr phys; - tVAddr ret; - - // Sanity Check - if(MaxBits < 12 || !PhysAddr) return 0; - - // Fast Allocate - if(Pages == 1 && MaxBits >= PHYS_BITS) - { - phys = MM_AllocPhys(); - *PhysAddr = phys; - ret = MM_MapHWPages(phys, 1); - MM_DerefPhys(phys); - return ret; - } - - // Slow Allocate - phys = MM_AllocPhysRange(Pages, MaxBits); - // - Was it allocated? - if(phys == 0) return 0; - - // Allocated successfully, now map - ret = MM_MapHWPages(phys, Pages); - // MapHWPages references the pages, so deref them back down to 1 - for(;Pages--;phys+=0x1000) - MM_DerefPhys(phys); - if( ret == 0 ) { - // If it didn't map, free then return 0 - return 0; - } - - *PhysAddr = phys; - return ret; -} - -// --- Tempory Mappings --- -tVAddr MM_MapTemp(tPAddr PAddr) -{ - const int max_slots = (MM_TMPMAP_END - MM_TMPMAP_BASE) / PAGE_SIZE; - tVAddr ret = MM_TMPMAP_BASE; - int i; - - for( i = 0; i < max_slots; i ++, ret += PAGE_SIZE ) - { - tPAddr *ent; - if( MM_GetPageEntryPtr( ret, 0, 1, 0, &ent) < 0 ) { - continue ; - } - - if( *ent & 1 ) - continue ; - - *ent = PAddr | 3; - MM_RefPhys(PAddr); - INVLPG(ret); - return ret; - } - return 0; -} - -void MM_FreeTemp(tVAddr VAddr) -{ - MM_Deallocate(VAddr); - return ; -} - - -// --- Address Space Clone -- -tPAddr MM_Clone(void) -{ - tPAddr ret; - int i; - tVAddr kstackbase; - - // #1 Create a copy of the PML4 - ret = MM_AllocPhys(); - if(!ret) return 0; - - // #2 Alter the fractal pointer - Mutex_Acquire(&glMM_TempFractalLock); - TMPCR3() = ret | 3; - INVLPG_ALL(); - - // #3 Set Copy-On-Write to all user pages - if( Threads_GetPID() != 0 ) - { - for( i = 0; i < 256; i ++) - { - if( PAGEMAPLVL4(i) & PF_WRITE ) { - PAGEMAPLVL4(i) |= PF_COW; - PAGEMAPLVL4(i) &= ~PF_WRITE; - } - - TMPMAPLVL4(i) = PAGEMAPLVL4(i); -// Log_Debug("MM", "TMPMAPLVL4(%i) = 0x%016llx", i, TMPMAPLVL4(i)); - if( !(TMPMAPLVL4(i) & PF_PRESENT) ) continue ; - - MM_RefPhys( TMPMAPLVL4(i) & PADDR_MASK ); - } - } - else - { - for( i = 0; i < 256; i ++ ) - { - TMPMAPLVL4(i) = 0; - } - } - - // #4 Map in kernel pages - for( i = 256; i < 512; i ++ ) - { - // Skip addresses: - // 320 0xFFFFA.... - Kernel Stacks - if( i == MM_KSTACK_BASE>>39 ) continue; - // 509 0xFFFFFE0.. - Fractal mapping - if( i == MM_FRACTAL_BASE>>39 ) continue; - // 510 0xFFFFFE8.. - Temp fractal mapping - if( i == MM_TMPFRAC_BASE>>39 ) continue; - - TMPMAPLVL4(i) = PAGEMAPLVL4(i); - if( TMPMAPLVL4(i) & 1 ) - MM_RefPhys( TMPMAPLVL4(i) & PADDR_MASK ); - } - - // Mark Per-Process data as COW - TMPMAPLVL4(MM_PPD_BASE>>39) |= PF_COW; - TMPMAPLVL4(MM_PPD_BASE>>39) &= ~PF_WRITE; - - // #5 Set fractal mapping - TMPMAPLVL4(MM_FRACTAL_BASE>>39) = ret | 3; // Main - TMPMAPLVL4(MM_TMPFRAC_BASE>>39) = 0; // Temp - - // #6 Create kernel stack - // tThread->KernelStack is the top - // There is 1 guard page below the stack - kstackbase = Proc_GetCurThread()->KernelStack - KERNEL_STACK_SIZE; - - // Clone stack - TMPMAPLVL4(MM_KSTACK_BASE >> PML4_SHIFT) = 0; - for( i = 1; i < KERNEL_STACK_SIZE/0x1000; i ++ ) - { - tPAddr phys = MM_AllocPhys(); - tVAddr tmpmapping; - MM_MapEx(kstackbase+i*0x1000, phys, 1, 0); - - tmpmapping = MM_MapTemp(phys); - if( MM_GetPhysAddr( kstackbase+i*0x1000 ) ) - memcpy((void*)tmpmapping, (void*)(kstackbase+i*0x1000), 0x1000); - else - memset((void*)tmpmapping, 0, 0x1000); -// if( i == 0xF ) -// Debug_HexDump("MM_Clone: *tmpmapping = ", (void*)tmpmapping, 0x1000); - MM_FreeTemp(tmpmapping); - } - -// MAGIC_BREAK(); - - // #7 Return - TMPCR3() = 0; - INVLPG_ALL(); - Mutex_Release(&glMM_TempFractalLock); -// Log("MM_Clone: RETURN %P", ret); - return ret; -} - -void MM_int_ClearTableLevel(tVAddr VAddr, int LevelBits, int MaxEnts) -{ - Uint64 * const table_bases[] = {&PAGETABLE(0), &PAGEDIR(0), &PAGEDIRPTR(0), &PAGEMAPLVL4(0)}; - Uint64 *table = table_bases[(LevelBits-12)/9] + (VAddr >> LevelBits); - int i; -// Log("MM_int_ClearTableLevel: (VAddr=%p, LevelBits=%i, MaxEnts=%i)", VAddr, LevelBits, MaxEnts); - for( i = 0; i < MaxEnts; i ++ ) - { - // Skip non-present tables - if( !(table[i] & PF_PRESENT) ) { - table[i] = 0; - continue ; - } - - if( (table[i] & PF_COW) && MM_GetRefCount(table[i] & PADDR_MASK) > 1 ) { - MM_DerefPhys(table[i] & PADDR_MASK); - table[i] = 0; - continue ; - } - // Clear table contents (if it is a table) - if( LevelBits > 12 ) - MM_int_ClearTableLevel(VAddr + ((tVAddr)i << LevelBits), LevelBits-9, 512); - MM_DerefPhys(table[i] & PADDR_MASK); - table[i] = 0; - } -} - -void MM_ClearUser(void) -{ - MM_int_ClearTableLevel(0, 39, 256); -} - -tVAddr MM_NewWorkerStack(void *StackData, size_t StackSize) -{ - tVAddr ret; - tPAddr phys; - int i; - - // #1 Set temp fractal to PID0 - Mutex_Acquire(&glMM_TempFractalLock); - TMPCR3() = ((tPAddr)gInitialPML4 - KERNEL_BASE) | 3; - INVLPG_ALL(); - - // #2 Scan for a free stack addresss < 2^47 - for(ret = 0x100000; ret < (1ULL << 47); ret += KERNEL_STACK_SIZE) - { - tPAddr *ptr; - if( MM_GetPageEntryPtr(ret, 1, 0, 0, &ptr) <= 0 ) break; - if( !(*ptr & 1) ) break; - } - if( ret >= (1ULL << 47) ) { - Mutex_Release(&glMM_TempFractalLock); - return 0; - } - - // #3 Map all save the last page in the range - // - This acts as as guard page - MM_GetPageEntryPtr(ret, 1, 1, 0, NULL); // Make sure tree is allocated - for( i = 0; i < KERNEL_STACK_SIZE/0x1000 - 1; i ++ ) - { - phys = MM_AllocPhys(); - if(!phys) { - // TODO: Clean up - Log_Error("MM", "MM_NewWorkerStack - Unable to allocate page"); - return 0; - } - MM_MapEx(ret + i*0x1000, phys, 1, 0); - MM_SetFlags(ret + i*0x1000, MM_PFLAG_KERNEL|MM_PFLAG_RO, MM_PFLAG_KERNEL); - } - - // Copy data - if( StackSize > 0x1000 ) { - Log_Error("MM", "MM_NewWorkerStack: StackSize(0x%x) > 0x1000, cbf handling", StackSize); - } - else { - tVAddr tmp_addr, dest; - tmp_addr = MM_MapTemp(phys); - dest = tmp_addr + (0x1000 - StackSize); - memcpy( (void*)dest, StackData, StackSize ); - Log_Debug("MM", "MM_NewWorkerStack: %p->%p %i bytes (i=%i)", StackData, dest, StackSize, i); - Log_Debug("MM", "MM_NewWorkerStack: ret = %p", ret); - MM_FreeTemp(tmp_addr); - } - - TMPCR3() = 0; - Mutex_Release(&glMM_TempFractalLock); - - return ret + i*0x1000; -} - -/** - * \brief Allocate a new kernel stack - */ -tVAddr MM_NewKStack(void) -{ - tVAddr base = MM_KSTACK_BASE; - Uint i; - for( ; base < MM_KSTACK_TOP; base += KERNEL_STACK_SIZE ) - { - if(MM_GetPhysAddr(base+KERNEL_STACK_SIZE-0x1000) != 0) - continue; - - //Log("MM_NewKStack: Found one at %p", base + KERNEL_STACK_SIZE); - for( i = 0x1000; i < KERNEL_STACK_SIZE; i += 0x1000) - { - if( !MM_Allocate(base+i) ) - { - Log_Warning("MM", "MM_NewKStack - Allocation failed"); - for( i -= 0x1000; i; i -= 0x1000) - MM_Deallocate(base+i); - return 0; - } - } - - return base + KERNEL_STACK_SIZE; - } - Log_Warning("MM", "MM_NewKStack - No address space left\n"); - return 0; -} diff --git a/Kernel/arch/x86_64/pci.c b/Kernel/arch/x86_64/pci.c deleted file mode 120000 index cac29a8f..00000000 --- a/Kernel/arch/x86_64/pci.c +++ /dev/null @@ -1 +0,0 @@ -../x86/pci.c \ No newline at end of file diff --git a/Kernel/arch/x86_64/proc.asm b/Kernel/arch/x86_64/proc.asm deleted file mode 100644 index f540b3b6..00000000 --- a/Kernel/arch/x86_64/proc.asm +++ /dev/null @@ -1,148 +0,0 @@ -; -; -; -%include "arch/x86_64/include/common.inc.asm" -[BITS 64] -[section .text] - -[extern Threads_Exit] - -[global GetRIP] -GetRIP: - mov rax, [rsp] - ret - -[global NewTaskHeader] -NewTaskHeader: - ; [rsp+0x00]: Thread - ; [rsp+0x08]: Function - ; [rsp+0x10]: Argument - - mov rdi, [rsp+0x10] - mov rax, [rsp+0x8] - add rsp, 0x10 ; Reclaim stack space (thread/fcn) - xchg bx, bx - call rax - - ; Quit thread with RAX as the return code - xor rdi, rdi - mov rsi, rax - call Threads_Exit - -.hlt: - jmp .hlt - -[extern MM_Clone] -[extern MM_DumpTables] -[global Proc_CloneInt] -Proc_CloneInt: - PUSH_GPR - ; Save RSP - mov [rdi], rsp - call MM_Clone - ; Save CR3 - mov rsi, [rsp+0x30] ; Saved version of RSI - mov [rsi], rax - ; Undo the PUSH_GPR - add rsp, 0x80 - mov rax, .newTask - ret -.newTask: -; mov rdi, 0 -; mov rsi, 0x800000000000 -; call MM_DumpTables - POP_GPR - xor eax, eax - ret - -[global SaveState] -SaveState: - ; Save regs to RSI - add rsi, 0x80 - SAVE_GPR rsi - ; Save return addr - mov rax, [rsp] - mov [rsi], rax - ; Return RSI as the RSP value - sub rsi, 0x80 - mov [rdi], rsi - ; Check for - mov rax, .restore - ret -.restore: - ; RSP = RSI now - POP_GPR - mov rax, [rsp] - mov rsp, [rsp-0x60] ; Restore RSP from the saved value - mov [rsp], rax ; Restore return address - xor eax, eax - ret - -[global SwitchTasks] -; rdi = New RSP -; rsi = Old RSP save loc -; rdx = New RIP -; rcx = Old RIP save loc -; r8 = CR3 -SwitchTasks: - PUSH_GPR - - ; Save state RIP and RSP - lea rax, [rel .restore] - mov [rcx], rax - mov [rsi], rsp - - ; Change CR3 if requested - test r8, r8 - jz .setState - mov cr3, r8 - - ; Make sure the stack is valid before jumping - invlpg [rdi-0x1000] - invlpg [rdi] - invlpg [rdi+0x1000] - - ; Go to new state -.setState: - mov rsp, rdi - jmp rdx - - ; Restore point for saved state -.restore: - POP_GPR - xor eax, eax ; Return zero - ret - -[global Proc_InitialiseSSE] -Proc_InitialiseSSE: - mov rax, cr4 - or ax, (1 << 9)|(1 << 10) ; Set OSFXSR and OSXMMEXCPT - mov cr4, rax - mov rax, cr0 - and ax, ~(1 << 2) ; Clear EM - or rax, (1 << 1) ; Set MP - mov rax, cr0 - ret -[global Proc_DisableSSE] -Proc_DisableSSE: - mov rax, cr0 - or ax, 1 << 3 ; Set TS - mov cr0, rax - ret -[global Proc_EnableSSE] -Proc_EnableSSE: - mov rax, cr0 - and ax, ~(1 << 3) ; Clear TS - mov cr0, rax - ret - -[global Proc_SaveSSE] -Proc_SaveSSE: - fxsave [rdi] - ret -[global Proc_RestoreSSE] -Proc_RestoreSSE: - fxrstor [rdi] - ret - -; vim: ft=nasm diff --git a/Kernel/arch/x86_64/proc.c b/Kernel/arch/x86_64/proc.c deleted file mode 100644 index 5f71a45f..00000000 --- a/Kernel/arch/x86_64/proc.c +++ /dev/null @@ -1,827 +0,0 @@ -/* - * Acess2 x86_64 port - * proc.c - */ -#include -#include -#include -#include -#include -#include -#include -#if USE_MP -# include -#endif -#include -#include - -// === FLAGS === -#define DEBUG_TRACE_SWITCH 0 -#define BREAK_ON_SWITCH 0 // Break into bochs debugger on a task switch - -// === CONSTANTS === - -// === TYPES === -typedef struct sCPU -{ - Uint8 APICID; - Uint8 State; // 0: Unavaliable, 1: Idle, 2: Active - Uint16 Resvd; - tThread *Current; - tThread *IdleThread; -} tCPU; - -// === IMPORTS === -extern tGDT gGDT[]; -extern void APStartup(void); // 16-bit AP startup code - -extern Uint GetRIP(void); // start.asm -extern Uint SaveState(Uint *RSP, Uint *Regs); -extern Uint Proc_CloneInt(Uint *RSP, Uint *CR3); -extern void NewTaskHeader(void); // Actually takes cdecl args -extern void Proc_InitialiseSSE(void); -extern void Proc_SaveSSE(Uint DestPtr); -extern void Proc_DisableSSE(void); - -extern Uint64 gInitialPML4[512]; // start.asm -extern int giNumCPUs; -extern int giNextTID; -extern int giTotalTickets; -extern int giNumActiveThreads; -extern tThread gThreadZero; -extern tProcess gProcessZero; -extern void Threads_Dump(void); -extern void Proc_ReturnToUser(tVAddr Handler, tVAddr KStackTop, int Argument); -extern void Time_UpdateTimestamp(void); -extern void SwitchTasks(Uint NewSP, Uint *OldSP, Uint NewIP, Uint *OldIO, Uint CR3); - -// === PROTOTYPES === -//void ArchThreads_Init(void); -#if USE_MP -void MP_StartAP(int CPU); -void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode); -#endif -void Proc_IdleTask(void *unused); -//void Proc_Start(void); -//tThread *Proc_GetCurThread(void); -// int Proc_NewKThread(void (*Fcn)(void*), void *Data); -// int Proc_Clone(Uint *Err, Uint Flags); -// int Proc_SpawnWorker(void); -Uint Proc_MakeUserStack(void); -//void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize); -void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) NORETURN; - int Proc_Demote(Uint *Err, int Dest, tRegs *Regs); -//void Proc_CallFaultHandler(tThread *Thread); -//void Proc_DumpThreadCPUState(tThread *Thread); -//void Proc_Reschedule(void); -void Proc_Scheduler(int CPU, Uint RSP, Uint RIP); - -// === GLOBALS === -//!\brief Used by desctab.asm in SyscallStub -const int ci_offsetof_tThread_KernelStack = offsetof(tThread, KernelStack); -// --- Multiprocessing --- -#if USE_MP -volatile int giNumInitingCPUs = 0; -tMPInfo *gMPFloatPtr = NULL; -tAPIC *gpMP_LocalAPIC = NULL; -Uint8 gaAPIC_to_CPU[256] = {0}; -#endif -tCPU gaCPUs[MAX_CPUS]; -tTSS *gTSSs = NULL; -tTSS gTSS0 = {0}; -// --- Error Recovery --- -Uint32 gaDoubleFaultStack[1024]; - -// === CODE === -/** - * \fn void ArchThreads_Init(void) - * \brief Starts the process scheduler - */ -void ArchThreads_Init(void) -{ - Uint pos = 0; - - #if USE_MP - tMPTable *mptable; - - // Mark BSP as active - gaCPUs[0].State = 2; - - // -- Initialise Multiprocessing - // Find MP Floating Table - // - EBDA/Last 1Kib (640KiB) - for(pos = KERNEL_BASE|0x9F000; pos < (KERNEL_BASE|0xA0000); pos += 16) { - if( *(Uint*)(pos) == MPPTR_IDENT ) { - Log("Possible %p", pos); - if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue; - gMPFloatPtr = (void*)pos; - break; - } - } - // - Last KiB (512KiB base mem) - if(!gMPFloatPtr) { - for(pos = KERNEL_BASE|0x7F000; pos < (KERNEL_BASE|0x80000); pos += 16) { - if( *(Uint*)(pos) == MPPTR_IDENT ) { - Log("Possible %p", pos); - if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue; - gMPFloatPtr = (void*)pos; - break; - } - } - } - // - BIOS ROM - if(!gMPFloatPtr) { - for(pos = KERNEL_BASE|0xE0000; pos < (KERNEL_BASE|0x100000); pos += 16) { - if( *(Uint*)(pos) == MPPTR_IDENT ) { - Log("Possible %p", pos); - if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue; - gMPFloatPtr = (void*)pos; - break; - } - } - } - - // If the MP Table Exists, parse it - if(gMPFloatPtr) - { - int i; - tMPTable_Ent *ents; - Log("gMPFloatPtr = %p", gMPFloatPtr); - Log("*gMPFloatPtr = {"); - Log("\t.Sig = 0x%08x", gMPFloatPtr->Sig); - Log("\t.MPConfig = 0x%08x", gMPFloatPtr->MPConfig); - Log("\t.Length = 0x%02x", gMPFloatPtr->Length); - Log("\t.Version = 0x%02x", gMPFloatPtr->Version); - Log("\t.Checksum = 0x%02x", gMPFloatPtr->Checksum); - Log("\t.Features = [0x%02x,0x%02x,0x%02x,0x%02x,0x%02x]", - gMPFloatPtr->Features[0], gMPFloatPtr->Features[1], - gMPFloatPtr->Features[2], gMPFloatPtr->Features[3], - gMPFloatPtr->Features[4] - ); - Log("}"); - - mptable = (void*)( KERNEL_BASE|gMPFloatPtr->MPConfig ); - Log("mptable = %p", mptable); - Log("*mptable = {"); - Log("\t.Sig = 0x%08x", mptable->Sig); - Log("\t.BaseTableLength = 0x%04x", mptable->BaseTableLength); - Log("\t.SpecRev = 0x%02x", mptable->SpecRev); - Log("\t.Checksum = 0x%02x", mptable->Checksum); - Log("\t.OEMID = '%8c'", mptable->OemID); - Log("\t.ProductID = '%8c'", mptable->ProductID); - Log("\t.OEMTablePtr = %p'", mptable->OEMTablePtr); - Log("\t.OEMTableSize = 0x%04x", mptable->OEMTableSize); - Log("\t.EntryCount = 0x%04x", mptable->EntryCount); - Log("\t.LocalAPICMemMap = 0x%08x", mptable->LocalAPICMemMap); - Log("\t.ExtendedTableLen = 0x%04x", mptable->ExtendedTableLen); - Log("\t.ExtendedTableChecksum = 0x%02x", mptable->ExtendedTableChecksum); - Log("}"); - - gpMP_LocalAPIC = (void*)MM_MapHWPage(mptable->LocalAPICMemMap, 1); - - ents = mptable->Entries; - giNumCPUs = 0; - - for( i = 0; i < mptable->EntryCount; i ++ ) - { - int entSize = 0; - switch( ents->Type ) - { - case 0: // Processor - entSize = 20; - Log("%i: Processor", i); - Log("\t.APICID = %i", ents->Proc.APICID); - Log("\t.APICVer = 0x%02x", ents->Proc.APICVer); - Log("\t.CPUFlags = 0x%02x", ents->Proc.CPUFlags); - Log("\t.CPUSignature = 0x%08x", ents->Proc.CPUSignature); - Log("\t.FeatureFlags = 0x%08x", ents->Proc.FeatureFlags); - - - if( !(ents->Proc.CPUFlags & 1) ) { - Log("DISABLED"); - break; - } - - // Check if there is too many processors - if(giNumCPUs >= MAX_CPUS) { - giNumCPUs ++; // If `giNumCPUs` > MAX_CPUS later, it will be clipped - break; - } - - // Initialise CPU Info - gaAPIC_to_CPU[ents->Proc.APICID] = giNumCPUs; - gaCPUs[giNumCPUs].APICID = ents->Proc.APICID; - gaCPUs[giNumCPUs].State = 0; - giNumCPUs ++; - - // Send IPI - if( !(ents->Proc.CPUFlags & 2) ) - { - MP_StartAP( giNumCPUs-1 ); - } - - break; - case 1: // Bus - entSize = 8; - Log("%i: Bus", i); - Log("\t.ID = %i", ents->Bus.ID); - Log("\t.TypeString = '%6c'", ents->Bus.TypeString); - break; - case 2: // I/O APIC - entSize = 8; - Log("%i: I/O APIC", i); - Log("\t.ID = %i", ents->IOAPIC.ID); - Log("\t.Version = 0x%02x", ents->IOAPIC.Version); - Log("\t.Flags = 0x%02x", ents->IOAPIC.Flags); - Log("\t.Addr = 0x%08x", ents->IOAPIC.Addr); - break; - case 3: // I/O Interrupt Assignment - entSize = 8; - Log("%i: I/O Interrupt Assignment", i); - Log("\t.IntType = %i", ents->IOInt.IntType); - Log("\t.Flags = 0x%04x", ents->IOInt.Flags); - Log("\t.SourceBusID = 0x%02x", ents->IOInt.SourceBusID); - Log("\t.SourceBusIRQ = 0x%02x", ents->IOInt.SourceBusIRQ); - Log("\t.DestAPICID = 0x%02x", ents->IOInt.DestAPICID); - Log("\t.DestAPICIRQ = 0x%02x", ents->IOInt.DestAPICIRQ); - break; - case 4: // Local Interrupt Assignment - entSize = 8; - Log("%i: Local Interrupt Assignment", i); - Log("\t.IntType = %i", ents->LocalInt.IntType); - Log("\t.Flags = 0x%04x", ents->LocalInt.Flags); - Log("\t.SourceBusID = 0x%02x", ents->LocalInt.SourceBusID); - Log("\t.SourceBusIRQ = 0x%02x", ents->LocalInt.SourceBusIRQ); - Log("\t.DestLocalAPICID = 0x%02x", ents->LocalInt.DestLocalAPICID); - Log("\t.DestLocalAPICIRQ = 0x%02x", ents->LocalInt.DestLocalAPICIRQ); - break; - default: - Log("%i: Unknown (%i)", i, ents->Type); - break; - } - ents = (void*)( (Uint)ents + entSize ); - } - - if( giNumCPUs > MAX_CPUS ) { - Warning("Too many CPUs detected (%i), only using %i of them", giNumCPUs, MAX_CPUS); - giNumCPUs = MAX_CPUS; - } - - while( giNumInitingCPUs ) - MM_FinishVirtualInit(); - - Panic("Uh oh... MP Table Parsing is unimplemented\n"); - } - else { - Log("No MP Table was found, assuming uniprocessor\n"); - giNumCPUs = 1; - gTSSs = &gTSS0; - } - #else - giNumCPUs = 1; - gTSSs = &gTSS0; - MM_FinishVirtualInit(); - #endif - - #if USE_MP - // Initialise Normal TSS(s) - for(pos=0;pos> 16; - gGDT[7+pos*2].BaseHi = ((Uint)(&gTSSs[pos])) >> 24; - gGDT[7+pos*2+1].DWord[0] = ((Uint)(&gTSSs[pos])) >> 32; - #if USE_MP - } - for(pos=0;pos>8)&0xFF); // High Byte - - // Create Per-Process Data Block - if( !MM_Allocate(MM_PPD_CFG) ) - { - Warning("Oh, hell, Unable to allocate PPD for Thread#0"); - } - - Proc_InitialiseSSE(); - - Log_Log("Proc", "Multithreading initialised"); -} - -#if USE_MP -void MP_StartAP(int CPU) -{ - Log("Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID); - // Set location of AP startup code and mark for a warm restart - *(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APStartup - (KERNEL_BASE|0xFFFF0); - *(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF; - outb(0x70, 0x0F); outb(0x71, 0x0A); // Warm Reset - MP_SendIPI(gaCPUs[CPU].APICID, 0, 5); - giNumInitingCPUs ++; -} - -void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode) -{ - Uint32 addr = (Uint)gpMP_LocalAPIC + 0x300; - Uint32 val; - - // High - val = (Uint)APICID << 24; - Log("*%p = 0x%08x", addr+0x10, val); - *(Uint32*)(addr+0x10) = val; - // Low (and send) - val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF); - Log("*%p = 0x%08x", addr, val); - *(Uint32*)addr = val; -} -#endif - -/** - * \brief Idle task - */ -void Proc_IdleTask(void *ptr) -{ - tCPU *cpu = ptr; - cpu->IdleThread = Proc_GetCurThread(); - cpu->IdleThread->ThreadName = (char*)"Idle Thread"; - Threads_SetPriority( cpu->IdleThread, -1 ); // Never called randomly - cpu->IdleThread->Quantum = 1; // 1 slice quantum - for(;;) { - HALT(); // Just yeilds - Threads_Yield(); - } -} - -/** - * \fn void Proc_Start(void) - * \brief Start process scheduler - */ -void Proc_Start(void) -{ - #if USE_MP - int i; - #endif - - #if USE_MP - // Start APs - for( i = 0; i < giNumCPUs; i ++ ) - { - int tid; - if(i) gaCPUs[i].Current = NULL; - - Proc_NewKThread(Proc_IdleTask, &gaCPUs[i]); - - // Create Idle Task - gaCPUs[i].IdleThread = Threads_GetThread(tid); - - - // Start the AP - if( i != giProc_BootProcessorID ) { - MP_StartAP( i ); - } - } - - // BSP still should run the current task - gaCPUs[0].Current = &gThreadZero; - __asm__ __volatile__ ("mov %0, %%db0" : : "r"(&gThreadZero)); - - // Start interrupts and wait for APs to come up - Log("Waiting for APs to come up\n"); - __asm__ __volatile__ ("sti"); - while( giNumInitingCPUs ) __asm__ __volatile__ ("hlt"); - #else - Proc_NewKThread(Proc_IdleTask, &gaCPUs[0]); - - // Start Interrupts (and hence scheduler) - __asm__ __volatile__("sti"); - #endif - MM_FinishVirtualInit(); - Log_Log("Proc", "Multithreading started"); -} - -/** - * \fn tThread *Proc_GetCurThread(void) - * \brief Gets the current thread - */ -tThread *Proc_GetCurThread(void) -{ - #if USE_MP - tThread *ret; - __asm__ __volatile__ ("mov %%db0, %0" : "=r"(thread)); - return ret; // gaCPUs[ GetCPUNum() ].Current; - #else - return gaCPUs[ 0 ].Current; - #endif -} - -void Proc_ClearProcess(tProcess *Process) -{ - Log_Warning("Proc", "TODO: Nuke address space etc"); -} - -/* - * - */ -void Proc_ClearThread(tThread *Thread) -{ -} - -/** - * \brief Create a new kernel thread - */ -tTID Proc_NewKThread(void (*Fcn)(void*), void *Data) -{ - Uint rsp; - tThread *newThread, *cur; - - cur = Proc_GetCurThread(); - newThread = Threads_CloneTCB(0); - if(!newThread) return -1; - - // Create new KStack - newThread->KernelStack = MM_NewKStack(); - // Check for errors - if(newThread->KernelStack == 0) { - free(newThread); - return -1; - } - - rsp = newThread->KernelStack; - *(Uint*)(rsp-=8) = (Uint)Data; // Data (shadowed) - *(Uint*)(rsp-=8) = (Uint)Fcn; // Function to call - *(Uint*)(rsp-=8) = (Uint)newThread; // Thread ID - - newThread->SavedState.RSP = rsp; - newThread->SavedState.RIP = (Uint)&NewTaskHeader; - newThread->SavedState.SSE = NULL; -// Log("New (KThread) %p, rsp = %p\n", newThread->SavedState.RIP, newThread->SavedState.RSP); - -// MAGIC_BREAK(); - Threads_AddActive(newThread); - - return newThread->TID; -} - -/** - * \fn int Proc_Clone(Uint Flags) - * \brief Clone the current process - */ -tTID Proc_Clone(Uint Flags) -{ - tThread *newThread, *cur = Proc_GetCurThread(); - Uint rip; - - // Sanity check - if( !(Flags & CLONE_VM) ) { - Log_Error("Proc", "Proc_Clone: Don't leave CLONE_VM unset, use Proc_NewKThread instead"); - return -1; - } - - // Create new TCB - newThread = Threads_CloneTCB(Flags); - if(!newThread) return -1; - - // Save core machine state - rip = Proc_CloneInt(&newThread->SavedState.RSP, &newThread->Process->MemState.CR3); - if(rip == 0) return 0; // Child - newThread->KernelStack = cur->KernelStack; - newThread->SavedState.RIP = rip; - newThread->SavedState.SSE = NULL; - - // DEBUG - #if 0 - Log("New (Clone) %p, rsp = %p, cr3 = %p", rip, newThread->SavedState.RSP, newThread->MemState.CR3); - { - Uint cr3; - __asm__ __volatile__ ("mov %%cr3, %0" : "=r" (cr3)); - Log("Current CR3 = 0x%x, PADDR(RSP) = 0x%x", cr3, MM_GetPhysAddr(newThread->SavedState.RSP)); - } - #endif - // /DEBUG - - // Lock list and add to active - Threads_AddActive(newThread); - - return newThread->TID; -} - -/** - * \fn int Proc_SpawnWorker(void) - * \brief Spawns a new worker thread - */ -int Proc_SpawnWorker(void (*Fcn)(void*), void *Data) -{ - tThread *new, *cur; - Uint stack_contents[3]; - - cur = Proc_GetCurThread(); - - // Create new thread - new = Threads_CloneThreadZero(); - if(!new) { - Warning("Proc_SpawnWorker - Out of heap space!\n"); - return -1; - } - - // Create the stack contents - stack_contents[2] = (Uint)Data; - stack_contents[1] = (Uint)Fcn; - stack_contents[0] = (Uint)new; - - // Create a new worker stack (in PID0's address space) - // - The stack is built by this code using stack_contents - new->KernelStack = MM_NewWorkerStack(stack_contents, sizeof(stack_contents)); - - new->SavedState.RSP = new->KernelStack - sizeof(stack_contents); - new->SavedState.RIP = (Uint)&NewTaskHeader; - new->SavedState.SSE = NULL; - -// Log("New (Worker) %p, rsp = %p\n", new->SavedState.RIP, new->SavedState.RSP); - - // Mark as active - new->Status = THREAD_STAT_PREINIT; - Threads_AddActive( new ); - - return new->TID; -} - -/** - * \brief Creates a new user stack - */ -Uint Proc_MakeUserStack(void) -{ - int i; - Uint base = USER_STACK_TOP - USER_STACK_SZ; - - // Check Prospective Space - for( i = USER_STACK_SZ >> 12; i--; ) - { - if( MM_GetPhysAddr( base + (i<<12) ) != 0 ) - break; - } - - if(i != -1) return 0; - - // Allocate Stack - Allocate incrementally to clean up MM_Dump output - // - Most of the user stack is the zero page - for( i = 0; i < (USER_STACK_SZ-USER_STACK_PREALLOC)/0x1000; i++ ) - { - MM_AllocateZero( base + (i<<12) ); - } - // - but the top USER_STACK_PREALLOC pages are actually allocated - for( ; i < USER_STACK_SZ/0x1000; i++ ) - { - tPAddr alloc = MM_Allocate( base + (i<<12) ); - if( !alloc ) - { - // Error - Log_Error("Proc", "Unable to allocate user stack (%i pages requested)", USER_STACK_SZ/0x1000); - while( i -- ) - MM_Deallocate( base + (i<<12) ); - return 0; - } - } - - return base + USER_STACK_SZ; -} - -void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) -{ - Uint *stack; - int i; - const char **envp = NULL; - Uint16 ss, cs; - - - // Copy Arguments - stack = (void*)Proc_MakeUserStack(); - if(!stack) { - Log_Error("Proc", "Unable to create user stack!"); - Threads_Exit(0, -1); - } - stack -= (DataSize+7)/8; - memcpy( stack, ArgV, DataSize ); - free(ArgV); - - // Adjust Arguments and environment - if(DataSize) - { - Uint delta = (Uint)stack - (Uint)ArgV; - ArgV = (const char**)stack; - for( i = 0; ArgV[i]; i++ ) ArgV[i] += delta; - envp = &ArgV[i+1]; - for( i = 0; envp[i]; i++ ) envp[i] += delta; - } - - // User Mode Segments - // 0x2B = 64-bit - ss = 0x23; cs = 0x2B; - - // Arguments - *--stack = (Uint)envp; - *--stack = (Uint)ArgV; - *--stack = (Uint)ArgC; - *--stack = Base; - - Proc_StartProcess(ss, (Uint)stack, 0x202, cs, Entrypoint); -} - -void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) -{ - if( !(CS == 0x1B || CS == 0x2B) || SS != 0x23 ) { - Log_Error("Proc", "Proc_StartProcess: CS / SS are not valid (%x, %x)", - CS, SS); - Threads_Exit(0, -1); - } -// Log("Proc_StartProcess: (SS=%x, Stack=%p, Flags=%x, CS=%x, IP=%p)", SS, Stack, Flags, CS, IP); -// MM_DumpTables(0, USER_MAX); - if(CS == 0x1B) - { - // 32-bit return - __asm__ __volatile__ ( - "mov %0, %%rsp;\n\t" // Set stack pointer - "mov %2, %%r11;\n\t" // Set RFLAGS - "sysret;\n\t" - : : "r" (Stack), "c" (IP), "r" (Flags) - ); - } - else - { - // 64-bit return - __asm__ __volatile__ ( - "mov %0, %%rsp;\n\t" // Set stack pointer - "mov %2, %%r11;\n\t" // Set RFLAGS - "sysretq;\n\t" - : : "r" (Stack), "c" (IP), "r" (Flags) - ); - } - for(;;); -} - -/** - * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs) - * \brief Demotes a process to a lower permission level - * \param Err Pointer to user's errno - * \param Dest New Permission Level - * \param Regs Pointer to user's register structure - */ -int Proc_Demote(Uint *Err, int Dest, tRegs *Regs) -{ - int cpl = Regs->CS & 3; - // Sanity Check - if(Dest > 3 || Dest < 0) { - *Err = -EINVAL; - return -1; - } - - // Permission Check - if(cpl > Dest) { - *Err = -EACCES; - return -1; - } - - // Change the Segment Registers - Regs->CS = (((Dest+1)<<4) | Dest) - 8; - Regs->SS = ((Dest+1)<<4) | Dest; - - return 0; -} - -/** - * \brief Calls a signal handler in user mode - * \note Used for signals - */ -void Proc_CallFaultHandler(tThread *Thread) -{ - // Never returns - Proc_ReturnToUser(Thread->FaultHandler, Thread->KernelStack, Thread->CurFaultNum); - for(;;); -} - -void Proc_DumpThreadCPUState(tThread *Thread) -{ - Log(" At %04x:%016llx", Thread->SavedState.UserCS, Thread->SavedState.UserRIP); -} - -void Proc_Reschedule(void) -{ - tThread *nextthread, *curthread; - int cpu = GetCPUNum(); - - // TODO: Wait for it? - if(IS_LOCKED(&glThreadListLock)) return; - - curthread = gaCPUs[cpu].Current; - - nextthread = Threads_GetNextToRun(cpu, curthread); - - if(nextthread == curthread) return ; - if(!nextthread) - nextthread = gaCPUs[cpu].IdleThread; - if(!nextthread) - return ; - - #if DEBUG_TRACE_SWITCH - LogF("\nSwitching to task CR3 = 0x%x, RIP = %p, RSP = %p - %i (%s)\n", - nextthread->Process->MemState.CR3, - nextthread->SavedState.RIP, - nextthread->SavedState.RSP, - nextthread->TID, - nextthread->ThreadName - ); - #endif - - // Update CPU state - gaCPUs[cpu].Current = nextthread; - gTSSs[cpu].RSP0 = nextthread->KernelStack-4; - __asm__ __volatile__ ("mov %0, %%db0" : : "r" (nextthread)); - - if( curthread ) - { - // Save FPU/MMX/XMM/SSE state - if( curthread->SavedState.SSE ) - { - Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF ); - curthread->SavedState.bSSEModified = 0; - Proc_DisableSSE(); - } - SwitchTasks( - nextthread->SavedState.RSP, &curthread->SavedState.RSP, - nextthread->SavedState.RIP, &curthread->SavedState.RIP, - nextthread->Process->MemState.CR3 - ); - } - else - { - Uint tmp; - SwitchTasks( - nextthread->SavedState.RSP, &tmp, - nextthread->SavedState.RIP, &tmp, - nextthread->Process->MemState.CR3 - ); - } - return ; -} - -/** - * \fn void Proc_Scheduler(int CPU) - * \brief Swap current thread and clears dead threads - */ -void Proc_Scheduler(int CPU, Uint RSP, Uint RIP) -{ -#if 0 - tThread *thread; - - // If the spinlock is set, let it complete - if(IS_LOCKED(&glThreadListLock)) return; - - // Get current thread - thread = gaCPUs[CPU].Current; - - if( thread ) - { - tRegs *regs; - // Reduce remaining quantum and continue timeslice if non-zero - if(thread->Remaining--) return; - // Reset quantum for next call - thread->Remaining = thread->Quantum; - - // TODO: Make this more stable somehow - { - regs = (tRegs*)(RSP+(1)*8); // CurThread - thread->SavedState.UserCS = regs->CS; - thread->SavedState.UserRIP = regs->RIP; - } - } - - // ACK Timer here? - - Proc_Reschedule(); -#endif -} - -// === EXPORTS === -EXPORT(Proc_SpawnWorker); diff --git a/Kernel/arch/x86_64/rme.c b/Kernel/arch/x86_64/rme.c deleted file mode 120000 index 330f4f5f..00000000 --- a/Kernel/arch/x86_64/rme.c +++ /dev/null @@ -1 +0,0 @@ -/home/tpg/Projects/RealmodeEmulator/src/rme.c \ No newline at end of file diff --git a/Kernel/arch/x86_64/rme.h b/Kernel/arch/x86_64/rme.h deleted file mode 120000 index 98b2739f..00000000 --- a/Kernel/arch/x86_64/rme.h +++ /dev/null @@ -1 +0,0 @@ -/home/tpg/Projects/RealmodeEmulator/src/rme.h \ No newline at end of file diff --git a/Kernel/arch/x86_64/start32.asm b/Kernel/arch/x86_64/start32.asm deleted file mode 100644 index 52b133c6..00000000 --- a/Kernel/arch/x86_64/start32.asm +++ /dev/null @@ -1,188 +0,0 @@ -; -; Acess2 x86_64 port -; - -%include "arch/x86_64/include/common.inc.asm" - -[BITS 32] - -;KERNEL_BASE equ 0xFFFF800000000000 -KERNEL_BASE equ 0xFFFFFFFF80000000 - -[section .multiboot] -mboot: - ; Multiboot macros to make a few lines later more readable - MULTIBOOT_PAGE_ALIGN equ 1<<0 - MULTIBOOT_MEMORY_INFO equ 1<<1 - MULTIBOOT_AOUT_KLUDGE equ 1<<16 - MULTIBOOT_HEADER_MAGIC equ 0x1BADB002 - MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO; | MULTIBOOT_AOUT_KLUDGE - MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) - - ; This is the GRUB Multiboot header. A boot signature - dd MULTIBOOT_HEADER_MAGIC - dd MULTIBOOT_HEADER_FLAGS - dd MULTIBOOT_CHECKSUM - [extern __load_addr] - [extern __bss_start] - [extern gKernelEnd] - ; a.out kludge - dd mboot ; Location of Multiboot Header - dd __load_addr ; Load address - dd __bss_start - KERNEL_BASE ; End of .data - dd gKernelEnd - KERNEL_BASE ; End of .bss (and kernel) - dd start - KERNEL_BASE ; Entrypoint - -[extern start64] - -[section .text] -[global start] -start: - mov [gMultibootMagic - KERNEL_BASE], eax - mov [gMultibootPtr - KERNEL_BASE], ebx - - ; Check for Long Mode support - mov eax, 0x80000000 - cpuid - cmp eax, 0x80000001 ; Compare the A-register with 0x80000001. - mov eax, 0x80000001 - cpuid - jb .not64bitCapable - test edx, 1<<29 - jz .not64bitCapable - - ; Enable PGE (Page Global Enable) - ; + PAE (Physical Address Extension) - ; + PSE (Page Size Extensions) - mov eax, cr4 - or eax, 0x80|0x20|0x10 - mov cr4, eax - - ; Load PDP4 - mov eax, gInitialPML4 - KERNEL_BASE - mov cr3, eax - - ; Enable IA-32e mode - ; (Also enables SYSCALL and NX) - mov ecx, 0xC0000080 - rdmsr - or eax, (1 << 11)|(1 << 8)|(1 << 0) ; NXE, LME, SCE - wrmsr - - ; Enable paging - mov eax, cr0 - or eax, 0x80010000 ; PG & WP - mov cr0, eax - - ; Load GDT - lgdt [gGDTPtr - KERNEL_BASE] - jmp 0x08:start64 - KERNEL_BASE - -.not64bitCapable: - mov ah, 0x0F - mov edi, 0xB8000 - mov esi, csNot64BitCapable - KERNEL_BASE - -.loop: - lodsb - test al, al - jz .hlt - stosw - jmp .loop - -.hlt: - cli - hlt - jmp .hlt - -[section .data] -[global gGDT] -[global gGDTPtr] -gGDT: - dd 0,0 - dd 0x00000000, 0x00209A00 ; 0x08: 64-bit Code - dd 0x00000000, 0x00009200 ; 0x10: 64-bit Data - dd 0x00000000, 0x0040FA00 ; 0x18: 32-bit User Code - dd 0x00000000, 0x0040F200 ; 0x20: User Data - dd 0x00000000, 0x0020FA00 ; 0x28: 64-bit User Code - dd 0x00000000, 0x0000F200 ; 0x30: User Data (64 version) - times MAX_CPUS dd 0, 0x00008900, 0, 0 ; 0x38+16*n: TSS 0 -gGDTPtr: - dw $-gGDT-1 - dd gGDT-KERNEL_BASE - dd 0 -[global gMultibootPtr] -[global gMultibootMagic] -gMultibootMagic: - dd 0 -gMultibootPtr: - dd 0 - -[section .padata] -[global gInitialPML4] -gInitialPML4: ; Covers 256 TiB (Full 48-bit Virtual Address Space) - dd gInitialPDP - KERNEL_BASE + 3, 0 ; Identity Map Low 4Mb - times 0xA0*2-1 dq 0 - dd gStackPDP - KERNEL_BASE + 3, 0 - times 512-4-($-gInitialPML4)/8 dq 0 - dd gInitialPML4 - KERNEL_BASE + 3, 0 ; Fractal Mapping - dq 0 - dq 0 - dd gHighPDP - KERNEL_BASE + 3, 0 ; Map Low 4Mb to kernel base - -gInitialPDP: ; Covers 512 GiB - dd gInitialPD - KERNEL_BASE + 3, 0 - times 511 dq 0 - -gStackPDP: - dd gStackPD - KERNEL_BASE + 3, 0 - times 511 dq 0 - -gHighPDP: ; Covers 512 GiB - times 510 dq 0 - ;dq 0 + 0x143 ; 1 GiB Page from zero - dd gInitialPD - KERNEL_BASE + 3, 0 - dq 0 - -gInitialPD: ; Covers 1 GiB -; dq 0 + 0x143 ; 1 GiB Page from zero - dd gInitialPT1 - KERNEL_BASE + 3, 0 - dd gInitialPT2 - KERNEL_BASE + 3, 0 - times 510 dq 0 - -gStackPD: - dd gKStackPT - KERNEL_BASE + 3, 0 - times 511 dq 0 - -gKStackPT: ; Covers 2 MiB - ; Initial stack - 64KiB - dq 0 - %assign i 0 - %rep INITIAL_KSTACK_SIZE-1 - dd gInitialKernelStack - KERNEL_BASE + i*0x1000 + 0x103, 0 - %assign i i+1 - %endrep - times 512-INITIAL_KSTACK_SIZE dq 0 -gInitialPT1: ; 2 MiB - %assign i 0 - %rep 512 - dq i*4096+0x103 - %assign i i+1 - %endrep -gInitialPT2: ; 2 MiB - %assign i 512 - %rep 512 - dq i*4096+0x103 - %assign i i+1 - %endrep - -[section .padata] -[global gInitialKernelStack] -gInitialKernelStack: - times 0x1000*(INITIAL_KSTACK_SIZE-1) db 0 ; 8 Pages - -[section .rodata] -csNot64BitCapable: - db "Not 64-bit Capable",0 - -; vim: ft=nasm diff --git a/Kernel/arch/x86_64/start64.asm b/Kernel/arch/x86_64/start64.asm deleted file mode 100644 index c8e7f9a8..00000000 --- a/Kernel/arch/x86_64/start64.asm +++ /dev/null @@ -1,143 +0,0 @@ -; -; Acess2 x86_64 Port -; -%include "arch/x86_64/include/common.inc.asm" -[bits 64] -;KERNEL_BASE equ 0xFFFF800000000000 -KERNEL_BASE equ 0xFFFFFFFF80000000 - -[extern kmain] - -[extern gMultibootPtr] -[extern gMultibootMagic] - -[section .text] -[global start64] -start64: - ; Load Registers - mov ax, 0x10 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - - ; Go to high memory - mov rax, start64.himem - jmp rax -.himem: - - xor rax, rax - mov dr0, rax ; Set CPU0 - - ; Clear the screen - mov rax, 0x1F201F201F201F20 ; Set the screen to White on blue, space (4 characters) - mov edi, 0xB8000 - mov ecx, 80*25*2/8 - rep stosq - - ; Set kernel stack - mov rsp, 0xFFFFA00000000000 + INITIAL_KSTACK_SIZE*0x1000 - - ; Call main - mov edi, [gMultibootMagic - KERNEL_BASE] - mov esi, [gMultibootPtr - KERNEL_BASE] - call kmain - - cli -.hlt: - hlt - jmp .hlt - -[global GetCPUNum] -GetCPUNum: - mov rax, dr1 - ret - -KSTACK_USERSTATE_SIZE equ (5+2+16+2)*8 ; IRET, ErrorNum, ErrorCode, GPRs, FS&GS -[global Proc_ReturnToUser] -Proc_ReturnToUser: - ; RDI - Handler - ; RSI - Kernel Stack - ; RDX - Signal num - - ; - ; NOTE: This can cause corruption if the signal happens while the user - ; has called a kernel operation. - ; Good thing this can only be called on a user fault. - ; - - xchg bx, bx - ; Get and alter User SP - mov rcx, [rsi-0x20] ; Get user SP - xor eax, eax - mov [rcx-16], rax - sub rcx, 16 - - ; Drop down to user mode - cli - mov rsp, rcx ; Set SP - mov rcx, rdi ; SYSRET IP - - mov rdi, rdx ; Argument for handler - mov r11, 0x202 ; RFlags - db 0x48 - sysret - -; int CallWithArgArray(void *Ptr, int NArgs, Uint *Args) -; Call a function passing the array as arguments -[global CallWithArgArray] -CallWithArgArray: - push rbp - mov rbp, rsp - push r10 - push r11 - - mov [rbp+2*8], rdi ; Save Ptr to stack - - mov r11, rsi ; NArgs - mov r10, rdx ; Args - - ; Arg 1: RDI - mov rdi, [r10] - add r10, 8 - dec r11 - jz .call - ; Arg 2: RSI - mov rsi, [r10] - add r10, 8 - dec r11 - jz .call - ; Arg 3: RDX - mov rdx, [r10] - add r10, 8 - dec r11 - jz .call - ; Arg 4: RCX - mov rcx, [r10] - add r10, 8 - dec r11 - jz .call - ; Arg 5: R8 - mov r8, [r10] - add r10, 8 - dec r11 - jz .call - ; Arg 6: R9 - mov r9, [r10] - add r10, 8 - dec r11 - jz .call - ; No support for more - -.call: - mov rax, [rbp+2*8] ; Ptr - call rax - - pop r11 - pop r10 - - lea rsp, [rbp] - pop rbp - ret - -; vim: ft=nasm diff --git a/Kernel/arch/x86_64/time.c b/Kernel/arch/x86_64/time.c deleted file mode 100644 index 03c889fe..00000000 --- a/Kernel/arch/x86_64/time.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Acess2 Kernel - * Timekeeping - * arch/x86_64/time.c - */ -#include -#include - -// === MACROS === -#define TIMER_QUANTUM 100 -#define TIMER_FREQ PIT_TIMER_BASE_N/(PIT_TIMER_BASE_D*PIT_TIMER_DIVISOR) -#define MS_PER_TICK_WHOLE (1000*(PIT_TIMER_BASE_D*PIT_TIMER_DIVISOR)/PIT_TIMER_BASE_N) -#define MS_PER_TICK_FRACT ((0x80000000ULL*1000ULL*PIT_TIMER_BASE_D*PIT_TIMER_DIVISOR/PIT_TIMER_BASE_N)&0x7FFFFFFF) - -// === IMPORTS === -extern volatile Sint64 giTimestamp; -extern volatile Uint64 giTicks; -extern volatile Uint64 giPartMiliseconds; -extern void Timer_CallTimers(void); - -// === GLOBALS === -volatile Uint64 giTime_TSCAtLastTick = 0; -volatile Uint64 giTime_TSCPerTick = 0; - -// === PROTOTYPES === -//Sint64 now(void); - int Time_Setup(void); -void Time_UpdateTimestamp(void); -Uint64 Time_ReadTSC(void); - -// === CODE === -/** - * \fn Sint64 now() - * \brief Return the current timestamp - */ -Sint64 now(void) -{ - Uint64 tsc = Time_ReadTSC(); - tsc -= giTime_TSCAtLastTick; - tsc *= MS_PER_TICK_WHOLE; - if( giTime_TSCPerTick ) { - tsc /= giTime_TSCPerTick; - } - else - tsc = 0; - return giTimestamp + tsc; -} - -/** - * \fn int Time_Setup(void) - * \brief Sets the system time from the Realtime-Clock - */ -int Time_Setup(void) -{ - Log_Log("Timer", "PIT Timer firing at %iHz, %i.0x%08x miliseconds per tick", - TIMER_FREQ, MS_PER_TICK_WHOLE, MS_PER_TICK_FRACT); - - // TODO: Read time from RTC - - return 0; -} - -/** - * \brief Called on the timekeeping IRQ - */ -void Time_UpdateTimestamp(void) -{ - Uint64 curTSC = Time_ReadTSC(); - - if( giTime_TSCAtLastTick ) - { - giTime_TSCPerTick = curTSC - giTime_TSCAtLastTick; - } - giTime_TSCAtLastTick = curTSC; - - giTicks ++; - giTimestamp += MS_PER_TICK_WHOLE; - giPartMiliseconds += MS_PER_TICK_FRACT; - if(giPartMiliseconds > 0x80000000) { - giTimestamp ++; - giPartMiliseconds -= 0x80000000; - } - - Timer_CallTimers(); -} - -#if 0 -/** - * \fn void Time_TimerThread(void) - */ -void Time_TimerThread(void) -{ - Sint64 next; - Threads_SetName("TIMER"); - - next = giTimestamp + TIMER_QUANTUM; - for(;;) - { - while(giTimestamp < next) Threads_Yield(); - next = giTimestamp + TIMER_QUANTUM; - Timer_CallTimers(); - } -} -#endif - -Uint64 Time_ReadTSC(void) -{ - Uint32 a, d; - __asm__ __volatile__ ("rdtsc" : "=a" (a), "=d" (d)); - return ((Uint64)d << 32) | a; -} diff --git a/Kernel/arch/x86_64/vm8086.c b/Kernel/arch/x86_64/vm8086.c deleted file mode 100644 index 42515705..00000000 --- a/Kernel/arch/x86_64/vm8086.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - */ -#include -#include -#include -//#include "rme.h" - -// === CONSTANTS === -#define VM8086_STACK_SEG 0x9F00 -#define VM8086_STACK_OFS 0x0AFE -#define VM8086_PAGES_PER_INST 4 - -// === PROTOTYPES === - int VM8086_Install(char **Arguments); -//tVM8086 *VM8086_Init(void); -//void VM8086_Free(tVM8086 *State); - -// === GLOBALS === -MODULE_DEFINE(0, 0x100, VM8086, VM8086_Install, NULL, NULL); -tMutex glVM8086_Process; -//tRME_State *gpVM8086_State; -tPID gVM8086_WorkerPID; -tTID gVM8086_CallingThread; -tVM8086 volatile * volatile gpVM8086_State = (void*)-1; // Set to -1 to avoid race conditions - -// === CODE === -int VM8086_Install(char **Arguments) -{ - //gpVM8086_State = RME_CreateState(); - return MODULE_ERR_OK; -} - -tVM8086 *VM8086_Init(void) -{ - return NULL; -} - -void VM8086_Free(tVM8086 *State) -{ - -} - -void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset) -{ - return NULL; -} - -void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset) -{ - return NULL; -} - -void VM8086_Int(tVM8086 *State, Uint8 Interrupt) -{ - -} diff --git a/Kernel/bin/README b/Kernel/bin/README deleted file mode 100644 index 63d64bb9..00000000 --- a/Kernel/bin/README +++ /dev/null @@ -1,24 +0,0 @@ -Acess2 Binary File Loader Specifcation --------------------------------------- - -Binary file loaders are defined by creating a \a tBinaryType variable that -is registered with the kernel. - -\a tBinaryType contains seven fields. --# \a Next - This field is used internally by the kernel and should be set to NULL - when the definition is initialise and not changed by the driver. --# \a Ident - This field tells the kernel how to recognise this file format. If the - first 32-bits of the file (ANDed with the \a Mask field) match this - value, the file will be passed to this loader. --# \a Mask - Determines what bits in the first 32-bits of the file matter for the - identifcation. --# \a Name - This is a C string that uniquely identifies this binary format. --# \a Load - This field is a pointer to a function that takes a VFS Handle of the - source exectuable file as an argument and returns a \a tBinary - pointer that defines the location and attributes of the exectable's - segments. (See \a tBinary for more information) diff --git a/Kernel/bin/elf.c b/Kernel/bin/elf.c deleted file mode 100644 index 8f51290a..00000000 --- a/Kernel/bin/elf.c +++ /dev/null @@ -1,643 +0,0 @@ -/* - * Acess v0.1 - * ELF Executable Loader Code - */ -#define DEBUG 0 -#include -#include -#include "elf.h" - -#define DEBUG_WARN 1 - -// === PROTOTYPES === -tBinary *Elf_Load(int fp); -tBinary *Elf_Load64(int fp, Elf64_Ehdr *hdr); -tBinary *Elf_Load32(int fp, Elf32_Ehdr *hdr); - int Elf_Relocate(void *Base); - int Elf_Relocate32(void *Base); - int Elf_GetSymbol(void *Base, const char *Name, Uint *ret); - int Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base); -Uint Elf_Int_HashString(const char *str); - -// === GLOBALS === -tBinaryType gELF_Info = { - NULL, - 0x464C457F, 0xFFFFFFFF, // '\x7FELF' - "ELF", - Elf_Load, Elf_Relocate, Elf_GetSymbol - }; - -// === CODE === -tBinary *Elf_Load(int fp) -{ - Elf64_Ehdr hdr; - - // Read ELF Header - VFS_Read(fp, sizeof(hdr), &hdr); - - // Check the file type - if(hdr.e_ident[0] != 0x7F || hdr.e_ident[1] != 'E' || hdr.e_ident[2] != 'L' || hdr.e_ident[3] != 'F') { - Log_Warning("ELF", "Non-ELF File was passed to the ELF loader"); - return NULL; - } - - switch(hdr.e_ident[4]) // EI_CLASS - { - case ELFCLASS32: - return Elf_Load32(fp, (Elf32_Ehdr*)&hdr); - case ELFCLASS64: - return Elf_Load64(fp, &hdr); - default: - Log_Warning("ELF", "Unknown EI_CLASS value %i", hdr.e_ident[4]); - return NULL; - } -} - -tBinary *Elf_Load64(int FD, Elf64_Ehdr *Header) -{ - tBinary *ret; - Elf64_Phdr phtab[Header->e_phnum]; - int nLoadSegments; - int i, j; - - // Sanity check - if( Header->e_phoff == 0 ) - { - Log_Warning("ELF", "No program header, panic!"); - return NULL; - } - if( Header->e_shentsize != sizeof(Elf64_Shdr) ) { - Log_Warning("ELF", "Header gives shentsize as %i, my type is %i", - Header->e_shentsize, sizeof(Elf64_Shdr) ); - } - if( Header->e_phentsize != sizeof(Elf64_Phdr) ) { - Log_Warning("ELF", "Header gives phentsize as %i, my type is %i", - Header->e_phentsize, sizeof(Elf64_Phdr) ); - } - - LOG("Header = {"); - LOG(" e_ident = %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", - Header->e_ident[0], Header->e_ident[1], Header->e_ident[2], Header->e_ident[3], - Header->e_ident[4], Header->e_ident[5], Header->e_ident[6], Header->e_ident[7], - Header->e_ident[8], Header->e_ident[9], Header->e_ident[10], Header->e_ident[11], - Header->e_ident[12], Header->e_ident[13], Header->e_ident[14], Header->e_ident[15] - ); - LOG(" e_type = %i", Header->e_type); - LOG(" e_machine = %i", Header->e_machine); - LOG(" e_version = %i", Header->e_version); - LOG(" e_entry = 0x%llx", Header->e_entry); - LOG(" e_phoff = 0x%llx", Header->e_phoff); - LOG(" e_shoff = 0x%llx", Header->e_shoff); - LOG(" e_flags = 0x%x", Header->e_flags); - LOG(" e_ehsize = %i", Header->e_ehsize); - LOG(" e_phentsize = %i", Header->e_phentsize); - LOG(" e_phnum = %i", Header->e_phnum); - LOG(" e_shentsize = %i", Header->e_shentsize); - LOG(" e_shnum = %i", Header->e_shnum); - LOG(" e_shstrndx = %i", Header->e_shstrndx); - LOG("}"); - - // Load Program Header table - VFS_Seek(FD, Header->e_phoff, SEEK_SET); - VFS_Read(FD, sizeof(Elf64_Phdr)*Header->e_phnum, phtab); - - // Count load segments - nLoadSegments = 0; - for( i = 0; i < Header->e_phnum; i ++ ) - { - if( phtab[i].p_type != PT_LOAD ) continue ; - nLoadSegments ++; - } - - // Allocate Information Structure - ret = malloc( sizeof(tBinary) + sizeof(tBinarySection)*nLoadSegments ); - // Fill Info Struct - ret->Entry = Header->e_entry; - ret->Base = -1; // Set Base to maximum value - ret->NumSections = nLoadSegments; - ret->Interpreter = NULL; - - j = 0; // LoadSections[] index - for( i = 0; i < Header->e_phnum; i ++ ) - { - LOG("phtab[%i] = {", i); - LOG(" .p_type = %i", phtab[i].p_type); - LOG(" .p_flags = 0x%x", phtab[i].p_flags); - LOG(" .p_offset = 0x%llx", phtab[i].p_offset); - LOG(" .p_vaddr = 0x%llx", phtab[i].p_vaddr); - LOG(" .p_paddr = 0x%llx", phtab[i].p_paddr); - LOG(" .p_filesz = 0x%llx", phtab[i].p_filesz); - LOG(" .p_memsz = 0x%llx", phtab[i].p_memsz); - LOG(" .p_align = 0x%llx", phtab[i].p_align); - LOG("}"); - - // Get Interpreter Name - if( phtab[i].p_type == PT_INTERP ) - { - char *tmp; - if(ret->Interpreter) continue; - tmp = malloc(phtab[i].p_filesz); - VFS_Seek(FD, phtab[i].p_offset, 1); - VFS_Read(FD, phtab[i].p_filesz, tmp); - ret->Interpreter = Binary_RegInterp(tmp); - LOG("Interpreter '%s'", tmp); - free(tmp); - continue; - } - - if( phtab[i].p_type != PT_LOAD ) continue ; - - // Find the executable base - if( phtab[i].p_vaddr < ret->Base ) ret->Base = phtab[i].p_vaddr; - - ret->LoadSections[j].Offset = phtab[i].p_offset; - ret->LoadSections[j].Virtual = phtab[i].p_vaddr; - ret->LoadSections[j].FileSize = phtab[i].p_filesz; - ret->LoadSections[j].MemSize = phtab[i].p_memsz; - - ret->LoadSections[j].Flags = 0; - if( !(phtab[i].p_flags & PF_W) ) - ret->LoadSections[j].Flags |= BIN_SECTFLAG_RO; - if( phtab[i].p_flags & PF_X ) - ret->LoadSections[j].Flags |= BIN_SECTFLAG_EXEC; - j ++; - } - - return ret; -} - -tBinary *Elf_Load32(int FD, Elf32_Ehdr *Header) -{ - tBinary *ret; - Elf32_Phdr *phtab; - int i, j; - int iLoadCount; - - ENTER("xFD", FD); - - // Check architecture with current CPU - // - TODO: Support kernel level emulation - #if ARCH_IS_x86 - if( Header->machine != EM_386 ) - { - Log_Warning("ELF", "Unknown architecure on ELF-32"); - LEAVE_RET('n'); - return NULL; - } - #endif - - // Check for a program header - if(Header->phoff == 0) { - #if DEBUG_WARN - Log_Warning("ELF", "File does not contain a program header (phoff == 0)"); - #endif - LEAVE('n'); - return NULL; - } - - // Read Program Header Table - phtab = malloc( sizeof(Elf32_Phdr) * Header->phentcount ); - if( !phtab ) { - LEAVE('n'); - return NULL; - } - LOG("hdr.phoff = 0x%08x", Header->phoff); - VFS_Seek(FD, Header->phoff, SEEK_SET); - VFS_Read(FD, sizeof(Elf32_Phdr)*Header->phentcount, phtab); - - // Count Pages - iLoadCount = 0; - LOG("Header->phentcount = %i", Header->phentcount); - for( i = 0; i < Header->phentcount; i++ ) - { - // Ignore Non-LOAD types - if(phtab[i].Type != PT_LOAD) - continue; - iLoadCount ++; - LOG("phtab[%i] = {VAddr:0x%x, MemSize:0x%x}", i, phtab[i].VAddr, phtab[i].MemSize); - } - - LOG("iLoadCount = %i", iLoadCount); - - // Allocate Information Structure - ret = malloc( sizeof(tBinary) + sizeof(tBinarySection)*iLoadCount ); - // Fill Info Struct - ret->Entry = Header->entrypoint; - ret->Base = -1; // Set Base to maximum value - ret->NumSections = iLoadCount; - ret->Interpreter = NULL; - - // Load Pages - j = 0; - for( i = 0; i < Header->phentcount; i++ ) - { - //LOG("phtab[%i].Type = 0x%x", i, phtab[i].Type); - LOG("phtab[%i] = {", i); - LOG(" .Type = 0x%08x", phtab[i].Type); - LOG(" .Offset = 0x%08x", phtab[i].Offset); - LOG(" .VAddr = 0x%08x", phtab[i].VAddr); - LOG(" .PAddr = 0x%08x", phtab[i].PAddr); - LOG(" .FileSize = 0x%08x", phtab[i].FileSize); - LOG(" .MemSize = 0x%08x", phtab[i].MemSize); - LOG(" .Flags = 0x%08x", phtab[i].Flags); - LOG(" .Align = 0x%08x", phtab[i].Align); - LOG(" }"); - // Get Interpreter Name - if( phtab[i].Type == PT_INTERP ) - { - char *tmp; - if(ret->Interpreter) continue; - tmp = malloc(phtab[i].FileSize); - VFS_Seek(FD, phtab[i].Offset, 1); - VFS_Read(FD, phtab[i].FileSize, tmp); - ret->Interpreter = Binary_RegInterp(tmp); - LOG("Interpreter '%s'", tmp); - free(tmp); - continue; - } - // Ignore non-LOAD types - if(phtab[i].Type != PT_LOAD) continue; - - // Find Base - if(phtab[i].VAddr < ret->Base) ret->Base = phtab[i].VAddr; - - LOG("phtab[%i] = {VAddr:0x%x,Offset:0x%x,FileSize:0x%x}", - i, phtab[i].VAddr, phtab[i].Offset, phtab[i].FileSize); - - ret->LoadSections[j].Offset = phtab[i].Offset; - ret->LoadSections[j].FileSize = phtab[i].FileSize; - ret->LoadSections[j].Virtual = phtab[i].VAddr; - ret->LoadSections[j].MemSize = phtab[i].MemSize; - ret->LoadSections[j].Flags = 0; - if( !(phtab[i].Flags & PF_W) ) - ret->LoadSections[j].Flags |= BIN_SECTFLAG_RO; - if( phtab[i].Flags & PF_X ) - ret->LoadSections[j].Flags |= BIN_SECTFLAG_EXEC; - j ++; - } - - // Clean Up - free(phtab); - // Return - LEAVE('p', ret); - return ret; -} - -// --- ELF RELOCATION --- -int Elf_Relocate(void *Base) -{ - Elf64_Ehdr *hdr = Base; - - switch( hdr->e_ident[EI_CLASS] ) - { - case ELFCLASS32: - return Elf_Relocate32(Base); - case ELFCLASS64: - return 0; - default: - return 1; - } -} - - -/** - * \brief Relocates a loaded ELF Executable - */ -int Elf_Relocate32(void *Base) -{ - Elf32_Ehdr *hdr = Base; - Elf32_Phdr *phtab; - int i, j; // Counters - char *libPath; - Uint iRealBase = -1; - Uint iBaseDiff; - int iSegmentCount; - int iSymCount = 0; - Elf32_Rel *rel = NULL; - Elf32_Rela *rela = NULL; - Uint32 *pltgot = NULL; - void *plt = NULL; - Uint32 *ptr; - int relSz=0, relEntSz=8; - int relaSz=0, relaEntSz=8; - int pltSz=0, pltType=0; - Elf32_Dyn *dynamicTab = NULL; // Dynamic Table Pointer - char *dynstrtab = NULL; // .dynamic String Table - Elf32_Sym *dynsymtab = NULL; - int bFailed = 0; - - ENTER("pBase", Base); - - // Parse Program Header to get Dynamic Table - phtab = (void *)( (tVAddr)Base + hdr->phoff ); - iSegmentCount = hdr->phentcount; - for(i = 0; i < iSegmentCount; i ++ ) - { - // Determine linked base address - if(phtab[i].Type == PT_LOAD && iRealBase > phtab[i].VAddr) - iRealBase = phtab[i].VAddr; - - // Find Dynamic Section - if(phtab[i].Type == PT_DYNAMIC) { - if(dynamicTab) { - Log_Warning("ELF", "Elf_Relocate - Multiple PT_DYNAMIC segments\n"); - continue; - } - dynamicTab = (void *) (tVAddr) phtab[i].VAddr; - j = i; // Save Dynamic Table ID - break; - } - } - - // Check if a PT_DYNAMIC segement was found - if(!dynamicTab) { - Log_Warning("ELF", "Elf_Relocate: No PT_DYNAMIC segment in image, returning\n"); - LEAVE('x', 0); - return 0; - } - - // Page Align real base - iRealBase &= ~0xFFF; - - // Adjust "Real" Base - iBaseDiff = (Uint)Base - iRealBase; - // Adjust Dynamic Table - dynamicTab = (void *) ((Uint)dynamicTab + iBaseDiff); - - // === Get Symbol table and String Table === - for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++) - { - switch(dynamicTab[j].d_tag) - { - // --- Symbol Table --- - case DT_SYMTAB: - dynamicTab[j].d_val += iBaseDiff; - dynsymtab = (void*) (tVAddr) dynamicTab[j].d_val; - hdr->misc.SymTable = dynamicTab[j].d_val; // Saved in unused bytes of ident - break; - - // --- String Table --- - case DT_STRTAB: - dynamicTab[j].d_val += iBaseDiff; - dynstrtab = (void*) (tVAddr) dynamicTab[j].d_val; - break; - - // --- Hash Table -- - case DT_HASH: - dynamicTab[j].d_val += iBaseDiff; - iSymCount = ((Uint*)((tVAddr)dynamicTab[j].d_val))[1]; - hdr->misc.HashTable = dynamicTab[j].d_val; // Saved in unused bytes of ident - break; - } - } - - if( !dynsymtab && iSymCount > 0 ) { - Log_Warning("ELF", "Elf_Relocate: No Dynamic symbol table, but count >0"); - return 0; - } - - // Alter Symbols to true base - for(i = 0; i < iSymCount; i ++) - { - dynsymtab[i].value += iBaseDiff; - dynsymtab[i].nameOfs += (Uint)dynstrtab; - //LOG("Sym '%s' = 0x%x (relocated)\n", dynsymtab[i].name, dynsymtab[i].value); - } - - // === Add to loaded list (can be imported now) === - //Binary_AddLoaded( (Uint)Base ); - - // === Parse Relocation Data === - for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++) - { - switch(dynamicTab[j].d_tag) - { - // --- Shared Library Name --- - case DT_SONAME: - LOG(".so Name '%s'\n", dynstrtab+dynamicTab[j].d_val); - break; - // --- Needed Library --- - case DT_NEEDED: - libPath = dynstrtab + dynamicTab[j].d_val; - Log_Notice("ELF", "%p - Required Library '%s' (Ignored in kernel mode)\n", Base, libPath); - break; - // --- PLT/GOT --- - case DT_PLTGOT: pltgot = (void*)(iBaseDiff+dynamicTab[j].d_val); break; - case DT_JMPREL: plt = (void*)(iBaseDiff+dynamicTab[j].d_val); break; - case DT_PLTREL: pltType = dynamicTab[j].d_val; break; - case DT_PLTRELSZ: pltSz = dynamicTab[j].d_val; break; - - // --- Relocation --- - case DT_REL: rel = (void*)(iBaseDiff + dynamicTab[j].d_val); break; - case DT_RELSZ: relSz = dynamicTab[j].d_val; break; - case DT_RELENT: relEntSz = dynamicTab[j].d_val; break; - - case DT_RELA: rela = (void*)(iBaseDiff + dynamicTab[j].d_val); break; - case DT_RELASZ: relaSz = dynamicTab[j].d_val; break; - case DT_RELAENT: relaEntSz = dynamicTab[j].d_val; break; - } - } - - // Parse Relocation Entries - if(rel && relSz) - { - j = relSz / relEntSz; - for( i = 0; i < j; i++ ) - { - ptr = (void*)(iBaseDiff + rel[i].r_offset); - if( !Elf_Int_DoRelocate(rel[i].r_info, ptr, *ptr, dynsymtab, (Uint)Base) ) { - bFailed = 1; - } - } - } - // Parse Relocation Entries - if(rela && relaSz) - { - j = relaSz / relaEntSz; - for( i = 0; i < j; i++ ) - { - ptr = (void*)(iBaseDiff + rela[i].r_offset); - if( !Elf_Int_DoRelocate(rela[i].r_info, ptr, rela[i].r_addend, dynsymtab, (Uint)Base) ) { - bFailed = 1; - } - } - } - - // === Process PLT (Procedure Linkage Table) === - if(plt && pltSz) - { - if(pltType == DT_REL) - { - Elf32_Rel *pltRel = plt; - j = pltSz / sizeof(Elf32_Rel); - LOG("PLT Rel - plt = %p, pltSz = %i (%i ents)", plt, pltSz, j); - for(i = 0; i < j; i++) - { - ptr = (void*)(iBaseDiff + pltRel[i].r_offset); - if( !Elf_Int_DoRelocate(pltRel[i].r_info, ptr, *ptr, dynsymtab, (Uint)Base) ) { - bFailed = 1; - } - } - } - else - { - Elf32_Rela *pltRela = plt; - j = pltSz / sizeof(Elf32_Rela); - LOG("PLT RelA - plt = %p, pltSz = %i (%i ents)", plt, pltSz, j); - for(i=0;imisc.HashTable; - symtab = (void *) hdr->misc.SymTable; - - nbuckets = pBuckets[0]; - iSymCount = pBuckets[1]; - pBuckets = &pBuckets[2]; - pChains = &pBuckets[ nbuckets ]; - - // Get hash - iNameHash = Elf_Int_HashString(Name); - iNameHash %= nbuckets; - - // Check Bucket - i = pBuckets[ iNameHash ]; - if(symtab[i].shndx != SHN_UNDEF && strcmp(symtab[i].name, Name) == 0) { - if(ret) *ret = symtab[ i ].value; - return 1; - } - - // Walk Chain - while(pChains[i] != STN_UNDEF) - { - i = pChains[i]; - if(symtab[i].shndx != SHN_UNDEF && strcmp(symtab[ i ].name, Name) == 0) { - if(ret) *ret = symtab[ i ].value; - return 1; - } - } - return 0; -} - -/** - * \fn Uint Elf_Int_HashString(char *str) - * \brief Hash a string in the ELF format - * \param str String to hash - * \return Hash value - */ -Uint Elf_Int_HashString(const char *str) -{ - Uint h = 0, g; - while(*str) - { - h = (h << 4) + *str++; - if( (g = h & 0xf0000000) ) - h ^= g >> 24; - h &= ~g; - } - return h; -} diff --git a/Kernel/bin/elf.h b/Kernel/bin/elf.h deleted file mode 100644 index 13b5373f..00000000 --- a/Kernel/bin/elf.h +++ /dev/null @@ -1,287 +0,0 @@ -/** - * \file elf.h - * \brief ELF Exeutable Loader - */ -#ifndef _BIN_ELF_H -#define _BIN_ELF_H - -typedef Uint16 Elf64_Half; -typedef Uint32 Elf64_Word; -typedef Uint64 Elf64_Addr; -typedef Uint64 Elf64_Off; -typedef Uint64 Elf64_Xword; - -enum e_ident_values -{ - EI_MAG0, - EI_MAG1, - EI_MAG2, - EI_MAG3, - EI_CLASS, - EI_DATA, - EI_VERSION, - EI_OSABI, - EI_ABIVERSION, - EI_PAD, - EI_NIDENT = 16, -}; - -#define ELFCLASS32 1 -#define ELFCLASS64 2 - -/** - * \brief ELF File Header - */ -struct sElf32_Ehdr -{ - union { - char ident[16]; //!< Identifier Bytes - struct { - Uint Ident1; - Uint Ident2; - Uint HashTable; - Uint SymTable; - } misc; - }; - Uint16 filetype; //!< File Type - Uint16 machine; //!< Machine / Arch - Uint32 version; //!< Version (File?) - Uint32 entrypoint; //!< Entry Point - Uint32 phoff; //!< Program Header Offset - Uint32 shoff; //!< Section Header Offset - Uint32 flags; //!< Flags - Uint16 headersize; //!< Header Size - Uint16 phentsize; //!< Program Header Entry Size - Uint16 phentcount; //!< Program Header Entry Count - Uint16 shentsize; //!< Section Header Entry Size - Uint16 shentcount; //!< Section Header Entry Count - Uint16 shstrindex; //!< Section Header String Table Index -} __attribute__ ((packed)); - -typedef struct -{ - unsigned char e_ident[16]; - Elf64_Half e_type; - Elf64_Half e_machine; - Elf64_Word e_version; - Elf64_Addr e_entry; - Elf64_Off e_phoff; - Elf64_Off e_shoff; - Elf64_Word e_flags; - Elf64_Half e_ehsize; - Elf64_Half e_phentsize; - Elf64_Half e_phnum; - Elf64_Half e_shentsize; - Elf64_Half e_shnum; - Elf64_Half e_shstrndx; -} Elf64_Ehdr; - -/** - * \brief Executable Types - */ -enum eElf32_ExecTypes -{ - ET_NONE = 0, //!< NULL Type - ET_REL = 1, //!< Relocatable (Object) - ET_EXEC = 2, //!< Executable - ET_DYN = 3, //!< Dynamic Library - ET_CORE = 4, //!< Core? - ET_LOPROC = 0xFF00, //!< Low Impl Defined - ET_HIPROC = 0xFFFF //!< High Impl Defined -}; - -/** - \name Section IDs - \{ -*/ -#define SHN_UNDEF 0 //!< Undefined Section -#define SHN_LORESERVE 0xFF00 //!< Low Reserved -#define SHN_LOPROC 0xFF00 //!< Low Impl Defined -#define SHN_HIPROC 0xFF1F //!< High Impl Defined -#define SHN_ABS 0xFFF1 //!< Absolute Address (Base: 0, Size: -1) -#define SHN_COMMON 0xFFF2 //!< Common -#define SHN_HIRESERVE 0xFFFF //!< High Reserved -//! \} - -/** - \enum eElfSectionTypes - \brief ELF Section Types -*/ -enum eElfSectionTypes { - SHT_NULL, //0 - SHT_PROGBITS, //1 - SHT_SYMTAB, //2 - SHT_STRTAB, //3 - SHT_RELA, //4 - SHT_HASH, //5 - SHT_DYNAMIC, //6 - SHT_NOTE, //7 - SHT_NOBITS, //8 - SHT_REL, //9 - SHT_SHLIB, //A - SHT_DYNSYM, //B - SHT_LAST, //C - SHT_LOPROC = 0x70000000, - SHT_HIPROC = 0x7fffffff, - SHT_LOUSER = 0x80000000, - SHT_HIUSER = 0xffffffff -}; - -#define SHF_WRITE 0x1 -#define SHF_ALLOC 0x2 -#define SHF_EXECINSTR 0x4 -#define SHF_MASKPROC 0xf0000000 - -struct sElf32_Shent { - Uint32 name; - Uint32 type; - Uint32 flags; - Uint32 address; - Uint32 offset; - Uint32 size; - Uint32 link; - Uint32 info; - Uint32 addralign; - Uint32 entsize; -} __attribute__ ((packed)); //sizeof = 40 - -typedef struct -{ - Elf64_Word sh_name; - Elf64_Word sh_type; - Elf64_Xword sh_flags; - Elf64_Addr sh_addr; - Elf64_Off sh_offset; - Elf64_Xword sh_size; - Elf64_Word sh_link; - Elf64_Word sh_info; - Elf64_Xword sh_addralign; - Elf64_Xword sh_entsize; -} Elf64_Shdr; - -struct elf_sym_s { - union { - Uint32 nameOfs; - char *name; - }; - Uint32 value; //Address - Uint32 size; - Uint8 info; - Uint8 other; - Uint16 shndx; -} __attribute__ ((packed)); -#define STN_UNDEF 0 // Undefined Symbol - -enum { - PT_NULL, //0 - PT_LOAD, //1 - PT_DYNAMIC, //2 - PT_INTERP, //3 - PT_NOTE, //4 - PT_SHLIB, //5 - PT_PHDR, //6 - PT_LOPROC = 0x70000000, - PT_HIPROC = 0x7fffffff -}; - -struct sElf32_Phdr { - Uint32 Type; - Uint32 Offset; - Uint32 VAddr; - Uint32 PAddr; - Uint32 FileSize; - Uint32 MemSize; - Uint32 Flags; - Uint32 Align; -} __attribute__ ((packed)); - -typedef struct -{ - Elf64_Word p_type; - Elf64_Word p_flags; - Elf64_Off p_offset; - Elf64_Addr p_vaddr; - Elf64_Addr p_paddr; - Elf64_Xword p_filesz; - Elf64_Xword p_memsz; - Elf64_Xword p_align; -} Elf64_Phdr; - -#define PF_X 1 -#define PF_W 2 -#define PF_R 4 - -struct elf32_rel_s { - Uint32 r_offset; - Uint32 r_info; -} __attribute__ ((packed)); - -struct elf32_rela_s { - Uint32 r_offset; - Uint32 r_info; - Sint32 r_addend; -} __attribute__ ((packed)); - -enum { - R_386_NONE = 0, // none - R_386_32, // S+A - R_386_PC32, // S+A-P - R_386_GOT32, // G+A-P - R_386_PLT32, // L+A-P - R_386_COPY, // none - R_386_GLOB_DAT, // S - R_386_JMP_SLOT, // S - R_386_RELATIVE, // B+A - R_386_GOTOFF, // S+A-GOT - R_386_GOTPC, // GOT+A-P - R_386_LAST // none -}; - -#define ELF32_R_SYM(i) ((i)>>8) // Takes an info value and returns a symbol index -#define ELF32_R_TYPE(i) ((i)&0xFF) // Takes an info value and returns a type -#define ELF32_R_INFO(s,t) (((s)<<8)+((t)&0xFF)) // Takes a type and symbol index and returns an info value - -struct elf32_dyn_s { - Uint32 d_tag; - Uint32 d_val; //Also d_ptr -} __attribute__ ((packed)); - -enum { - DT_NULL, //!< Marks End of list - DT_NEEDED, //!< Offset in strtab to needed library - DT_PLTRELSZ, //!< Size in bytes of PLT - DT_PLTGOT, //!< Address of PLT/GOT - DT_HASH, //!< Address of symbol hash table - DT_STRTAB, //!< String Table address - DT_SYMTAB, //!< Symbol Table address - DT_RELA, //!< Relocation table address - DT_RELASZ, //!< Size of relocation table - DT_RELAENT, //!< Size of entry in relocation table - DT_STRSZ, //!< Size of string table - DT_SYMENT, //!< Size of symbol table entry - DT_INIT, //!< Address of initialisation function - DT_FINI, //!< Address of termination function - DT_SONAME, //!< String table offset of so name - DT_RPATH, //!< String table offset of library path - DT_SYMBOLIC,//!< Reverse order of symbol searching for library, search libs first then executable - DT_REL, //!< Relocation Entries (Elf32_Rel instead of Elf32_Rela) - DT_RELSZ, //!< Size of above table (bytes) - DT_RELENT, //!< Size of entry in above table - DT_PLTREL, //!< Relocation entry of PLT - DT_DEBUG, //!< Debugging Entry - Unknown contents - DT_TEXTREL, //!< Indicates that modifcations to a non-writeable segment may occur - DT_JMPREL, //!< Address of PLT only relocation entries - DT_LOPROC = 0x70000000, //!< Low Definable - DT_HIPROC = 0x7FFFFFFF //!< High Definable -}; - -typedef struct sElf32_Ehdr Elf32_Ehdr; -typedef struct sElf32_Phdr Elf32_Phdr; -typedef struct sElf32_Shent Elf32_Shent; -typedef struct elf_sym_s elf_symtab; -typedef struct elf_sym_s Elf32_Sym; -typedef struct elf32_rel_s Elf32_Rel; -typedef struct elf32_rela_s Elf32_Rela; -typedef struct elf32_dyn_s Elf32_Dyn; - -#endif // defined(_EXE_ELF_H) diff --git a/Kernel/bin/pe.c b/Kernel/bin/pe.c deleted file mode 100644 index 0e7d1acd..00000000 --- a/Kernel/bin/pe.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Acess v1 - * Portable Executable Loader - */ -#define DEBUG 1 -#include -#include -#include -#include "pe.h" - -// === PROTOTYPES === - int PE_Install(char **Arguments); -tBinary *PE_Load(int fp); -tBinary *MZ_Open(int fp); - int PE_Relocate(void *Base); - int PE_GetSymbol(void *Base, const char *Name, Uint *Ret); - -// === GLOBALS === -MODULE_DEFINE(0, 0x0032, BinPE, PE_Install, NULL, NULL); -const char *gsPE_DefaultInterpreter = "/Acess/Libs/ld-acess.so"; -tBinaryType gPE_Loader = { - NULL, - ('M'|('Z'<<8)), 0xFFFF, // 'MZ' - "PE/DOS", - PE_Load, PE_Relocate, PE_GetSymbol - }; - -// === CODE === -int PE_Install(char **Arguments) -{ - Binary_RegisterType(&gPE_Loader); - return MODULE_ERR_OK; -} - -/** - * \brief Loads a PE Binary - */ -tBinary *PE_Load(int FP) -{ - int count, i, j; - int iSectCount; - tBinary *ret; - tPE_DOS_HEADER dosHdr; - tPE_IMAGE_HEADERS peHeaders; - tPE_SECTION_HEADER *peSections; - char namebuf[9] = {0}; - Uint iFlags, iVA; - - ENTER("xFP", FP); - - // Read DOS header and check - VFS_Read(FP, sizeof(tPE_DOS_HEADER), &dosHdr); - if( dosHdr.Ident != ('M'|('Z'<<8)) ) { - LEAVE('n'); - return NULL; - } - - // - Read PE Header - VFS_Seek(FP, dosHdr.PeHdrOffs, SEEK_SET); - if( VFS_Tell(FP) != dosHdr.PeHdrOffs ) { - ret = MZ_Open(FP); - LEAVE('p', ret); - return ret; - } - VFS_Read(FP, sizeof(tPE_IMAGE_HEADERS), &peHeaders); - - // - Check PE Signature and pass on to the MZ Loader if invalid - if( peHeaders.Signature != (('P')|('E'<<8)) ) { - ret = MZ_Open(FP); - LEAVE('p', ret); - return ret; - } - - // Read Sections (Uses `count` as a temp variable) - count = sizeof(tPE_SECTION_HEADER) * peHeaders.FileHeader.SectionCount; - peSections = malloc( count ); - if(!peSections) - { - Warning("PE_Load - Unable to allocate `peSections`, 0x%x bytes", count); - LEAVE('n'); - return NULL; - } - VFS_Read(FP, count, peSections); - - // Count Pages - iSectCount = 1; // 1st page is headers - for( i = 0; i < peHeaders.FileHeader.SectionCount; i++ ) - { - // Check if the section is loadable - // (VA is zero in non-loadable sections) - if(peSections[i].RVA + peHeaders.OptHeader.ImageBase == 0) continue; - - // Moar pages - iSectCount ++; - } - - LOG("%i Sections", iSectCount); - - // Initialise Executable Information - ret = malloc(sizeof(tBinary) + sizeof(tBinarySection)*iSectCount); - - ret->Entry = peHeaders.OptHeader.EntryPoint + peHeaders.OptHeader.ImageBase; - ret->Base = peHeaders.OptHeader.ImageBase; - ret->Interpreter = gsPE_DefaultInterpreter; - ret->NumSections = iSectCount; - - LOG("Entry=%p, BaseAddress=0x%x\n", ret->Entry, ret->Base); - - ret->LoadSections[0].Virtual = peHeaders.OptHeader.ImageBase; - ret->LoadSections[0].Offset = 0; - ret->LoadSections[0].FileSize = 4096; - ret->LoadSections[0].MemSize = 4096; - ret->LoadSections[0].Flags = 0; - - // Parse Sections - j = 1; // Page Index - for( i = 0; i < peHeaders.FileHeader.SectionCount; i++ ) - { - tBinarySection *sect = &ret->LoadSections[j]; - iVA = peSections[i].RVA + peHeaders.OptHeader.ImageBase; - - // Skip non-loadable sections - if(iVA == 0) continue; - - // Create Name Buffer - memcpy(namebuf, peSections[i].Name, 8); - LOG("Section %i '%s', iVA = %p", i, namebuf, iVA); - - // Create Flags - iFlags = 0; - if(peSections[i].Flags & PE_SECTION_FLAG_MEM_EXECUTE) - iFlags |= BIN_SECTFLAG_EXEC; - if( !(peSections[i].Flags & PE_SECTION_FLAG_MEM_WRITE) ) - iFlags |= BIN_SECTFLAG_RO; - - sect->Virtual = iVA; - sect->Offset = peSections[i].RawOffs; - sect->FileSize = peSections[i].RawSize; - sect->MemSize = peSections[i].VirtualSize; - sect->Flags = iFlags; - j ++; - - LOG("%i Name:'%s', RVA: 0x%x, Size: 0x%x (0x%x), Ofs: 0x%x, Flags: 0x%x", - i, namebuf, - iVA, - peSections[i].VirtualSize, peSections[i].RawSize, peSections[i].RawOffs, - peSections[i].Flags - ); - - } - // Free Executable Memory - free(peSections); - - LEAVE('p', ret); - return ret; -} - -/** - */ -tBinary *MZ_Open(int FP) -{ - ENTER("xFP", FP); - UNIMPLEMENTED(); - LEAVE('n'); - return NULL; -} - -int PE_Relocate(void *Base) -{ - tPE_DOS_HEADER *dosHdr; - tPE_IMAGE_HEADERS *peHeaders; - tPE_SECTION_HEADER *peSections; - tPE_DATA_DIR *directory; - tPE_IMPORT_DIR *impDir; - int i; - Uint iBase = (Uint)Base; - #if 0 - void *hLibrary; - char *libPath; - #endif - - ENTER("pBase", Base); - dosHdr = Base; - peHeaders = (void*)( iBase + dosHdr->PeHdrOffs ); - LOG("Prefered Base %p", peHeaders->OptHeader.ImageBase); - peSections = (void*)( iBase + sizeof(tPE_IMAGE_HEADERS) ); - - directory = (void*)(peSections[0].RVA + iBase); - - // === Load Import Tables - impDir = (void*)( directory[PE_DIR_IMPORT].RVA + iBase ); - for( i = 0; impDir[i].DLLName != NULL; i++ ) - { - impDir[i].DLLName += iBase; - impDir[i].ImportLookupTable += iBase/4; - impDir[i].ImportAddressTable += iBase/4; - LOG("DLL Required '%s'(0x%x)", impDir[i].DLLName, impDir[i].DLLName); - #if 0 - libPath = FindLibrary(impDir[i].DLLName); - if(libPath == NULL) - { - Warning("PE_Relocate - Unable to find library '%s'"); - LEAVE('i', -1); - return -1; - } - LOG("DLL Path = '%s'", libPath); - hLibrary = DynLib_Load(libPath, 0); - #endif - } - - for(i=0;i -#include -#include -#include -#include - -// === CONSTANTS === -#define BIN_LOWEST MM_USER_MIN // 1MiB -#define BIN_GRANUALITY 0x10000 // 64KiB -#define BIN_HIGHEST (USER_LIB_MAX-BIN_GRANUALITY) // Just below the kernel -#define KLIB_LOWEST MM_MODULE_MIN -#define KLIB_GRANUALITY 0x10000 // 32KiB -#define KLIB_HIGHEST (MM_MODULE_MAX-KLIB_GRANUALITY) - -// === TYPES === -typedef struct sKernelBin { - struct sKernelBin *Next; - void *Base; - tBinary *Info; -} tKernelBin; - -// === IMPORTS === -extern char *Threads_GetName(int ID); -extern tKernelSymbol gKernelSymbols[]; -extern tKernelSymbol gKernelSymbolsEnd[]; -extern tBinaryType gELF_Info; - -// === PROTOTYPES === - int Binary_int_CacheArgs(const char **Path, const char ***ArgV, const char ***EnvP, void *DestBuffer); -tVAddr Binary_Load(const char *Path, tVAddr *EntryPoint); -tBinary *Binary_GetInfo(tMount MountID, tInode InodeID); -tVAddr Binary_MapIn(tBinary *Binary, const char *Path, tVAddr LoadMin, tVAddr LoadMax); -tVAddr Binary_IsMapped(tBinary *Binary); -tBinary *Binary_DoLoad(tMount MountID, tInode Inode, const char *Path); -void Binary_Dereference(tBinary *Info); -#if 0 -Uint Binary_Relocate(void *Base); -#endif -Uint Binary_GetSymbolEx(const char *Name, Uint *Value); -#if 0 -Uint Binary_FindSymbol(void *Base, const char *Name, Uint *Val); -#endif - int Binary_int_CheckMemFree( tVAddr _start, size_t _len ); - -// === GLOBALS === -tShortSpinlock glBinListLock; -tBinary *glLoadedBinaries = NULL; -char **gsaRegInterps = NULL; - int giRegInterps = 0; -tShortSpinlock glKBinListLock; -tKernelBin *glLoadedKernelLibs; -tBinaryType *gRegBinTypes = &gELF_Info; - -// === FUNCTIONS === -/** - * \brief Registers a binary type - */ -int Binary_RegisterType(tBinaryType *Type) -{ - Type->Next = gRegBinTypes; - gRegBinTypes = Type; - return 1; -} - -/** - * \fn int Proc_Spawn(const char *Path) - */ -int Proc_Spawn(const char *Path) -{ - char stackPath[strlen(Path)+1]; - ENTER("sPath", Path); - - strcpy(stackPath, Path); - - LOG("stackPath = '%s'", stackPath); - - if(Proc_Clone(CLONE_VM|CLONE_NOUSER) == 0) - { - // CHILD - const char *args[2] = {stackPath, NULL}; - LOG("stackPath = '%s'", stackPath); - Proc_Execve(stackPath, args, &args[1], 0); - for(;;); - } - LEAVE('i', 0); - return 0; -} - -/** - * \todo Document - */ -int Binary_int_CacheArgs(const char **Path, const char ***ArgV, const char ***EnvP, void *DestBuffer) -{ - int size, argc=0, envc=0; - int i; - char *strbuf; - const char **arrays; - - // Calculate size - size = 0; - if( ArgV && *ArgV ) - { - const char **argv = *ArgV; - for( argc = 0; argv[argc]; argc ++ ) - size += strlen( argv[argc] ) + 1; - } - if( EnvP && *EnvP ) - { - const char **envp = *EnvP; - for( envc = 0; envp[envc]; envc ++ ) - size += strlen( envp[envc] ) + 1; - } - size = (size + sizeof(void*)-1) & ~(sizeof(void*)-1); // Word align - size += (argc+1+envc+1)*sizeof(void*); // Arrays - if( Path ) - { - size += strlen( *Path ) + 1; - } - - if( DestBuffer ) - { - arrays = DestBuffer; - strbuf = (void*)&arrays[argc+1+envc+1]; - - // Fill ArgV - if( ArgV && *ArgV ) - { - const char **argv = *ArgV; - for( i = 0; argv[i]; i ++ ) - { - arrays[i] = strbuf; - strcpy(strbuf, argv[i]); - strbuf += strlen( argv[i] ) + 1; - } - *ArgV = arrays; - arrays += i; - } - *arrays++ = NULL; - // Fill EnvP - if( EnvP && *EnvP ) - { - const char **envp = *EnvP; - for( i = 0; envp[i]; i ++ ) - { - arrays[i] = strbuf; - strcpy(strbuf, envp[i]); - strbuf += strlen( envp[i] ) + 1; - } - *EnvP = arrays; - arrays += i; - } - *arrays++ = NULL; - // Fill path - if( Path ) - { - strcpy(strbuf, *Path); - *Path = strbuf; - } - } - - return size; -} - -/** - * \brief Create a new process with the specified set of file descriptors - */ -int Proc_SysSpawn(const char *Binary, const char **ArgV, const char **EnvP, int nFD, int *FDs) -{ - void *handles; - void *cachebuf; - int size; - tPID ret; - - // --- Save File, ArgV and EnvP - size = Binary_int_CacheArgs( &Binary, &ArgV, &EnvP, NULL ); - cachebuf = malloc( size ); - Binary_int_CacheArgs( &Binary, &ArgV, &EnvP, cachebuf ); - - // Cache the VFS handles - handles = VFS_SaveHandles(nFD, FDs); - - // Create new process - ret = Proc_Clone(CLONE_VM|CLONE_NOUSER); - if( ret == 0 ) - { - VFS_RestoreHandles(nFD, handles); - VFS_FreeSavedHandles(nFD, handles); - // Frees cachebuf - Proc_Execve(Binary, ArgV, EnvP, size); - for(;;); - } - if( ret < 0 ) - { - VFS_FreeSavedHandles(nFD, handles); - } - - return ret; -} - -/** - * \brief Replace the current user image with another - * \param File File to load as the next image - * \param ArgV Arguments to pass to user - * \param EnvP User's environment - * \note Called Proc_ for historical reasons - */ -int Proc_Execve(const char *File, const char **ArgV, const char **EnvP, int DataSize) -{ - void *cachebuf; - tVAddr entry; - Uint base; // Uint because Proc_StartUser wants it - int argc; - - ENTER("sFile pArgV pEnvP", File, ArgV, EnvP); - - // --- Save File, ArgV and EnvP - if( DataSize == 0 ) - { - DataSize = Binary_int_CacheArgs( &File, &ArgV, &EnvP, NULL ); - cachebuf = malloc( DataSize ); - Binary_int_CacheArgs( &File, &ArgV, &EnvP, cachebuf ); - } - - // --- Get argc - for( argc = 0; ArgV && ArgV[argc]; argc ++ ); - - // --- Set Process Name - Threads_SetName(File); - - // --- Clear User Address space - // NOTE: This is a little roundabout, maybe telling ClearUser to not touch the - // PPD area would be a better idea. - { - int nfd = *Threads_GetMaxFD(); - void *handles; - handles = VFS_SaveHandles(nfd, NULL); - VFS_CloseAllUserHandles(); - MM_ClearUser(); - VFS_RestoreHandles(nfd, handles); - VFS_FreeSavedHandles(nfd, handles); - } - - // --- Load new binary - base = Binary_Load(File, &entry); - if(base == 0) - { - Log_Warning("Binary", "Proc_Execve - Unable to load '%s'", File); - LEAVE('-'); - Threads_Exit(0, -10); - for(;;); - } - - LOG("entry = 0x%x, base = 0x%x", entry, base); - LEAVE('-'); - // --- And... Jump to it - Proc_StartUser(entry, base, argc, ArgV, DataSize); - for(;;); // Tell GCC that we never return -} - -/** - * \brief Load a binary into the current address space - * \param Path Path to binary to load - * \param EntryPoint Pointer for exectuable entry point - * \return Virtual address where the binary has been loaded - */ -tVAddr Binary_Load(const char *Path, tVAddr *EntryPoint) -{ - tMount mount_id; - tInode inode; - tBinary *pBinary; - tVAddr base = -1; - - ENTER("sPath pEntryPoint", Path, EntryPoint); - - // Sanity Check Argument - if(Path == NULL) { - LEAVE('x', 0); - return 0; - } - - // Check if this path has been loaded before. - #if 0 - // TODO: Implement a list of string/tBinary pairs for loaded bins - #endif - - // Get Inode - { - int fd; - tFInfo info; - fd = VFS_Open(Path, VFS_OPENFLAG_READ|VFS_OPENFLAG_EXEC); - if( fd == -1 ) { - LOG("%s does not exist", Path); - LEAVE_RET('x', 0); - } - VFS_FInfo(fd, &info, 0); - VFS_Close(fd); - mount_id = info.mount; - inode = info.inode; - LOG("mount_id = %i, inode = %i", mount_id, inode); - } - - // TODO: Also get modifcation time? - - // Check if the binary has already been loaded - if( !(pBinary = Binary_GetInfo(mount_id, inode)) ) - pBinary = Binary_DoLoad(mount_id, inode, Path); // Else load it - - // Error Check - if(pBinary == NULL) { - LEAVE('x', 0); - return 0; - } - - // Map into process space - base = Binary_MapIn(pBinary, Path, BIN_LOWEST, BIN_HIGHEST); - - // Check for errors - if(base == 0) { - LEAVE('x', 0); - return 0; - } - - // Interpret - if(pBinary->Interpreter) { - tVAddr start; - if( Binary_Load(pBinary->Interpreter, &start) == 0 ) { - LEAVE('x', 0); - return 0; - } - *EntryPoint = start; - } - else - *EntryPoint = pBinary->Entry - pBinary->Base + base; - - // Return - LOG("*EntryPoint = 0x%x", *EntryPoint); - LEAVE('x', base); - return base; // Pass the base as an argument to the user if there is an interpreter -} - -/** - * \brief Finds a matching binary entry - * \param MountID Mountpoint ID of binary file - * \param InodeID Inode ID of the file - * \return Pointer to the binary definition (if already loaded) - */ -tBinary *Binary_GetInfo(tMount MountID, tInode InodeID) -{ - tBinary *pBinary; - for(pBinary = glLoadedBinaries; pBinary; pBinary = pBinary->Next) - { - if(pBinary->MountID == MountID && pBinary->Inode == InodeID) - return pBinary; - } - return NULL; -} - -/** - * \brief Maps an already-loaded binary into an address space. - * \param Binary Pointer to globally stored binary definition - * \param Path Path to the binary's file (for debug) - * \param LoadMin Lowest location to map to - * \param LoadMax Highest location to map to - * \return Base load address - */ -tVAddr Binary_MapIn(tBinary *Binary, const char *Path, tVAddr LoadMin, tVAddr LoadMax) -{ - tVAddr base; - int i, fd; - - ENTER("pBinary sPath pLoadMin pLoadMax", Binary, Path, LoadMin, LoadMax); - - // Reference Executable (Makes sure that it isn't unloaded) - Binary->ReferenceCount ++; - - // Get Binary Base - base = Binary->Base; - - // Check if base is free - if(base != 0) - { - LOG("Checking base %p", base); - for( i = 0; i < Binary->NumSections; i ++ ) - { - if( Binary_int_CheckMemFree( Binary->LoadSections[i].Virtual, Binary->LoadSections[i].MemSize ) ) - { - base = 0; - LOG("Address 0x%x is taken\n", Binary->LoadSections[i].Virtual); - break; - } - } - } - - // Check if the executable has no base or it is not free - if(base == 0) - { - // If so, give it a base - base = LoadMax; - while(base >= LoadMin) - { - for( i = 0; i < Binary->NumSections; i ++ ) - { - tVAddr addr = Binary->LoadSections[i].Virtual - Binary->Base + base; - if( Binary_int_CheckMemFree( addr, Binary->LoadSections[i].MemSize ) ) - break; - } - // If space was found, break - if(i == Binary->NumSections) break; - // Else decrement pointer and try again - base -= BIN_GRANUALITY; - } - LOG("Allocated base %p", base); - } - - // Error Check - if(base < LoadMin) { - Log_Warning("Binary", "Executable '%s' cannot be loaded, no space", Path); - LEAVE('i', 0); - return 0; - } - - // Map Executable In - fd = VFS_OpenInode(Binary->MountID, Binary->Inode, VFS_OPENFLAG_READ); - for( i = 0; i < Binary->NumSections; i ++ ) - { - tBinarySection *sect = &Binary->LoadSections[i]; - Uint protflags, mapflags; - tVAddr addr = sect->Virtual - Binary->Base + base; - LOG("%i - %p to offset 0x%llx (%x)", i, addr, sect->Offset, sect->Flags); - - protflags = MMAP_PROT_READ; - mapflags = MMAP_MAP_FIXED; - - if( sect->Flags & BIN_SECTFLAG_EXEC ) - protflags |= MMAP_PROT_EXEC; - // Read only pages are COW - if( sect->Flags & BIN_SECTFLAG_RO ) { - VFS_MMap( (void*)addr, sect->FileSize, protflags, MMAP_MAP_SHARED|mapflags, fd, sect->Offset ); - } - else { - protflags |= MMAP_PROT_WRITE; - VFS_MMap( (void*)addr, sect->FileSize, protflags, MMAP_MAP_PRIVATE|mapflags, fd, sect->Offset ); - } - - // Apply anonymous memory for BSS - if( sect->FileSize < sect->MemSize ) { - mapflags |= MMAP_MAP_ANONYMOUS; - VFS_MMap( - (void*)(addr + sect->FileSize), sect->MemSize - sect->FileSize, - protflags, MMAP_MAP_PRIVATE|mapflags, - 0, 0 - ); - } - } - - Log_Debug("Binary", "PID %i - Mapped '%s' to %p", Threads_GetPID(), Path, base); - VFS_Close(fd); - - LEAVE('p', base); - return base; -} - -#if 0 -/** - * \fn Uint Binary_IsMapped(tBinary *binary) - * \brief Check if a binary is already mapped into the address space - * \param binary Binary information to check - * \return Current Base or 0 - */ -Uint Binary_IsMapped(tBinary *binary) -{ - Uint iBase; - - // Check prefered base - iBase = binary->Base; - if(MM_GetPage( iBase ) == (binary->Pages[0].Physical & ~0xFFF)) - return iBase; - - for(iBase = BIN_HIGHEST; - iBase >= BIN_LOWEST; - iBase -= BIN_GRANUALITY) - { - if(MM_GetPage( iBase ) == (binary->Pages[0].Physical & ~0xFFF)) - return iBase; - } - - return 0; -} -#endif - -/** - * \fn tBinary *Binary_DoLoad(char *truePath) - * \brief Loads a binary file into memory - * \param truePath Absolute filename of binary - */ -tBinary *Binary_DoLoad(tMount MountID, tInode Inode, const char *Path) -{ - tBinary *pBinary; - int fp; - Uint32 ident; - tBinaryType *bt = gRegBinTypes; - - ENTER("iMountID XInode sPath", MountID, Inode, Path); - - // Open File - fp = VFS_OpenInode(MountID, Inode, VFS_OPENFLAG_READ); - if(fp == -1) { - LOG("Unable to load file, access denied"); - LEAVE('n'); - return NULL; - } - - LOG("fp = 0x%x", fp); - - // Read File Type - VFS_Read(fp, 4, &ident); - VFS_Seek(fp, 0, SEEK_SET); - - LOG("ident = 0x%x", ident); - - // Determine the type - for(; bt; bt = bt->Next) - { - if( (ident & bt->Mask) != (Uint32)bt->Ident ) - continue; - LOG("bt = %p (%s)", bt, bt->Name); - pBinary = bt->Load(fp); - break; - } - - // Close File - VFS_Close(fp); - - // Catch errors - if(!bt) { - Log_Warning("Binary", "'%s' is an unknown file type. (%02x %02x %02x %02x)", - Path, ident&0xFF, (ident>>8)&0xFF, (ident>>16)&0xFF, (ident>>24)&0xFF); - LEAVE('n'); - return NULL; - } - - LOG("pBinary = %p", pBinary); - - // Error Check - if(pBinary == NULL) { - LEAVE('n'); - return NULL; - } - - // Initialise Structure - pBinary->ReferenceCount = 0; - pBinary->MountID = MountID; - pBinary->Inode = Inode; - - // Debug Information - LOG("Interpreter: '%s'", pBinary->Interpreter); - LOG("Base: 0x%x, Entry: 0x%x", pBinary->Base, pBinary->Entry); - LOG("NumSections: %i", pBinary->NumSections); - - // Add to the list - SHORTLOCK(&glBinListLock); - pBinary->Next = glLoadedBinaries; - glLoadedBinaries = pBinary; - SHORTREL(&glBinListLock); - - // TODO: Register the path with the binary - - // Return - LEAVE('p', pBinary); - return pBinary; -} - -/** - * \fn void Binary_Unload(void *Base) - * \brief Unload / Unmap a binary - * \param Base Loaded Base - * \note Currently used only for kernel libaries - */ -void Binary_Unload(void *Base) -{ - tKernelBin *pKBin; - tKernelBin *prev = NULL; - int i; - - if((Uint)Base < 0xC0000000) - { - // TODO: User Binaries - Log_Warning("BIN", "Unloading user binaries is currently unimplemented"); - return; - } - - // Kernel Libraries - for(pKBin = glLoadedKernelLibs; - pKBin; - prev = pKBin, pKBin = pKBin->Next) - { - // Check the base - if(pKBin->Base != Base) continue; - // Deallocate Memory - for(i = 0; i < pKBin->Info->NumSections; i++) - { - // TODO: VFS_MUnmap(); - } - // Dereference Binary - Binary_Dereference( pKBin->Info ); - // Remove from list - if(prev) prev->Next = pKBin->Next; - else glLoadedKernelLibs = pKBin->Next; - // Free Kernel Lib - free(pKBin); - return; - } -} - -/** - * \fn void Binary_Dereference(tBinary *Info) - * \brief Dereferences and if nessasary, deletes a binary - * \param Info Binary information structure - */ -void Binary_Dereference(tBinary *Info) -{ - // Decrement reference count - Info->ReferenceCount --; - - // Check if it is still in use - if(Info->ReferenceCount) return; - - /// \todo Implement binary freeing -} - -/** - * \fn char *Binary_RegInterp(char *Path) - * \brief Registers an Interpreter - * \param Path Path to interpreter provided by executable - */ -char *Binary_RegInterp(char *Path) -{ - int i; - // NULL Check Argument - if(Path == NULL) return NULL; - // NULL Check the array - if(gsaRegInterps == NULL) - { - giRegInterps = 1; - gsaRegInterps = malloc( sizeof(char*) ); - gsaRegInterps[0] = malloc( strlen(Path) ); - strcpy(gsaRegInterps[0], Path); - return gsaRegInterps[0]; - } - - // Scan Array - for( i = 0; i < giRegInterps; i++ ) - { - if(strcmp(gsaRegInterps[i], Path) == 0) - return gsaRegInterps[i]; - } - - // Interpreter is not in list - giRegInterps ++; - gsaRegInterps = malloc( sizeof(char*)*giRegInterps ); - gsaRegInterps[i] = malloc( strlen(Path) ); - strcpy(gsaRegInterps[i], Path); - return gsaRegInterps[i]; -} - -// ============ -// Kernel Binary Handling -// ============ -/** - * \fn void *Binary_LoadKernel(const char *File) - * \brief Load a binary into kernel space - * \note This function shares much with #Binary_Load, but does it's own mapping - * \param File File to load into the kernel - */ -void *Binary_LoadKernel(const char *File) -{ - tBinary *pBinary; - tKernelBin *pKBinary; - tVAddr base = -1; - tMount mount_id; - tInode inode; - - ENTER("sFile", File); - - // Sanity Check Argument - if(File == NULL) { - LEAVE('n'); - return 0; - } - - { - int fd = VFS_Open(File, VFS_OPENFLAG_READ); - tFInfo info; - if(fd == -1) { - LEAVE('n'); - return NULL; - } - VFS_FInfo(fd, &info, 0); - mount_id = info.mount; - inode = info.inode; - VFS_Close(fd); - } - - // Check if the binary has already been loaded - if( (pBinary = Binary_GetInfo(mount_id, inode)) ) - { - for(pKBinary = glLoadedKernelLibs; - pKBinary; - pKBinary = pKBinary->Next ) - { - if(pKBinary->Info == pBinary) { - LEAVE('p', pKBinary->Base); - return pKBinary->Base; - } - } - } - else - pBinary = Binary_DoLoad(mount_id, inode, File); // Else load it - - // Error Check - if(pBinary == NULL) { - LEAVE('n'); - return NULL; - } - - // -------------- - // Now pBinary is valid (either freshly loaded or only user mapped) - // So, map it into kernel space - // -------------- - - // Reference Executable (Makes sure that it isn't unloaded) - pBinary->ReferenceCount ++; - - Binary_MapIn(pBinary, File, KLIB_LOWEST, KLIB_HIGHEST); - - // Relocate Library - if( !Binary_Relocate( (void*)base ) ) - { - Log_Warning("Binary", "Relocation of '%s' failed, unloading", File); - Binary_Unload( (void*)base ); - Binary_Dereference( pBinary ); - LEAVE('n'); - return 0; - } - - // Add to list (relocator must look at itself manually, not via Binary_GetSymbol) - pKBinary = malloc(sizeof(*pKBinary)); - pKBinary->Base = (void*)base; - pKBinary->Info = pBinary; - SHORTLOCK( &glKBinListLock ); - pKBinary->Next = glLoadedKernelLibs; - glLoadedKernelLibs = pKBinary; - SHORTREL( &glKBinListLock ); - - LEAVE('p', base); - return (void*)base; -} - -/** - * \fn Uint Binary_Relocate(void *Base) - * \brief Relocates a loaded binary (used by kernel libraries) - * \param Base Loaded base address of binary - * \return Boolean Success - */ -Uint Binary_Relocate(void *Base) -{ - Uint32 ident = *(Uint32*) Base; - tBinaryType *bt = gRegBinTypes; - - for(; bt; bt = bt->Next) - { - if( (ident & bt->Mask) == (Uint)bt->Ident ) - return bt->Relocate( (void*)Base); - } - - Log_Warning("BIN", "%p is an unknown file type. (%02x %02x %02x %02x)", - Base, ident&0xFF, (ident>>8)&0xFF, (ident>>16)&0xFF, (ident>>24)&0xFF); - return 0; -} - -/** - * \fn int Binary_GetSymbol(char *Name, Uint *Val) - * \brief Get a symbol value - * \return Value of symbol or -1 on error - * - * Gets the value of a symbol from either the currently loaded - * libraries or the kernel's exports. - */ -int Binary_GetSymbol(const char *Name, Uint *Val) -{ - if( Binary_GetSymbolEx(Name, Val) ) return 1; - return 0; -} - -/** - * \fn Uint Binary_GetSymbolEx(char *Name, Uint *Value) - * \brief Get a symbol value - * - * Gets the value of a symbol from either the currently loaded - * libraries or the kernel's exports. - */ -Uint Binary_GetSymbolEx(const char *Name, Uint *Value) -{ - int i; - tKernelBin *pKBin; - int numKSyms = ((Uint)&gKernelSymbolsEnd-(Uint)&gKernelSymbols)/sizeof(tKernelSymbol); - - // Scan Kernel - for( i = 0; i < numKSyms; i++ ) - { - if(strcmp(Name, gKernelSymbols[i].Name) == 0) { - *Value = gKernelSymbols[i].Value; - return 1; - } - } - - // Scan Loaded Libraries - for(pKBin = glLoadedKernelLibs; - pKBin; - pKBin = pKBin->Next ) - { - if( Binary_FindSymbol(pKBin->Base, Name, Value) ) { - return 1; - } - } - - Log_Warning("BIN", "Unable to find symbol '%s'", Name); - return 0; -} - -/** - * \fn Uint Binary_FindSymbol(void *Base, char *Name, Uint *Val) - * \brief Get a symbol from the specified library - * \param Base Base address - * \param Name Name of symbol to find - * \param Val Pointer to place final value - */ -Uint Binary_FindSymbol(void *Base, const char *Name, Uint *Val) -{ - Uint32 ident = *(Uint32*) Base; - tBinaryType *bt = gRegBinTypes; - - for(; bt; bt = bt->Next) - { - if( (ident & bt->Mask) == (Uint)bt->Ident ) - return bt->GetSymbol(Base, Name, Val); - } - - Log_Warning("BIN", "Binary_FindSymbol - %p is an unknown file type. (%02x %02x %02x %02x)", - Base, ident&0xFF, ident>>8, ident>>16, ident>>24); - return 0; -} - -/** - * \brief Check if a range of memory is fully free - * \return Inverse boolean free (0 if all pages are unmapped) - */ -int Binary_int_CheckMemFree( tVAddr _start, size_t _len ) -{ - _len += _start & (PAGE_SIZE-1); - _len = (_len + PAGE_SIZE - 1) & ~(PAGE_SIZE-1); - _start &= ~(PAGE_SIZE-1); - for( ; _len > PAGE_SIZE; _len -= PAGE_SIZE, _start += PAGE_SIZE ) { - if( MM_GetPhysAddr(_start) != 0 ) - return 1; - } - if( _len == PAGE_SIZE && MM_GetPhysAddr(_start) != 0 ) - return 1; - return 0; -} - - -// === EXPORTS === -EXPORT(Binary_FindSymbol); -EXPORT(Binary_Unload); diff --git a/Kernel/debug.c b/Kernel/debug.c deleted file mode 100644 index 355624d5..00000000 --- a/Kernel/debug.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - * AcessOS Microkernel Version - * debug.c - * - * TODO: Move the Debug_putchar methods out to the arch/ tree - */ -#include -#include - -#define DEBUG_MAX_LINE_LEN 256 - -#define LOCK_DEBUG_OUTPUT 1 - -#define TRACE_TO_KTERM 0 - -// === IMPORTS === -extern void Threads_Dump(void); -extern void KernelPanic_SetMode(void); -extern void KernelPanic_PutChar(char Ch); - -// === PROTOTYPES === -static void Debug_Putchar(char ch); -static void Debug_Puts(int bUseKTerm, const char *Str); -void Debug_DbgOnlyFmt(const char *format, va_list args); -void Debug_FmtS(int bUseKTerm, const char *format, ...); -void Debug_Fmt(int bUseKTerm, const char *format, va_list args); -void Debug_SetKTerminal(const char *File); - -// === GLOBALS === - int gDebug_Level = 0; - int giDebug_KTerm = -1; - int gbDebug_IsKPanic = 0; -volatile int gbInPutChar = 0; -#if LOCK_DEBUG_OUTPUT -tShortSpinlock glDebug_Lock; -#endif - -// === CODE === -static void Debug_Putchar(char ch) -{ - Debug_PutCharDebug(ch); - if( !gbDebug_IsKPanic ) - { - if(gbInPutChar) return ; - gbInPutChar = 1; - if(giDebug_KTerm != -1) - VFS_Write(giDebug_KTerm, 1, &ch); - gbInPutChar = 0; - } - else - KernelPanic_PutChar(ch); -} - -static void Debug_Puts(int UseKTerm, const char *Str) -{ - int len = 0; - - Debug_PutStringDebug(Str); - - if( gbDebug_IsKPanic ) - { - for( len = 0; Str[len]; len ++ ) - KernelPanic_PutChar( Str[len] ); - } - else - for( len = 0; Str[len]; len ++ ); - - // Output to the kernel terminal - if( UseKTerm && !gbDebug_IsKPanic && giDebug_KTerm != -1) - { - if(gbInPutChar) return ; - gbInPutChar = 1; - VFS_Write(giDebug_KTerm, len, Str); - gbInPutChar = 0; - } -} - -void Debug_DbgOnlyFmt(const char *format, va_list args) -{ - Debug_Fmt(0, format, args); -} - -void Debug_Fmt(int bUseKTerm, const char *format, va_list args) -{ - char buf[DEBUG_MAX_LINE_LEN]; - int len; - buf[DEBUG_MAX_LINE_LEN-1] = 0; - len = vsnprintf(buf, DEBUG_MAX_LINE_LEN-1, format, args); - //if( len < DEBUG_MAX_LINE ) - // do something - Debug_Puts(bUseKTerm, buf); - return ; -} - -void Debug_FmtS(int bUseKTerm, const char *format, ...) -{ - va_list args; - va_start(args, format); - Debug_Fmt(bUseKTerm, format, args); - va_end(args); -} - -void Debug_KernelPanic(void) -{ - #if LOCK_DEBUG_OUTPUT - SHORTREL(&glDebug_Lock); - #endif - gbDebug_IsKPanic = 1; - KernelPanic_SetMode(); -} - -/** - * \fn void LogF(const char *Msg, ...) - * \brief Raw debug log (no new line, no prefix) - */ -void LogF(const char *Fmt, ...) -{ - va_list args; - - #if LOCK_DEBUG_OUTPUT - SHORTLOCK(&glDebug_Lock); - #endif - - va_start(args, Fmt); - - Debug_Fmt(1, Fmt, args); - - va_end(args); - - #if LOCK_DEBUG_OUTPUT - SHORTREL(&glDebug_Lock); - #endif -} -/** - * \fn void Debug(const char *Msg, ...) - * \brief Print only to the debug channel (not KTerm) - */ -void Debug(const char *Fmt, ...) -{ - va_list args; - - #if LOCK_DEBUG_OUTPUT - SHORTLOCK(&glDebug_Lock); - #endif - - Debug_Puts(0, "Debug: "); - va_start(args, Fmt); - Debug_DbgOnlyFmt(Fmt, args); - va_end(args); - Debug_PutCharDebug('\r'); - Debug_PutCharDebug('\n'); - #if LOCK_DEBUG_OUTPUT - SHORTREL(&glDebug_Lock); - #endif -} -/** - * \fn void Log(const char *Msg, ...) - */ -void Log(const char *Fmt, ...) -{ - va_list args; - - #if LOCK_DEBUG_OUTPUT - SHORTLOCK(&glDebug_Lock); - #endif - - Debug_Puts(1, "Log: "); - va_start(args, Fmt); - Debug_Fmt(1, Fmt, args); - va_end(args); - Debug_Puts(1, "\r\n"); - - #if LOCK_DEBUG_OUTPUT - SHORTREL(&glDebug_Lock); - #endif -} -void Warning(const char *Fmt, ...) -{ - va_list args; - - #if LOCK_DEBUG_OUTPUT - SHORTLOCK(&glDebug_Lock); - #endif - - Debug_Puts(1, "Warning: "); - va_start(args, Fmt); - Debug_Fmt(1, Fmt, args); - va_end(args); - Debug_Putchar('\r'); - Debug_Putchar('\n'); - - #if LOCK_DEBUG_OUTPUT - SHORTREL(&glDebug_Lock); - #endif -} -void Panic(const char *Fmt, ...) -{ - va_list args; - - #if LOCK_DEBUG_OUTPUT - SHORTLOCK(&glDebug_Lock); - #endif - // And never SHORTREL - - Debug_KernelPanic(); - - Debug_Puts(1, "Panic: "); - va_start(args, Fmt); - Debug_Fmt(1, Fmt, args); - va_end(args); - Debug_Putchar('\r'); - Debug_Putchar('\n'); - - Threads_Dump(); - - for(;;) ; -} - -void Debug_SetKTerminal(const char *File) -{ - int tmp; - if(giDebug_KTerm != -1) { - tmp = giDebug_KTerm; - giDebug_KTerm = -1; - VFS_Close(tmp); - } - tmp = VFS_Open(File, VFS_OPENFLAG_WRITE); -// Log_Log("Debug", "Opened '%s' as 0x%x", File, tmp); - giDebug_KTerm = tmp; -// Log_Log("Debug", "Returning to %p", __builtin_return_address(0)); -} - -void Debug_Enter(const char *FuncName, const char *ArgTypes, ...) -{ - va_list args; - int i; - int pos; - tTID tid = Threads_GetTID(); - - #if LOCK_DEBUG_OUTPUT - SHORTLOCK(&glDebug_Lock); - #endif - - i = gDebug_Level ++; - - va_start(args, ArgTypes); - - Debug_FmtS(TRACE_TO_KTERM, "%014lli ", now()); - while(i--) Debug_Puts(TRACE_TO_KTERM, " "); - - Debug_Puts(TRACE_TO_KTERM, FuncName); - Debug_FmtS(TRACE_TO_KTERM, "[%i]", tid); - Debug_Puts(TRACE_TO_KTERM, ": ("); - - while(*ArgTypes) - { - pos = strpos(ArgTypes, ' '); - if(pos == -1 || pos > 1) { - if(pos == -1) - Debug_Puts(TRACE_TO_KTERM, ArgTypes+1); - else { - Debug_FmtS(TRACE_TO_KTERM, "%.*s", pos-1, ArgTypes+1); - } - Debug_Puts(TRACE_TO_KTERM, "="); - } - switch(*ArgTypes) - { - case 'p': Debug_FmtS(TRACE_TO_KTERM, "%p", va_arg(args, void*)); break; - case 'P': Debug_FmtS(TRACE_TO_KTERM, "%P", va_arg(args, tPAddr)); break; - case 's': Debug_FmtS(TRACE_TO_KTERM, "'%s'", va_arg(args, char*)); break; - case 'i': Debug_FmtS(TRACE_TO_KTERM, "%i", va_arg(args, int)); break; - case 'u': Debug_FmtS(TRACE_TO_KTERM, "%u", va_arg(args, Uint)); break; - case 'x': Debug_FmtS(TRACE_TO_KTERM, "0x%x", va_arg(args, Uint)); break; - case 'b': Debug_FmtS(TRACE_TO_KTERM, "0b%b", va_arg(args, Uint)); break; - case 'X': Debug_FmtS(TRACE_TO_KTERM, "0x%llx", va_arg(args, Uint64)); break; // Extended (64-Bit) - case 'B': Debug_FmtS(TRACE_TO_KTERM, "0b%llb", va_arg(args, Uint64)); break; // Extended (64-Bit) - } - if(pos != -1) { - Debug_Puts(TRACE_TO_KTERM, ", "); - } - - if(pos == -1) break; - ArgTypes = &ArgTypes[pos+1]; - } - - va_end(args); - Debug_Puts(TRACE_TO_KTERM, ")\r\n"); - - #if LOCK_DEBUG_OUTPUT - SHORTREL(&glDebug_Lock); - #endif -} - -void Debug_Log(const char *FuncName, const char *Fmt, ...) -{ - va_list args; - int i = gDebug_Level; - tTID tid = Threads_GetTID(); - - #if LOCK_DEBUG_OUTPUT - SHORTLOCK(&glDebug_Lock); - #endif - - Debug_FmtS(TRACE_TO_KTERM, "%014lli ", now()); - while(i--) Debug_Puts(TRACE_TO_KTERM, " "); - - Debug_Puts(TRACE_TO_KTERM, FuncName); - Debug_FmtS(TRACE_TO_KTERM, "[%i]", tid); - Debug_Puts(TRACE_TO_KTERM, ": "); - - va_start(args, Fmt); - Debug_Fmt(TRACE_TO_KTERM, Fmt, args); - va_end(args); - - Debug_Puts(TRACE_TO_KTERM, "\r\n"); - - #if LOCK_DEBUG_OUTPUT - SHORTREL(&glDebug_Lock); - #endif -} - -void Debug_Leave(const char *FuncName, char RetType, ...) -{ - va_list args; - int i; - tTID tid = Threads_GetTID(); - - #if LOCK_DEBUG_OUTPUT - SHORTLOCK(&glDebug_Lock); - #endif - - i = --gDebug_Level; - - va_start(args, RetType); - - if( i == -1 ) { - gDebug_Level = 0; - i = 0; - } - Debug_FmtS(TRACE_TO_KTERM, "%014lli ", now()); - // Indenting - while(i--) Debug_Puts(TRACE_TO_KTERM, " "); - - Debug_Puts(TRACE_TO_KTERM, FuncName); - Debug_FmtS(TRACE_TO_KTERM, "[%i]", tid); - Debug_Puts(TRACE_TO_KTERM, ": RETURN"); - - // No Return - if(RetType == '-') { - Debug_Puts(TRACE_TO_KTERM, "\r\n"); - #if LOCK_DEBUG_OUTPUT - SHORTREL(&glDebug_Lock); - #endif - return; - } - - switch(RetType) - { - case 'n': Debug_Puts(TRACE_TO_KTERM, " NULL"); break; - case 'p': Debug_Fmt(TRACE_TO_KTERM, " %p", args); break; - case 'P': Debug_Fmt(TRACE_TO_KTERM, " %P", args); break; // PAddr - case 's': Debug_Fmt(TRACE_TO_KTERM, " '%s'", args); break; - case 'i': Debug_Fmt(TRACE_TO_KTERM, " %i", args); break; - case 'u': Debug_Fmt(TRACE_TO_KTERM, " %u", args); break; - case 'x': Debug_Fmt(TRACE_TO_KTERM, " 0x%x", args); break; - // Extended (64-Bit) - case 'X': Debug_Fmt(TRACE_TO_KTERM, " 0x%llx", args); break; - } - Debug_Puts(TRACE_TO_KTERM, "\r\n"); - - va_end(args); - - #if LOCK_DEBUG_OUTPUT - SHORTREL(&glDebug_Lock); - #endif -} - -void Debug_HexDump(const char *Header, const void *Data, Uint Length) -{ - const Uint8 *cdat = Data; - Uint pos = 0; - LogF("%014lli ", now()); - Debug_Puts(1, Header); - LogF(" (Hexdump of %p)\r\n", Data); - - #define CH(n) ((' '<=cdat[(n)]&&cdat[(n)]<0x7F) ? cdat[(n)] : '.') - - while(Length >= 16) - { - LogF("%014lli Log: %04x:" - " %02x %02x %02x %02x %02x %02x %02x %02x" - " %02x %02x %02x %02x %02x %02x %02x %02x" - " %c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c\r\n", - now(), - pos, - cdat[ 0], cdat[ 1], cdat[ 2], cdat[ 3], cdat[ 4], cdat[ 5], cdat[ 6], cdat[ 7], - cdat[ 8], cdat[ 9], cdat[10], cdat[11], cdat[12], cdat[13], cdat[14], cdat[15], - CH(0), CH(1), CH(2), CH(3), CH(4), CH(5), CH(6), CH(7), - CH(8), CH(9), CH(10), CH(11), CH(12), CH(13), CH(14), CH(15) - ); - Length -= 16; - cdat += 16; - pos += 16; - } - - { - int i ; - LogF("%014lli Log: %04x: ", now(), pos); - for(i = 0; i < Length; i ++) - { - LogF("%02x ", cdat[i]); - } - for( ; i < 16; i ++) LogF(" "); - LogF(" "); - for(i = 0; i < Length; i ++) - { - if( i == 8 ) LogF(" "); - LogF("%c", CH(i)); - } - - Debug_Putchar('\r'); - Debug_Putchar('\n'); - } -} - -// --- EXPORTS --- -EXPORT(Debug); -EXPORT(Log); -EXPORT(Warning); -EXPORT(Debug_Enter); -EXPORT(Debug_Log); -EXPORT(Debug_Leave); diff --git a/Kernel/drv/Makefile b/Kernel/drv/Makefile deleted file mode 100644 index 44f17cde..00000000 --- a/Kernel/drv/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Acess2 Module/Driver Templater Makefile -# Makefile.tpl - --include ../../Makefile.cfg - -CPPFLAGS = -I../include -I../arch/$(ARCHDIR)/include -DARCH=$(ARCH) -DBUILD_MODULE -CFLAGS = -Wall -Werror $(CPPFLAGS) - -.PHONY: all clean - -all: bochsvbe.kmd - -%.kmd: %.o - $(CC) -shared -nostdlib -o $@ $< - -%.o: %.c - $(CC) $(CFLAGS) -o $@ -c $< - -#ata_x86.kmd: ata_x86.o -#bochsvbe.kmd: bochsvbe.o diff --git a/Kernel/drv/fifo.c b/Kernel/drv/fifo.c deleted file mode 100644 index 0779a60b..00000000 --- a/Kernel/drv/fifo.c +++ /dev/null @@ -1,436 +0,0 @@ -/* AcessOS - * FIFO Pipe Driver - */ -#define DEBUG 0 -#include -#include -#include -#include - -// === CONSTANTS === -#define DEFAULT_RING_SIZE 2048 -#define PF_BLOCKING 1 - -// === TYPES === -typedef struct sPipe { - struct sPipe *Next; - char *Name; - tVFS_Node Node; - Uint Flags; - int ReadPos; - int WritePos; - int BufSize; - char *Buffer; -} tPipe; - -// === PROTOTYPES === - int FIFO_Install(char **Arguments); - int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data); -char *FIFO_ReadDir(tVFS_Node *Node, int Id); -tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename); - int FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags); -void FIFO_Reference(tVFS_Node *Node); -void FIFO_Close(tVFS_Node *Node); - int FIFO_Relink(tVFS_Node *Node, const char *OldName, const char *NewName); -Uint64 FIFO_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -Uint64 FIFO_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); -tPipe *FIFO_Int_NewPipe(int Size, const char *Name); - -// === GLOBALS === -MODULE_DEFINE(0, 0x0032, FIFO, FIFO_Install, NULL, NULL); -tVFS_NodeType gFIFO_DirNodeType = { - .TypeName = "FIFO Dir Node", - .ReadDir = FIFO_ReadDir, - .FindDir = FIFO_FindDir, - .MkNod = FIFO_MkNod, - .Relink = FIFO_Relink, - .IOCtl = FIFO_IOCtl -}; -tVFS_NodeType gFIFO_PipeNodeType = { - .TypeName = "FIFO Pipe Node", - .Read = FIFO_Read, - .Write = FIFO_Write, - .Close = FIFO_Close, - .Reference = FIFO_Reference -}; -tDevFS_Driver gFIFO_DriverInfo = { - NULL, "fifo", - { - .Size = 1, - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRW, - .Flags = VFS_FFLAG_DIRECTORY, - .Type = &gFIFO_DirNodeType - } -}; -tVFS_Node gFIFO_AnonNode = { - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRW, - }; -tPipe *gFIFO_NamedPipes = NULL; - -// === CODE === -/** - * \fn int FIFO_Install(char **Options) - * \brief Installs the FIFO Driver - */ -int FIFO_Install(char **Options) -{ - DevFS_AddDevice( &gFIFO_DriverInfo ); - return MODULE_ERR_OK; -} - -/** - * \fn int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data) - */ -int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data) -{ - return 0; -} - -/** - * \fn char *FIFO_ReadDir(tVFS_Node *Node, int Id) - * \brief Reads from the FIFO root - */ -char *FIFO_ReadDir(tVFS_Node *Node, int Id) -{ - tPipe *tmp = gFIFO_NamedPipes; - - // Entry 0 is Anon Pipes - if(Id == 0) return strdup("anon"); - - // Find the id'th node - while(--Id && tmp) tmp = tmp->Next; - // If node found, return it - if(tmp) return strdup(tmp->Name); - // else error return - return NULL; -} - -/** - * \fn tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename) - * \brief Find a file in the FIFO root - * \note Creates an anon pipe if anon is requested - */ -tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename) -{ - tPipe *tmp; - if(!Filename) return NULL; - - // NULL String Check - if(Filename[0] == '\0') return NULL; - - // Anon Pipe - if(Filename[0] == 'a' && Filename[1] == 'n' - && Filename[2] == 'o' && Filename[3] == 'n' - && Filename[4] == '\0') { - tmp = FIFO_Int_NewPipe(DEFAULT_RING_SIZE, "anon"); - return &tmp->Node; - } - - // Check Named List - tmp = gFIFO_NamedPipes; - while(tmp) - { - if(strcmp(tmp->Name, Filename) == 0) - return &tmp->Node; - tmp = tmp->Next; - } - return NULL; -} - -/** - * \fn int FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags) - */ -int FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags) -{ - return 0; -} - -void FIFO_Reference(tVFS_Node *Node) -{ - if(!Node->ImplPtr) return ; - - Node->ReferenceCount ++; -} - -/** - * \fn void FIFO_Close(tVFS_Node *Node) - * \brief Close a FIFO end - */ -void FIFO_Close(tVFS_Node *Node) -{ - tPipe *pipe; - if(!Node->ImplPtr) return ; - - Node->ReferenceCount --; - if(Node->ReferenceCount) return ; - - pipe = Node->ImplPtr; - - if(strcmp(pipe->Name, "anon") == 0) { - Log_Debug("FIFO", "Pipe %p closed", Node->ImplPtr); - free(Node->ImplPtr); - return ; - } - - return ; -} - -/** - * \fn int FIFO_Relink(tVFS_Node *Node, const char *OldName, const char *NewName) - * \brief Relink a file (Deletes named pipes) - */ -int FIFO_Relink(tVFS_Node *Node, const char *OldName, const char *NewName) -{ - tPipe *pipe, *tmp; - - if(Node != &gFIFO_DriverInfo.RootNode) return 0; - - // Can't relink anon - if(strcmp(OldName, "anon")) return 0; - - // Find node - for(pipe = gFIFO_NamedPipes; - pipe; - pipe = pipe->Next) - { - if(strcmp(pipe->Name, OldName) == 0) - break; - } - if(!pipe) return 0; - - // Relink a named pipe - if(NewName) { - // Check new name - for(tmp = gFIFO_NamedPipes; - tmp; - tmp = tmp->Next) - { - if(strcmp(tmp->Name, NewName) == 0) return 0; - } - // Create new name - free(pipe->Name); - pipe->Name = malloc(strlen(NewName)+1); - strcpy(pipe->Name, NewName); - return 1; - } - - // Unlink the pipe - if(Node->ImplPtr) { - free(Node->ImplPtr); - return 1; - } - - return 0; -} - -/** - * \fn Uint64 FIFO_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Read from a fifo pipe - */ -Uint64 FIFO_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - tPipe *pipe = Node->ImplPtr; - Uint len; - Uint remaining = Length; - - if(!pipe) return 0; - - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - - while(remaining) - { - // Wait for buffer to fill - if(pipe->Flags & PF_BLOCKING) - { - if( pipe->ReadPos == pipe->WritePos ) - VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "FIFO_Read"); - - } - else - { - if(pipe->ReadPos == pipe->WritePos) - { - VFS_MarkAvaliable(Node, 0); - LEAVE('i', 0); - return 0; - } - } - - len = remaining; - if( pipe->ReadPos < pipe->WritePos ) - { - int avail_bytes = pipe->WritePos - pipe->ReadPos; - if( avail_bytes < remaining ) len = avail_bytes; - } - else - { - int avail_bytes = pipe->WritePos + pipe->BufSize - pipe->ReadPos; - if( avail_bytes < remaining ) len = avail_bytes; - } - - LOG("len = %i, remaining = %i", len, remaining); - - // Check if read overflows buffer - if(len > pipe->BufSize - pipe->ReadPos) - { - int ofs = pipe->BufSize - pipe->ReadPos; - memcpy(Buffer, &pipe->Buffer[pipe->ReadPos], ofs); - memcpy((Uint8*)Buffer + ofs, &pipe->Buffer, len-ofs); - } - else - { - memcpy(Buffer, &pipe->Buffer[pipe->ReadPos], len); - } - - // Increment read position - pipe->ReadPos += len; - pipe->ReadPos %= pipe->BufSize; - - // Mark some flags - if( pipe->ReadPos == pipe->WritePos ) { - VFS_MarkAvaliable(Node, 0); - } - VFS_MarkFull(Node, 0); // Buffer can't still be full - - // Decrement Remaining Bytes - remaining -= len; - // Increment Buffer address - Buffer = (Uint8*)Buffer + len; - - // TODO: Option to read differently - LEAVE('i', len); - return len; - } - - LEAVE('i', Length); - return Length; - -} - -/** - * \fn Uint64 FIFO_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Write to a fifo pipe - */ -Uint64 FIFO_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - tPipe *pipe = Node->ImplPtr; - Uint len; - Uint remaining = Length; - - if(!pipe) return 0; - - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - - while(remaining) - { - // Wait for buffer to empty - if(pipe->Flags & PF_BLOCKING) { - if( pipe->ReadPos == (pipe->WritePos+1)%pipe->BufSize ) - VFS_SelectNode(Node, VFS_SELECT_WRITE, NULL, "FIFO_Write"); - - len = remaining; - if( pipe->ReadPos > pipe->WritePos ) - { - int rem_space = pipe->ReadPos - pipe->WritePos; - if(rem_space < remaining) len = rem_space; - } - else - { - int rem_space = pipe->ReadPos + pipe->BufSize - pipe->WritePos; - if(rem_space < remaining) len = rem_space; - } - } - else - { - if(pipe->ReadPos == (pipe->WritePos+1)%pipe->BufSize) - { - LEAVE('i', 0); - return 0; - } - // Write buffer - if(pipe->ReadPos - pipe->WritePos < remaining) - len = pipe->ReadPos - pipe->WritePos; - else - len = remaining; - } - - // Check if write overflows buffer - if(len > pipe->BufSize - pipe->WritePos) - { - int ofs = pipe->BufSize - pipe->WritePos; - memcpy(&pipe->Buffer[pipe->WritePos], Buffer, ofs); - memcpy(&pipe->Buffer, (Uint8*)Buffer + ofs, len-ofs); - } - else - { - memcpy(&pipe->Buffer[pipe->WritePos], Buffer, len); - } - - // Increment read position - pipe->WritePos += len; - pipe->WritePos %= pipe->BufSize; - - // Mark some flags - if( pipe->ReadPos == pipe->WritePos ) { - VFS_MarkFull(Node, 1); // Buffer full - } - VFS_MarkAvaliable(Node, 1); - - // Decrement Remaining Bytes - remaining -= len; - // Increment Buffer address - Buffer = (Uint8*)Buffer + len; - } - - LEAVE('i', Length); - return Length; -} - -// --- HELPERS --- -/** - * \fn tPipe *FIFO_Int_NewPipe(int Size, const char *Name) - * \brief Create a new pipe - */ -tPipe *FIFO_Int_NewPipe(int Size, const char *Name) -{ - tPipe *ret; - int namelen = strlen(Name) + 1; - int allocsize = sizeof(tPipe) + sizeof(tVFS_ACL) + Size + namelen; - - ret = calloc(1, allocsize); - if(!ret) return NULL; - - // Clear Return - ret->Flags = PF_BLOCKING; - - // Allocate Buffer - ret->BufSize = Size; - ret->Buffer = (void*)( (Uint)ret + sizeof(tPipe) + sizeof(tVFS_ACL) ); - - // Set name (and FIFO name) - ret->Name = ret->Buffer + Size; - strcpy(ret->Name, Name); - // - Start empty, max of `Size` - //Semaphore_Init( &ret->Semaphore, 0, Size, "FIFO", ret->Name ); - - // Set Node - ret->Node.ReferenceCount = 1; - ret->Node.Size = 0; - ret->Node.ImplPtr = ret; - ret->Node.UID = Threads_GetUID(); - ret->Node.GID = Threads_GetGID(); - ret->Node.NumACLs = 1; - ret->Node.ACLs = (void*)( (Uint)ret + sizeof(tPipe) ); - ret->Node.ACLs->Group = 0; - ret->Node.ACLs->ID = ret->Node.UID; - ret->Node.ACLs->Inv = 0; - ret->Node.ACLs->Perms = -1; - ret->Node.CTime - = ret->Node.MTime - = ret->Node.ATime = now(); - ret->Node.Type = &gFIFO_PipeNodeType; - - return ret; -} diff --git a/Kernel/drv/iocache.c b/Kernel/drv/iocache.c deleted file mode 100644 index 8f735419..00000000 --- a/Kernel/drv/iocache.c +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Acess2 Kernel - * - IO Cache - * - * By thePowersGang (John Hodge) - * - * TODO: Convert to use spare physical pages instead - */ -#define DEBUG 0 -#include -#include - -// === TYPES === -typedef struct sIOCache_Ent tIOCache_Ent; -typedef struct sIOCache_PageInfo tIOCache_PageInfo; - -// === STRUCTURES === -struct sIOCache_Ent -{ - tIOCache_Ent *Next; - Uint64 Num; - Sint64 LastAccess; - Sint64 LastWrite; - Uint8 Data[]; -}; - -struct sIOCache_PageInfo -{ - tIOCache_PageInfo *GlobalNext; - tIOCache_PageInfo *CacheNext; - tIOCache *Owner; - tPAddr BasePhys; - Uint64 BaseOffset; -}; - -struct sIOCache -{ - tIOCache *Next; - int SectorSize; - tMutex Lock; - int Mode; - Uint32 ID; - tIOCache_WriteCallback Write; - int CacheSize; - int CacheUsed; - tIOCache_Ent *Entries; -}; - -// === GLOBALS === -tShortSpinlock glIOCache_Caches; -tIOCache *gIOCache_Caches = NULL; - int giIOCache_NumCaches = 0; -tIOCache_PageInfo *gIOCache_GlobalPages; - -// === CODE === -/** - * \fn tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize ) - * \brief Creates a new IO Cache - */ -tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize ) -{ - tIOCache *ret = calloc( 1, sizeof(tIOCache) ); - - // Sanity Check - if(!ret) return NULL; - - // Fill Structure - ret->SectorSize = SectorSize; - ret->Mode = IOCACHE_WRITEBACK; - ret->ID = ID; - ret->Write = Write; - ret->CacheSize = CacheSize; - - // Append to list - SHORTLOCK( &glIOCache_Caches ); - ret->Next = gIOCache_Caches; - gIOCache_Caches = ret; - SHORTREL( &glIOCache_Caches ); - - // Return - return ret; -} - -/** - * \fn int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer ) - * \brief Read from a cached sector - */ -int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer ) -{ - - ENTER("pCache XSector pBuffer", Cache, Sector, Buffer); - - // Sanity Check! - if(!Cache || !Buffer) { - LEAVE('i', -1); - return -1; - } - - // Lock - Mutex_Acquire( &Cache->Lock ); - if(Cache->CacheSize == 0) { - Mutex_Release( &Cache->Lock ); - LEAVE('i', -1); - return -1; - } - - #if IOCACHE_USE_PAGES - tIOCache_PageInfo *page; - size_t offset = (Sector*Cache->SectorSize) % PAGE_SIZE; - Uint64 wanted_base = (Sector*Cache->SectorSize) & ~(PAGE_SIZE-1); - for( page = Cache->Pages; page; page = page->CacheNext ) - { - void *tmp; - if(page->BaseOffset < WantedBase) continue; - if(page->BaseOffset > WantedBase) break; - tmp = MM_MapTemp( page->BasePhys ); - memcpy( Buffer, tmp + offset, Cache->SectorSize ); - MM_FreeTemp( tmp ); - } - #else - tIOCache_Ent *ent; - // Search the list - for( ent = Cache->Entries; ent; ent = ent->Next ) - { - // Have we found what we are looking for? - if( ent->Num == Sector ) { - memcpy(Buffer, ent->Data, Cache->SectorSize); - ent->LastAccess = now(); - Mutex_Release( &Cache->Lock ); - LEAVE('i', 1); - return 1; - } - // It's a sorted list, so as soon as we go past `Sector` we know - // it's not there - if(ent->Num > Sector) break; - } - #endif - - Mutex_Release( &Cache->Lock ); - LEAVE('i', 0); - return 0; -} - -/** - * \fn int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer ) - * \brief Cache a sector - */ -int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer ) -{ - tIOCache_Ent *ent, *prev; - tIOCache_Ent *new; - tIOCache_Ent *oldest = NULL, *oldestPrev; - - // Sanity Check! - if(!Cache || !Buffer) - return -1; - - // Lock - Mutex_Acquire( &Cache->Lock ); - if(Cache->CacheSize == 0) { - Mutex_Release( &Cache->Lock ); - return -1; - } - - // Search the list - prev = (tIOCache_Ent*)&Cache->Entries; - for( ent = Cache->Entries; ent; prev = ent, ent = ent->Next ) - { - // Is it already here? - if( ent->Num == Sector ) { - Mutex_Release( &Cache->Lock ); - return 0; - } - - // Check if we have found the oldest entry - if( !oldest || oldest->LastAccess > ent->LastAccess ) { - oldest = ent; - oldestPrev = prev; - } - - // Here we go! - if(ent->Num > Sector) - break; - } - - // Create the new entry - new = malloc( sizeof(tIOCache_Ent) + Cache->SectorSize ); - new->Next = ent; - new->Num = Sector; - new->LastAccess = now(); - new->LastWrite = 0; // Zero is special, it means unmodified - memcpy(new->Data, Buffer, Cache->SectorSize); - - // Have we reached the maximum cached entries? - if( Cache->CacheUsed == Cache->CacheSize ) - { - tIOCache_Ent *savedPrev = prev; - oldestPrev = (tIOCache_Ent*)&Cache->Entries; - // If so, search for the least recently accessed entry - for( ; ent; prev = ent, ent = ent->Next ) - { - // Check if we have found the oldest entry - if( !oldest || oldest->LastAccess > ent->LastAccess ) { - oldest = ent; - oldestPrev = prev; - } - } - if( !oldest ) { - Log_Error("IOCache", "Cache full, but also empty"); - return -1; - } - // Remove from list, write back and free - oldestPrev->Next = oldest->Next; - if(oldest->LastWrite && Cache->Mode != IOCACHE_VIRTUAL) - Cache->Write(Cache->ID, oldest->Num, oldest->Data); - free(oldest); - - // Decrement the used count - Cache->CacheUsed --; - - // Restore `prev` - prev = savedPrev; - } - - // Append to list - prev->Next = new; - Cache->CacheUsed ++; - - // Release Spinlock - Mutex_Release( &Cache->Lock ); - - // Return success - return 1; -} - -/** - * \fn int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer ) - * \brief Read from a cached sector - */ -int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer ) -{ - tIOCache_Ent *ent; - - // Sanity Check! - if(!Cache || !Buffer) - return -1; - // Lock - Mutex_Acquire( &Cache->Lock ); - if(Cache->CacheSize == 0) { - Mutex_Release( &Cache->Lock ); - return -1; - } - - // Search the list - for( ent = Cache->Entries; ent; ent = ent->Next ) - { - // Have we found what we are looking for? - if( ent->Num == Sector ) { - memcpy(ent->Data, Buffer, Cache->SectorSize); - ent->LastAccess = ent->LastWrite = now(); - - if(Cache->Mode == IOCACHE_WRITEBACK) { - Cache->Write(Cache->ID, Sector, Buffer); - ent->LastWrite = 0; - } - - Mutex_Release( &Cache->Lock ); - return 1; - } - // It's a sorted list, so as soon as we go past `Sector` we know - // it's not there - if(ent->Num > Sector) break; - } - - Mutex_Release( &Cache->Lock ); - return 0; -} - -/** - * \fn void IOCache_Flush( tIOCache *Cache ) - * \brief Flush a cache - */ -void IOCache_Flush( tIOCache *Cache ) -{ - tIOCache_Ent *ent; - - if( Cache->Mode == IOCACHE_VIRTUAL ) return; - - // Lock - Mutex_Acquire( &Cache->Lock ); - if(Cache->CacheSize == 0) { - Mutex_Release( &Cache->Lock ); - return; - } - - // Write All - for( ent = Cache->Entries; ent; ent = ent->Next ) - { - Cache->Write(Cache->ID, ent->Num, ent->Data); - ent->LastWrite = 0; - } - - Mutex_Release( &Cache->Lock ); -} - -/** - * \fn void IOCache_Destroy( tIOCache *Cache ) - * \brief Destroy a cache - */ -void IOCache_Destroy( tIOCache *Cache ) -{ - tIOCache_Ent *ent, *prev = NULL; - - // Lock - Mutex_Acquire( &Cache->Lock ); - if(Cache->CacheSize == 0) { - Mutex_Release( &Cache->Lock ); - return; - } - - // Free All - for(ent = Cache->Entries; - ent; - prev = ent, ent = ent->Next, free(prev) ) - { - if( Cache->Mode != IOCACHE_VIRTUAL ) - { - Cache->Write(Cache->ID, ent->Num, ent->Data); - ent->LastWrite = 0; - } - } - - Cache->CacheSize = 0; - - Mutex_Release( &Cache->Lock ); - - // Remove from list - SHORTLOCK( &glIOCache_Caches ); - { - tIOCache *cache; - tIOCache *prev_cache = (tIOCache*)&gIOCache_Caches; - for(cache = gIOCache_Caches; - cache; - prev_cache = cache, cache = cache->Next ) - { - if(cache == Cache) { - prev_cache->Next = cache->Next; - break; - } - } - } - SHORTREL( &glIOCache_Caches ); - - free(Cache); -} diff --git a/Kernel/drv/pci.c b/Kernel/drv/pci.c deleted file mode 100644 index 8745080b..00000000 --- a/Kernel/drv/pci.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * AcessOS/AcessBasic v0.1 - * PCI Bus Driver - */ -#define DEBUG 0 -#include -#include -#include -#include -#include -#include - -#define LIST_DEVICES 1 - -// === STRUCTURES === -typedef struct sPCIDevice -{ - Uint16 bus, slot, fcn; - Uint16 vendor, device; - Uint32 class; // Class:Subclass:ProgIf - Uint8 revision; - Uint32 ConfigCache[256/4]; - char Name[8]; - tVFS_Node Node; -} tPCIDevice; - -// === CONSTANTS === -#define SPACE_STEP 5 -#define MAX_RESERVED_PORT 0xD00 - -// === PROTOTYPES === - int PCI_Install(char **Arguments); - int PCI_ScanBus(int ID, int bFill); - -char *PCI_int_ReadDirRoot(tVFS_Node *node, int pos); -tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename); -Uint32 PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset); -Uint64 PCI_int_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer); - int PCI_int_EnumDevice(Uint16 bus, Uint16 dev, Uint16 fcn, tPCIDevice *info); - -// === GLOBALS === -MODULE_DEFINE(0, 0x0100, PCI, PCI_Install, NULL, NULL); - int giPCI_BusCount = 1; - int giPCI_InodeHandle = -1; - int giPCI_DeviceCount = 0; -tPCIDevice *gPCI_Devices = NULL; -tVFS_NodeType gPCI_RootNodeType = { - .TypeName = "PCI Root Node", - .ReadDir = PCI_int_ReadDirRoot, - .FindDir = PCI_int_FindDirRoot -}; -tVFS_NodeType gPCI_DevNodeType = { - .TypeName = "PCI Dev Node", - .Read = PCI_int_ReadDevice -}; -tDevFS_Driver gPCI_DriverStruct = { - NULL, "pci", - { - .Flags = VFS_FFLAG_DIRECTORY, - .Size = -1, - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Type = &gPCI_RootNodeType - } -}; -Uint32 *gaPCI_PortBitmap = NULL; -Uint32 gaPCI_BusBitmap[256/32]; - -// === CODE === -/** - * \brief Scan the PCI Bus for devices - * \param Arguments Boot-time parameters - */ -int PCI_Install(char **Arguments) -{ - int i; - void *tmpPtr; - - // Build Portmap - gaPCI_PortBitmap = malloc( 1 << 13 ); - if( !gaPCI_PortBitmap ) { - Log_Error("PCI", "Unable to allocate %i bytes for bitmap", 1 << 13); - return MODULE_ERR_MALLOC; - } - memset( gaPCI_PortBitmap, 0, 1 << 13 ); - for( i = 0; i < MAX_RESERVED_PORT / 32; i ++ ) - gaPCI_PortBitmap[i] = -1; - for( i = 0; i < MAX_RESERVED_PORT % 32; i ++ ) - gaPCI_PortBitmap[MAX_RESERVED_PORT / 32] = 1 << i; - - // Scan Bus (Bus 0, Don't fill gPCI_Devices) - i = PCI_ScanBus(0, 0); - if(i != MODULE_ERR_OK) return i; - - if(giPCI_DeviceCount == 0) { - Log_Notice("PCI", "No devices were found"); - return MODULE_ERR_NOTNEEDED; - } - - // Allocate device buffer - tmpPtr = malloc(giPCI_DeviceCount * sizeof(tPCIDevice)); - if(tmpPtr == NULL) { - Log_Warning("PCI", "Malloc ERROR"); - return MODULE_ERR_MALLOC; - } - gPCI_Devices = tmpPtr; - - Log_Log("PCI", "%i devices, filling structure", giPCI_DeviceCount); - - // Reset counts - giPCI_DeviceCount = 0; - giPCI_BusCount = 0; - memset(gaPCI_BusBitmap, 0, sizeof(gaPCI_BusBitmap)); - // Rescan, filling the PCI device array - PCI_ScanBus(0, 1); - - // Complete Driver Structure - gPCI_DriverStruct.RootNode.Size = giPCI_DeviceCount; - - // And add to DevFS - DevFS_AddDevice(&gPCI_DriverStruct); - - return MODULE_ERR_OK; -} - -/** - * \brief Scans a specific PCI Bus - * \param BusID PCI Bus ID to scan - * \param bFill Fill the \a gPCI_Devices array? - */ -int PCI_ScanBus(int BusID, int bFill) -{ - int dev, fcn; - tPCIDevice devInfo; - - if( gaPCI_BusBitmap[BusID/32] & (1 << (BusID%32)) ) - return MODULE_ERR_OK; - - gaPCI_BusBitmap[BusID/32] |= (1 << (BusID%32)); - - for( dev = 0; dev < 32; dev++ ) // 32 Devices per bus - { - for( fcn = 0; fcn < 8; fcn++ ) // Max 8 functions per device - { - // Check if the device/function exists - if(!PCI_int_EnumDevice(BusID, dev, fcn, &devInfo)) - continue; - - if(devInfo.class == PCI_OC_PCIBRIDGE) - { - #if LIST_DEVICES - if( !bFill ) - Log_Log("PCI", "Bridge @ %i,%i:%i (0x%x:0x%x)", - BusID, dev, fcn, devInfo.vendor, devInfo.device); - #endif - //TODO: Handle PCI-PCI Bridges - //PCI_ScanBus(devInfo.???, bFill); - giPCI_BusCount ++; - } - else - { - #if LIST_DEVICES - if( !bFill ) - Log_Log("PCI", "Device %i,%i:%i %06x => 0x%04x:0x%04x", - BusID, dev, fcn, devInfo.class, devInfo.vendor, devInfo.device); - #endif - } - - if( bFill ) { - devInfo.Node.Inode = giPCI_DeviceCount; - memcpy(&gPCI_Devices[giPCI_DeviceCount], &devInfo, sizeof(tPCIDevice)); - } - giPCI_DeviceCount ++; - - // If bit 23 of (soemthing) is set, there are sub-functions - if(fcn == 0 && !(devInfo.ConfigCache[3] & 0x00800000) ) - break; - } - } - - return MODULE_ERR_OK; -} - -/** - * \brief Read from Root of PCI Driver -*/ -char *PCI_int_ReadDirRoot(tVFS_Node *Node, int Pos) -{ - ENTER("pNode iPos", Node, Pos); - if(Pos < 0 || Pos >= giPCI_DeviceCount) { - LEAVE('n'); - return NULL; - } - - LEAVE('s', gPCI_Devices[Pos].Name); - return strdup( gPCI_Devices[Pos].Name ); -} -/** - */ -tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename) -{ - int bus,slot,fcn; - int i; - // Validate Filename (Pointer and length) - if(!filename || strlen(filename) != 7) - return NULL; - // Check for spacers - if(filename[2] != '.' || filename[5] != ':') - return NULL; - - // Get Information - if(filename[0] < '0' || filename[0] > '9') return NULL; - bus = (filename[0] - '0')*10; - if(filename[1] < '0' || filename[1] > '9') return NULL; - bus += filename[1] - '0'; - if(filename[3] < '0' || filename[3] > '9') return NULL; - slot = (filename[3] - '0')*10; - if(filename[4] < '0' || filename[4] > '9') return NULL; - slot += filename[4] - '0'; - if(filename[6] < '0' || filename[6] > '9') return NULL; - fcn = filename[6] - '0'; - - // Find Match - for(i=0;i 256 ) return 0; - - memcpy( - buffer, - (char*)gPCI_Devices[node->Inode].ConfigCache + pos, - length); - - return length; -} - -// --- Kernel Code Interface --- -/** - * \brief Counts the devices with the specified codes - * \param vendor Vendor ID - * \param device Device ID - */ -int PCI_CountDevices(Uint16 vendor, Uint16 device) -{ - int i, ret=0; - for(i=0;i= giPCI_DeviceCount) return 1; - - if(Vendor) *Vendor = dev->vendor; - if(Device) *Device = dev->device; - if(Class) *Class = dev->class; - return 0; -} - -int PCI_GetDeviceVersion(tPCIDev ID, Uint8 *Revision) -{ - tPCIDevice *dev = &gPCI_Devices[ID]; - if(ID < 0 || ID >= giPCI_DeviceCount) return 1; - - if(Revision) *Revision = dev->revision; - return 0; -} - -int PCI_GetDeviceSubsys(tPCIDev ID, Uint16 *SubsystemVendor, Uint16 *SubsystemID) -{ - tPCIDevice *dev = &gPCI_Devices[ID]; - if(ID < 0 || ID >= giPCI_DeviceCount) return 1; - - if(SubsystemVendor) *SubsystemVendor = dev->ConfigCache[0x2c/4] & 0xFFFF; - if(SubsystemID) *SubsystemID = dev->ConfigCache[0x2c/4] >> 16; - - return 0; -} - -Uint32 PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset) -{ - Bus &= 0xFF; - Slot &= 0x1F; - Fcn &= 7; - Offset &= 0xFC; - return ((Uint32)Bus << 16) | (Slot << 11) | (Fcn << 8) | (Offset & 0xFC); -} - -Uint32 PCI_ConfigRead(tPCIDev ID, int Offset, int Size) -{ - tPCIDevice *dev; - Uint32 dword, addr; - - if( ID < 0 || ID >= giPCI_DeviceCount ) return 0; - if( Offset < 0 || Offset > 256 ) return 0; - - // TODO: Should I support non-aligned reads? - if( Offset & (Size - 1) ) return 0; - - dev = &gPCI_Devices[ID]; - addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset); - - dword = PCI_CfgReadDWord(addr); - gPCI_Devices[ID].ConfigCache[Offset/4] = dword; - switch( Size ) - { - case 1: return (dword >> (8 * (Offset&3))) & 0xFF; - case 2: return (dword >> (8 * (Offset&2))) & 0xFFFF; - case 4: return dword; - default: - return 0; - } -} - -void PCI_ConfigWrite(tPCIDev ID, int Offset, int Size, Uint32 Value) -{ - tPCIDevice *dev; - Uint32 dword, addr; - int shift; - if( ID < 0 || ID >= giPCI_DeviceCount ) return ; - if( Offset < 0 || Offset > 256 ) return ; - - dev = &gPCI_Devices[ID]; - addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset); - - if(Size != 4) - dword = PCI_CfgReadDWord(addr); - switch(Size) - { - case 1: - shift = (Offset&3)*8; - dword &= ~(0xFF << shift); - dword |= Value << shift; - break; - case 2: - shift = (Offset&2)*8; - dword &= ~(0xFFFF << shift); - dword |= Value << shift; - break; - case 4: - dword = Value; - break; - default: - return; - } - PCI_CfgWriteDWord(addr, dword); -} - -/** - * \brief Get the IRQ assigned to a device - */ -Uint8 PCI_GetIRQ(tPCIDev id) -{ - if(id < 0 || id >= giPCI_DeviceCount) - return 0; - return gPCI_Devices[id].ConfigCache[15] & 0xFF; - //return PCI_CfgReadByte( gPCI_Devices[id].bus, gPCI_Devices[id].slot, gPCI_Devices[id].fcn, 0x3C); -} - -/** - * \brief Read the a BAR (base address register) from the PCI config space - */ -Uint32 PCI_GetBAR(tPCIDev id, int BARNum) -{ - if(id < 0 || id >= giPCI_DeviceCount) - return 0; - if(BARNum < 0 || BARNum >= 6) - return 0; - return gPCI_Devices[id].ConfigCache[4+BARNum]; -} - -/** - * \brief Get device information for a slot/function - */ -int PCI_int_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, tPCIDevice *info) -{ - Uint32 vendor_dev, tmp; - int i; - Uint32 addr; - addr = PCI_int_GetBusAddr(bus, slot, fcn, 0); - - vendor_dev = PCI_CfgReadDWord( addr ); - if((vendor_dev & 0xFFFF) == 0xFFFF) // Invalid Device - return 0; - - info->ConfigCache[0] = vendor_dev; - for( i = 1, addr += 4; i < 256/4; i ++, addr += 4 ) - { - info->ConfigCache[i] = PCI_CfgReadDWord(addr); - } - - info->bus = bus; - info->slot = slot; - info->fcn = fcn; - info->vendor = vendor_dev & 0xFFFF; - info->device = vendor_dev >> 16; - tmp = info->ConfigCache[2]; - info->revision = tmp & 0xFF; - info->class = tmp >> 8; - -// #if LIST_DEVICES -// Log("BAR0 0x%08x BAR1 0x%08x BAR2 0x%08x", info->ConfigCache[4], info->ConfigCache[5], info->ConfigCache[6]); -// Log("BAR3 0x%08x BAR4 0x%08x BAR5 0x%08x", info->ConfigCache[7], info->ConfigCache[8], info->ConfigCache[9]); -// Log("Class: 0x%06x", info->class); -// #endif - - // Make node name - info->Name[0] = '0' + bus/10; - info->Name[1] = '0' + bus%10; - info->Name[2] = '.'; - info->Name[3] = '0' + slot/10; - info->Name[4] = '0' + slot%10; - info->Name[5] = ':'; - info->Name[6] = '0' + fcn; - info->Name[7] = '\0'; - - // Create VFS Node - memset( &info->Node, 0, sizeof(tVFS_Node) ); - info->Node.Size = 256; - - info->Node.NumACLs = 1; - info->Node.ACLs = &gVFS_ACL_EveryoneRO; - - info->Node.Type = &gPCI_RootNodeType; - - return 1; -} - -// === EXPORTS === -//* -EXPORT(PCI_CountDevices); -EXPORT(PCI_GetDevice); -EXPORT(PCI_GetDeviceByClass); -EXPORT(PCI_GetDeviceInfo); -EXPORT(PCI_GetDeviceVersion); -EXPORT(PCI_GetDeviceSubsys); -//EXPORT(PCI_AssignPort); -EXPORT(PCI_GetBAR); -EXPORT(PCI_GetIRQ); -//*/ diff --git a/Kernel/drv/proc.c b/Kernel/drv/proc.c deleted file mode 100644 index 73f45354..00000000 --- a/Kernel/drv/proc.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Acess2 - * - Kernel Status Driver - */ -#define DEBUG 1 -#include -#include -#include -#include - -// === CONSTANTS === -#define VERSION ((0 << 8) | (1)) // 0.01 - -// === TYPES === -typedef struct sSysFS_Ent -{ - struct sSysFS_Ent *Next; - struct sSysFS_Ent *ListNext; - struct sSysFS_Ent *Parent; - tVFS_Node Node; - char Name[]; -} tSysFS_Ent; - -// === PROTOTYPES === - int SysFS_Install(char **Arguments); - int SysFS_IOCtl(tVFS_Node *Node, int Id, void *Data); - -#if 0 - int SysFS_RegisterFile(const char *Path, const char *Data, int Length); - int SysFS_UpdateFile(int ID, const char *Data, int Length); - int SysFS_RemoveFile(int ID); -#endif - -char *SysFS_Comm_ReadDir(tVFS_Node *Node, int Id); -tVFS_Node *SysFS_Comm_FindDir(tVFS_Node *Node, const char *Filename); -Uint64 SysFS_Comm_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -void SysFS_Comm_CloseFile(tVFS_Node *Node); - -// === GLOBALS === -extern tSysFS_Ent gSysFS_Version; // Defined Later -extern tSysFS_Ent gSysFS_Root; // Defined Later -MODULE_DEFINE(0, VERSION, SysFS, SysFS_Install, NULL, NULL); -tVFS_NodeType gSysFS_FileNodeType = { - .TypeName = "SysFS File", - .Read = SysFS_Comm_ReadFile - }; -tVFS_NodeType gSysFS_DirNodeType = { - .TypeName = "SysFS Dir", - .ReadDir = SysFS_Comm_ReadDir, - .FindDir = SysFS_Comm_FindDir - }; -tSysFS_Ent gSysFS_Version_Kernel = { - NULL, NULL, // Nexts - &gSysFS_Version, // Parent - { - .Inode = 1, // File #1 - .ImplPtr = NULL, - .ImplInt = (Uint)&gSysFS_Version_Kernel, // Self-Link - .Size = 0, - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRO, - .Type = &gSysFS_FileNodeType - }, - "Kernel" -}; -tSysFS_Ent gSysFS_Version = { - NULL, NULL, - &gSysFS_Root, - { - .Size = 1, - .ImplPtr = &gSysFS_Version_Kernel, - .ImplInt = (Uint)&gSysFS_Version, // Self-Link - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Flags = VFS_FFLAG_DIRECTORY, - .Type = &gSysFS_DirNodeType - }, - "Version" -}; -// Root of the SysFS tree (just used to keep the code clean) -tSysFS_Ent gSysFS_Root = { - NULL, NULL, - NULL, - { - .Size = 1, - .ImplPtr = &gSysFS_Version, - .ImplInt = (Uint)&gSysFS_Root // Self-Link - }, - "/" -}; -tDevFS_Driver gSysFS_DriverInfo = { - NULL, "system", - { - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Flags = VFS_FFLAG_DIRECTORY, - .ImplPtr = &gSysFS_Version, - .Size = sizeof(gSysFS_Root)/sizeof(tSysFS_Ent), - .Type = &gSysFS_DirNodeType - } -}; - int giSysFS_NextFileID = 2; -tSysFS_Ent *gSysFS_FileList; - -// === CODE === -/** - * \fn int SysFS_Install(char **Options) - * \brief Installs the SysFS Driver - */ -int SysFS_Install(char **Options) -{ - { - const char *fmt = "Acess2 "EXPAND_STR(KERNEL_VERSION)" "EXPAND_STR(ARCHDIR)" build %i, hash %s"; - gSysFS_Version_Kernel.Node.Size = sprintf(NULL, fmt, BUILD_NUM, gsGitHash); - gSysFS_Version_Kernel.Node.ImplPtr = malloc( gSysFS_Version_Kernel.Node.Size + 1 ); - sprintf(gSysFS_Version_Kernel.Node.ImplPtr, fmt, BUILD_NUM, gsGitHash); - } - - DevFS_AddDevice( &gSysFS_DriverInfo ); - return MODULE_ERR_OK; -} - -/** - * \fn int SysFS_RegisterFile(char *Path, char *Data, int Length) - * \brief Registers a file (buffer) for the user to be able to read from - * \param Path Path for the file to be accessable from (relative to SysFS root) - * \param Data Pointer to the data buffer (must be non-volatile) - * \param Length Length of the data buffer - * \return The file's identifier - */ -int SysFS_RegisterFile(const char *Path, const char *Data, int Length) -{ - int start = 0; - int tmp; - tSysFS_Ent *ent = NULL; - tSysFS_Ent *child, *prev; - - // Find parent directory - while( (tmp = strpos(&Path[start], '/')) != -1 ) - { - prev = NULL; - - if(ent) - child = ent->Node.ImplPtr; - else - child = gSysFS_DriverInfo.RootNode.ImplPtr; - for( ; child; prev = child, child = child->Next ) - { - if( strncmp( &Path[start], child->Name, tmp+1 ) == '/' ) - break; - } - - // Need a new directory? - if( !child ) - { - child = calloc( 1, sizeof(tSysFS_Ent)+tmp+1 ); - child->Next = NULL; - memcpy(child->Name, &Path[start], tmp); - child->Name[tmp] = '\0'; - child->Parent = ent; - child->Node.Inode = 0; - child->Node.ImplPtr = NULL; - child->Node.ImplInt = (Uint)child; // Uplink - child->Node.NumACLs = 1; - child->Node.ACLs = &gVFS_ACL_EveryoneRX; - child->Node.Flags = VFS_FFLAG_DIRECTORY; - child->Node.Type = &gSysFS_DirNodeType; - if( !prev ) { - if(ent) - ent->Node.ImplPtr = child; - else - gSysFS_DriverInfo.RootNode.ImplPtr = child; - // ^^^ Impossible (There is already /Version) - } - else - prev->Next = child; - if(ent) - ent->Node.Size ++; - else - gSysFS_DriverInfo.RootNode.Size ++; - Log_Log("SysFS", "Added directory '%s'", child->Name); - } - - ent = child; - - start = tmp+1; - } - - // ent: Parent tSysFS_Ent or NULL - // start: beginning of last path element - - // Check if the name is taken - prev = NULL; - if(ent) - child = ent->Node.ImplPtr; - else - child = gSysFS_DriverInfo.RootNode.ImplPtr; - for( ; child; child = child->Next ) - { - if( strcmp( &Path[start], child->Name ) == 0 ) - break; - } - if( child ) { - Log_Warning("SysFS", "'%s' is taken (in '%s')\n", &Path[start], Path); - return 0; - } - - // Create new node - child = calloc( 1, sizeof(tSysFS_Ent)+strlen(&Path[start])+1 ); - child->Next = NULL; - strcpy(child->Name, &Path[start]); - child->Parent = ent; - - child->Node.Inode = giSysFS_NextFileID++; - child->Node.ImplPtr = (void*)Data; - child->Node.ImplInt = (Uint)child; // Uplink - child->Node.Size = Length; - child->Node.NumACLs = 1; - child->Node.ACLs = &gVFS_ACL_EveryoneRO; - child->Node.Type = &gSysFS_FileNodeType; - - // Add to parent's child list - if(ent) { - ent->Node.Size ++; - child->Next = ent->Node.ImplPtr; - ent->Node.ImplPtr = child; - } - else { - gSysFS_DriverInfo.RootNode.Size ++; - child->Next = gSysFS_DriverInfo.RootNode.ImplPtr; - gSysFS_DriverInfo.RootNode.ImplPtr = child; - } - // Add to global file list - child->ListNext = gSysFS_FileList; - gSysFS_FileList = child; - - Log_Log("SysFS", "Added '%s' (%p)", Path, Data); - - return child->Node.Inode; -} - -/** - * \fn int SysFS_UpdateFile(int ID, char *Data, int Length) - * \brief Updates a file - * \param ID Identifier returned by ::SysFS_RegisterFile - * \param Data Pointer to the data buffer - * \param Length Length of the data buffer - * \return Boolean Success - */ -int SysFS_UpdateFile(int ID, const char *Data, int Length) -{ - tSysFS_Ent *ent; - - for( ent = gSysFS_FileList; ent; ent = ent->Next ) - { - // It's a reverse sorted list - if(ent->Node.Inode < ID) return 0; - if(ent->Node.Inode == ID) - { - ent->Node.ImplPtr = (void*)Data; - ent->Node.Size = Length; - return 1; - } - } - - return 0; -} - -/** - * \fn int SysFS_RemoveFile(int ID) - * \brief Removes a file from user access - * \param ID Identifier returned by ::SysFS_RegisterFile - * \return Boolean Success - * \note If a handle is still open to the file, it will be invalidated - */ -int SysFS_RemoveFile(int ID) -{ - tSysFS_Ent *file; - tSysFS_Ent *ent, *parent, *prev; - - prev = NULL; - for( ent = gSysFS_FileList; ent; prev = ent, ent = ent->Next ) - { - // It's a reverse sorted list - if(ent->Node.Inode < ID) return 0; - if(ent->Node.Inode == ID) break; - } - - if(!ent) return 0; - - // Set up for next part - file = ent; - parent = file->Parent; - - // Remove from file list - if(prev) - prev->ListNext = file->ListNext; - else - gSysFS_FileList = file->ListNext; - file->Node.Size = 0; - file->Node.ImplPtr = NULL; - - // Search parent directory - for( ent = parent->Node.ImplPtr; ent; prev = ent, ent = ent->Next ) - { - if( ent == file ) break; - } - if(!ent) { - Log_Warning("SysFS", "Bookkeeping Error: File in list, but not in directory"); - return 0; - } - - // Remove from parent directory - if(prev) - prev->Next = ent->Next; - else - parent->Node.ImplPtr = ent->Next; - - // Free if not in use - if(file->Node.ReferenceCount == 0) - free(file); - - return 1; -} - -/** - * \fn char *SysFS_Comm_ReadDir(tVFS_Node *Node, int Pos) - * \brief Reads from a SysFS directory - */ -char *SysFS_Comm_ReadDir(tVFS_Node *Node, int Pos) -{ - tSysFS_Ent *child = (tSysFS_Ent*)Node->ImplPtr; - if(Pos < 0 || Pos >= Node->Size) return NULL; - - for( ; child; child = child->Next, Pos-- ) - { - if( Pos == 0 ) return strdup(child->Name); - } - return NULL; -} - -/** - * \fn tVFS_Node *SysFS_Comm_FindDir(tVFS_Node *Node, const char *Filename) - * \brief Find a file in a SysFS directory - */ -tVFS_Node *SysFS_Comm_FindDir(tVFS_Node *Node, const char *Filename) -{ - tSysFS_Ent *child = (tSysFS_Ent*)Node->ImplPtr; - - for( ; child; child = child->Next ) - { - if( strcmp(child->Name, Filename) == 0 ) - return &child->Node; - } - - return NULL; -} - -/** - * \fn Uint64 SysFS_Comm_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Read from an exposed buffer - */ -Uint64 SysFS_Comm_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - if( Offset > Node->Size ) return -1; - if( Length > Node->Size ) Length = Node->Size; - if( Offset + Length > Node->Size) Length = Node->Size - Offset; - - if( Node->ImplPtr ) - memcpy(Buffer, (void*)((Uint)Node->ImplPtr+(Uint)Offset), Length); - else - return -1; - - return Length; -} - -/** - * \fn void SysFS_Comm_CloseFile(tVFS_Node *Node) - * \brief Closes an open file - * \param Node Node to close - */ -void SysFS_Comm_CloseFile(tVFS_Node *Node) -{ - // Dereference - Node->ReferenceCount --; - if( Node->ReferenceCount > 0 ) return; - - // Check if it is still valid - if( Node->ImplPtr ) return; - - // Delete - free( (void*)Node->ImplInt ); -} diff --git a/Kernel/drv/vterm.c b/Kernel/drv/vterm.c deleted file mode 100644 index 2ae38ee8..00000000 --- a/Kernel/drv/vterm.c +++ /dev/null @@ -1,764 +0,0 @@ -/* - * Acess2 Virtual Terminal Driver - */ -#define DEBUG 0 -#include "vterm.h" -#include -#include -#include -#include -#include -#include - -// === CONSTANTS === -#define VERSION ((0<<8)|(50)) - -#define NUM_VTS 8 -//#define DEFAULT_OUTPUT "BochsGA" -#define DEFAULT_OUTPUT "Vesa" -#define FALLBACK_OUTPUT "x86_VGAText" -#define DEFAULT_INPUT "PS2Keyboard" -#define DEFAULT_WIDTH 640 -#define DEFAULT_HEIGHT 480 -#define DEFAULT_SCROLLBACK 2 // 2 Screens of text + current screen -//#define DEFAULT_SCROLLBACK 0 - -// === TYPES === - -// === IMPORTS === -extern void Debug_SetKTerminal(const char *File); - -// === PROTOTYPES === - int VT_Install(char **Arguments); -char *VT_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name); - int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data); -Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); - int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data); - -// === CONSTANTS === - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, VTerm, VT_Install, NULL, DEFAULT_INPUT, NULL); -tVFS_NodeType gVT_RootNodeType = { - .TypeName = "VTerm Root", - .ReadDir = VT_ReadDir, - .FindDir = VT_FindDir, - .IOCtl = VT_Root_IOCtl - }; -tVFS_NodeType gVT_TermNodeType = { - .TypeName = "VTerm", - .Read = VT_Read, - .Write = VT_Write, - .IOCtl = VT_Terminal_IOCtl - }; -tDevFS_Driver gVT_DrvInfo = { - NULL, "VTerm", - { - .Flags = VFS_FFLAG_DIRECTORY, - .Size = NUM_VTS, - .Inode = -1, - .NumACLs = 0, - .Type = &gVT_RootNodeType - } -}; -// --- Terminals --- -tVTerm gVT_Terminals[NUM_VTS]; - int giVT_CurrentTerminal = 0; -tVTerm *gpVT_CurTerm = &gVT_Terminals[0]; -// --- Video State --- -short giVT_RealWidth = DEFAULT_WIDTH; //!< Screen Width -short giVT_RealHeight = DEFAULT_HEIGHT; //!< Screen Height - int giVT_Scrollback = DEFAULT_SCROLLBACK; -// --- Driver Handles --- -char *gsVT_OutputDevice = NULL; -char *gsVT_InputDevice = NULL; - int giVT_OutputDevHandle = -2; - int giVT_InputDevHandle = -2; - -// === CODE === -/** - * \fn int VT_Install(char **Arguments) - * \brief Installs the Virtual Terminal Driver - */ -int VT_Install(char **Arguments) -{ - int i; - - // Scan Arguments - if(Arguments) - { - char **args; - const char *arg; - for(args = Arguments; (arg = *args); args++ ) - { - char data[strlen(arg)+1]; - char *opt = data; - char *val; - - val = strchr(arg, '='); - strcpy(data, arg); - if( val ) { - data[ val - arg ] = '\0'; - val ++; - } - Log_Debug("VTerm", "Argument '%s'", arg); - - if( strcmp(opt, "Video") == 0 ) { - if( !gsVT_OutputDevice ) - gsVT_OutputDevice = strdup(val); - } - else if( strcmp(opt, "Input") == 0 ) { - if( !gsVT_InputDevice ) - gsVT_InputDevice = strdup(val); - } - else if( strcmp(opt, "Width") == 0 ) { - giVT_RealWidth = atoi( val ); - } - else if( strcmp(opt, "Height") == 0 ) { - giVT_RealHeight = atoi( val ); - } - else if( strcmp(opt, "Scrollback") == 0 ) { - giVT_Scrollback = atoi( val ); - } - } - } - - // Apply Defaults - if(!gsVT_OutputDevice) gsVT_OutputDevice = (char*)DEFAULT_OUTPUT; - else if( Module_EnsureLoaded( gsVT_OutputDevice ) ) gsVT_OutputDevice = (char*)DEFAULT_OUTPUT; - if( Module_EnsureLoaded( gsVT_OutputDevice ) ) gsVT_OutputDevice = (char*)FALLBACK_OUTPUT; - if( Module_EnsureLoaded( gsVT_OutputDevice ) ) { - Log_Error("VTerm", "Fallback video '%s' is not avaliable, giving up", FALLBACK_OUTPUT); - return MODULE_ERR_MISC; - } - - if(!gsVT_InputDevice) gsVT_InputDevice = (char*)DEFAULT_INPUT; - else if( Module_EnsureLoaded( gsVT_InputDevice ) ) gsVT_InputDevice = (char*)DEFAULT_INPUT; - - // Create device paths - { - char *tmp; - tmp = malloc( 9 + strlen(gsVT_OutputDevice) + 1 ); - strcpy(tmp, "/Devices/"); - strcpy(&tmp[9], gsVT_OutputDevice); - gsVT_OutputDevice = tmp; - - tmp = malloc( 9 + strlen(gsVT_InputDevice) + 1 ); - strcpy(tmp, "/Devices/"); - strcpy(&tmp[9], gsVT_InputDevice); - gsVT_InputDevice = tmp; - } - - Log_Log("VTerm", "Using '%s' as output", gsVT_OutputDevice); - Log_Log("VTerm", "Using '%s' as input", gsVT_InputDevice); - - VT_InitOutput(); - VT_InitInput(); - - // Create Nodes - for( i = 0; i < NUM_VTS; i++ ) - { - gVT_Terminals[i].Mode = TERM_MODE_TEXT; - gVT_Terminals[i].Flags = 0; -// gVT_Terminals[i].Flags = VT_FLAG_HIDECSR; //HACK - Stop all those memcpy calls - gVT_Terminals[i].CurColour = DEFAULT_COLOUR; - gVT_Terminals[i].WritePos = 0; - gVT_Terminals[i].AltWritePos = 0; - gVT_Terminals[i].ViewPos = 0; - gVT_Terminals[i].ReadingThread = -1; - gVT_Terminals[i].ScrollHeight = 0; - - // Initialise - VT_int_ChangeMode( &gVT_Terminals[i], - TERM_MODE_TEXT, giVT_RealWidth, giVT_RealHeight ); - - gVT_Terminals[i].Name[0] = '0'+i; - gVT_Terminals[i].Name[1] = '\0'; - gVT_Terminals[i].Node.Inode = i; - gVT_Terminals[i].Node.ImplPtr = &gVT_Terminals[i]; - gVT_Terminals[i].Node.NumACLs = 0; // Only root can open virtual terminals - - gVT_Terminals[i].Node.Type = &gVT_TermNodeType; -// Semaphore_Init(&gVT_Terminals[i].InputSemaphore, 0, MAX_INPUT_CHARS8, "VTerm", gVT_Terminals[i].Name); - } - - // Add to DevFS - DevFS_AddDevice( &gVT_DrvInfo ); - - // Set kernel output to VT0 - Debug_SetKTerminal("/Devices/VTerm/0"); - - return MODULE_ERR_OK; -} - -/** - * \brief Set the video resolution - * \param Width New screen width - * \param Height New screen height - */ -void VT_SetResolution(int Width, int Height) -{ - tVideo_IOCtl_Mode mode = {0}; - int tmp; - int i; - - // Create the video mode - mode.width = Width; - mode.height = Height; - mode.bpp = 32; - mode.flags = 0; - - // Set video mode - VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_FINDMODE, &mode ); - tmp = mode.id; - if( Width != mode.width || Height != mode.height ) - { - Log_Warning("VTerm", - "Selected resolution (%ix%i is not supported) by the device, using (%ix%i)", - giVT_RealWidth, giVT_RealHeight, - mode.width, mode.height - ); - giVT_RealWidth = mode.width; - giVT_RealHeight = mode.height; - } - VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_GETSETMODE, &tmp ); - - // Resize text terminals if needed - if( gVT_Terminals[0].Text && (giVT_RealWidth != mode.width || giVT_RealHeight != mode.height) ) - { - int newBufSize = (giVT_RealWidth/giVT_CharWidth) - *(giVT_RealHeight/giVT_CharHeight) - *(giVT_Scrollback+1); - //tVT_Char *tmp; - // Resize the text terminals - Log_Debug("VTerm", "Resizing terminals to %ix%i", - giVT_RealWidth/giVT_CharWidth, giVT_RealHeight/giVT_CharHeight); - for( i = 0; i < NUM_VTS; i ++ ) - { - if( gVT_Terminals[i].Mode != TERM_MODE_TEXT ) continue; - - gVT_Terminals[i].TextWidth = giVT_RealWidth/giVT_CharWidth; - gVT_Terminals[i].TextHeight = giVT_RealHeight/giVT_CharHeight; - gVT_Terminals[i].ScrollHeight = gVT_Terminals[i].TextHeight; - - gVT_Terminals[i].Text = realloc( - gVT_Terminals[i].Text, - newBufSize*sizeof(tVT_Char) - ); - } - } -} - -/** - * \fn char *VT_ReadDir(tVFS_Node *Node, int Pos) - * \brief Read from the VTerm Directory - */ -char *VT_ReadDir(tVFS_Node *Node, int Pos) -{ - if(Pos < 0) return NULL; - if(Pos >= NUM_VTS) return NULL; - return strdup( gVT_Terminals[Pos].Name ); -} - -/** - * \fn tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name) - * \brief Find an item in the VTerm directory - * \param Node Root node - * \param Name Name (number) of the terminal - */ -tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name) -{ - int num; - - ENTER("pNode sName", Node, Name); - - // Open the input and output files if needed - if(giVT_OutputDevHandle == -2) VT_InitOutput(); - if(giVT_InputDevHandle == -2) VT_InitInput(); - - // Sanity check name - if(Name[0] < '0' || Name[0] > '9' || Name[1] != '\0') { - LEAVE('n'); - return NULL; - } - // Get index - num = Name[0] - '0'; - if(num >= NUM_VTS) { - LEAVE('n'); - return NULL; - } - // Return node - LEAVE('p', &gVT_Terminals[num].Node); - return &gVT_Terminals[num].Node; -} - -/** - * \fn int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data) - * \brief Control the VTerm Driver - */ -int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data) -{ - int len; - switch(Id) - { - case DRV_IOCTL_TYPE: return DRV_TYPE_MISC; - case DRV_IOCTL_IDENT: memcpy(Data, "VT\0\0", 4); return 0; - case DRV_IOCTL_VERSION: return VERSION; - case DRV_IOCTL_LOOKUP: return 0; - - case 4: // Get Video Driver - if(Data) strcpy(Data, gsVT_OutputDevice); - return strlen(gsVT_OutputDevice); - - case 5: // Set Video Driver - if(!Data) return -EINVAL; - if(Threads_GetUID() != 0) return -EACCES; - - len = strlen(Data); - - // TODO: Check if the string used is a heap string - - free(gsVT_OutputDevice); - - gsVT_OutputDevice = malloc(len+1); - strcpy(gsVT_OutputDevice, Data); - - VFS_Close(giVT_OutputDevHandle); - giVT_OutputDevHandle = -1; - - VT_InitOutput(); - return 1; - } - return 0; -} - -/** - * \brief Read from a virtual terminal - */ -Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - int pos = 0; - int avail; - tVTerm *term = &gVT_Terminals[ Node->Inode ]; - Uint32 *codepoint_buf = Buffer; - Uint32 *codepoint_in; - - Mutex_Acquire( &term->ReadingLock ); - - // Check current mode - switch(term->Mode) - { - // Text Mode (UTF-8) - case TERM_MODE_TEXT: - VT_int_UpdateCursor(term, 1); - - VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UTF-8)"); - - avail = term->InputWrite - term->InputRead; - if(avail < 0) - avail += MAX_INPUT_CHARS8; - if(avail > Length - pos) - avail = Length - pos; - - while( avail -- ) - { - ((char*)Buffer)[pos] = term->InputBuffer[term->InputRead]; - pos ++; - term->InputRead ++; - while(term->InputRead >= MAX_INPUT_CHARS8) - term->InputRead -= MAX_INPUT_CHARS8; - } - break; - - //case TERM_MODE_FB: - // Other - UCS-4 - default: - VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UCS-4)"); - - avail = term->InputWrite - term->InputRead; - if(avail < 0) - avail += MAX_INPUT_CHARS32; - Length /= 4; - if(avail > Length - pos) - avail = Length - pos; - - codepoint_in = (void*)term->InputBuffer; - codepoint_buf = Buffer; - - while( avail -- ) - { - codepoint_buf[pos] = codepoint_in[term->InputRead]; - pos ++; - term->InputRead ++; - while(term->InputRead >= MAX_INPUT_CHARS32) - term->InputRead -= MAX_INPUT_CHARS32; - } - pos *= 4; - break; - } - - // Mark none avaliable if buffer empty - if( term->InputRead == term->InputWrite ) - VFS_MarkAvaliable(&term->Node, 0); - - term->ReadingThread = -1; - -// VT_int_UpdateCursor(term, term->Mode == TERM_MODE_TEXT); - - Mutex_Release( &term->ReadingLock ); - - return pos; -} - -/** - * \fn Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) - * \brief Write to a virtual terminal - */ -Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - tVTerm *term = &gVT_Terminals[ Node->Inode ]; - int size; - - // Write - switch( term->Mode ) - { - // Print Text - case TERM_MODE_TEXT: - VT_int_PutString(term, Buffer, Length); - break; - - // Framebuffer :) - case TERM_MODE_FB: - // - Sanity Checking - size = term->Width*term->Height*4; - if( Offset > size ) { - Log_Notice("VTerm", "VT_Write: Offset (0x%llx) > FBSize (0x%x)", - Offset, size); - return 0; - } - if( Offset + Length > size ) { - Log_Notice("VTerm", "VT_Write: Offset+Length (0x%llx) > FBSize (0x%x)", - Offset+Length, size); - Length = size - Offset; - } - - // Update screen if needed - if( Node->Inode == giVT_CurrentTerminal ) - { - if( giVT_RealHeight > term->Height ) - Offset += (giVT_RealHeight - term->Height) / 2 * term->Width * 4; - // Handle undersized virtual terminals - if( giVT_RealWidth > term->Width ) - { - // No? :( Well, just center it - int x, y, w, h; - Uint dst_ofs; - // TODO: Fix to handle the final line correctly? - x = Offset/4; y = x / term->Width; x %= term->Width; - w = Length/4+x; h = w / term->Width; w %= term->Width; - - // Center - x += (giVT_RealWidth - term->Width) / 2; - dst_ofs = (x + y * giVT_RealWidth) * 4; - while(h--) - { - VFS_WriteAt( giVT_OutputDevHandle, - dst_ofs, - term->Width * 4, - Buffer - ); - Buffer = (void*)( (Uint)Buffer + term->Width*4 ); - dst_ofs += giVT_RealWidth * 4; - } - return 0; - } - else - { - return VFS_WriteAt( giVT_OutputDevHandle, Offset, Length, Buffer ); - } - } - else - { - if( !term->Buffer ) - term->Buffer = malloc( term->Width * term->Height * 4 ); - // Copy to the local cache - memcpy( (char*)term->Buffer + (Uint)Offset, Buffer, Length ); - } - break; - // Just pass on (for now) - // TODO: Handle locally too to ensure no information is lost on - // VT Switch (and to isolate terminals from each other) - case TERM_MODE_2DACCEL: - //case TERM_MODE_3DACCEL: - if( Node->Inode == giVT_CurrentTerminal ) - { - VFS_Write( giVT_OutputDevHandle, Length, Buffer ); - } - break; - } - - return 0; -} - -/** - * \fn int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data) - * \brief Call an IO Control on a virtual terminal - */ -int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data) -{ - int *iData = Data; - int ret; - tVTerm *term = Node->ImplPtr; - ENTER("pNode iId pData", Node, Id, Data); - - if(Id >= DRV_IOCTL_LOOKUP) { - // Only root can fiddle with graphics modes - // TODO: Remove this and replace with user ownership - if( Threads_GetUID() != 0 ) return -1; - } - - switch(Id) - { - // --- Core Defined - case DRV_IOCTL_TYPE: - LEAVE('i', DRV_TYPE_TERMINAL); - return DRV_TYPE_TERMINAL; - case DRV_IOCTL_IDENT: - memcpy(Data, "VT\0\0", 4); - LEAVE('i', 0); - return 0; - case DRV_IOCTL_VERSION: - LEAVE('x', VERSION); - return VERSION; - case DRV_IOCTL_LOOKUP: - LEAVE('i', 0); - return 0; - - // Get/Set the mode (and apply any changes) - case TERM_IOCTL_MODETYPE: - if(Data != NULL) - { - if( CheckMem(Data, sizeof(int)) == 0 ) { - LEAVE('i', -1); - return -1; - } - Log_Log("VTerm", "VTerm %i mode set to %i", (int)Node->Inode, *iData); - - // Update mode if needed - if( term->Mode != *iData || term->NewWidth || term->NewHeight) - { - // Adjust for text mode - if( *iData == TERM_MODE_TEXT ) { - term->NewHeight *= giVT_CharHeight; - term->NewWidth *= giVT_CharWidth; - } - // Fill unchanged dimensions - if(term->NewHeight == 0) term->NewHeight = term->Height; - if(term->NewWidth == 0) term->NewWidth = term->Width; - // Set new mode - VT_int_ChangeMode(term, *iData, term->NewWidth, term->NewHeight); - // Clear unapplied dimensions - term->NewWidth = 0; - term->NewHeight = 0; - } - - // Update the screen dimensions - if(Node->Inode == giVT_CurrentTerminal) - VT_SetTerminal( giVT_CurrentTerminal ); - } - LEAVE('i', term->Mode); - return term->Mode; - - // Get/set the terminal width - case TERM_IOCTL_WIDTH: - if(Data != NULL) { - if( CheckMem(Data, sizeof(int)) == 0 ) { - LEAVE('i', -1); - return -1; - } - term->NewWidth = *iData; - } - if( term->NewWidth ) - ret = term->NewWidth; - else if( term->Mode == TERM_MODE_TEXT ) - ret = term->TextWidth; - else - ret = term->Width; - LEAVE('i', ret); - return ret; - - // Get/set the terminal height - case TERM_IOCTL_HEIGHT: - if(Data != NULL) { - if( CheckMem(Data, sizeof(int)) == 0 ) { - LEAVE('i', -1); - return -1; - } - term->NewHeight = *iData; - } - if( term->NewHeight ) - ret = term->NewHeight; - else if( term->Mode == TERM_MODE_TEXT ) - ret = term->TextHeight; - else - ret = term->Height; - LEAVE('i', ret); - return ret; - - case TERM_IOCTL_FORCESHOW: - Log_Log("VTerm", "Thread %i forced VTerm %i to be shown", - Threads_GetTID(), (int)Node->Inode); - VT_SetTerminal( Node->Inode ); - LEAVE('i', 1); - return 1; - - case TERM_IOCTL_GETSETCURSOR: - if(Data != NULL) - { - tVideo_IOCtl_Pos *pos = Data; - if( !CheckMem(Data, sizeof(*pos)) ) { - errno = -EINVAL; - LEAVE('i', -1); - return -1; - } - - if( term->Mode == TERM_MODE_TEXT ) - { - if(term->Flags & VT_FLAG_ALTBUF) - term->AltWritePos = pos->x + pos->y * term->TextWidth; - else - term->WritePos = pos->x + pos->y * term->TextWidth + term->ViewPos; - VT_int_UpdateCursor(term, 0); - } - else - { - term->VideoCursorX = pos->x; - term->VideoCursorY = pos->y; - VT_int_UpdateCursor(term, 1); - } - } - ret = (term->Flags & VT_FLAG_ALTBUF) ? term->AltWritePos : term->WritePos-term->ViewPos; - LEAVE('i', ret); - return ret; - - case TERM_IOCTL_SETCURSORBITMAP: { - tVideo_IOCtl_Bitmap *bmp = Data; - if( Data == NULL ) - { - free( term->VideoCursor ); - term->VideoCursor = NULL; - LEAVE('i', 0); - return 0; - } - - // Sanity check bitmap - if( !CheckMem(bmp, sizeof(tVideo_IOCtl_Bitmap)) ) { - Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp); - errno = -EINVAL; - LEAVE_RET('i', -1); - } - if( !CheckMem(bmp->Data, bmp->W*bmp->H*sizeof(Uint32)) ) { - Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp); - errno = -EINVAL; - LEAVE_RET('i', -1); - } - - // Reallocate if needed - if(term->VideoCursor) - { - if(bmp->W * bmp->H != term->VideoCursor->W * term->VideoCursor->H) { - free(term->VideoCursor); - term->VideoCursor = NULL; - } - } - if(!term->VideoCursor) { - term->VideoCursor = malloc(sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32)); - if(!term->VideoCursor) { - Log_Error("VTerm", "Unable to allocate memory for cursor"); - errno = -ENOMEM; - LEAVE_RET('i', -1); - } - } - - memcpy(term->VideoCursor, bmp, sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32)); - - Log_Debug("VTerm", "Set VT%i's cursor to %p %ix%i", - (int)term->Node.Inode, bmp, bmp->W, bmp->H); - - if(gpVT_CurTerm == term) - VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, term->VideoCursor); - - LEAVE('i', 0); - return 0; } - } - LEAVE('i', -1); - return -1; -} - -/** - * \fn void VT_SetTerminal(int ID) - * \brief Set the current terminal - */ -void VT_SetTerminal(int ID) -{ - // Copy the screen state - if( ID != giVT_CurrentTerminal && gpVT_CurTerm->Mode != TERM_MODE_TEXT ) - { - if( !gpVT_CurTerm->Buffer ) - gpVT_CurTerm->Buffer = malloc( gpVT_CurTerm->Width*gpVT_CurTerm->Height*4 ); - if( gpVT_CurTerm->Width < giVT_RealWidth ) - { - int line; - Uint ofs = 0; - Uint32 *dest = gpVT_CurTerm->Buffer; - // Slower scanline copy - for( line = 0; line < gpVT_CurTerm->Height; line ++ ) - { - VFS_ReadAt(giVT_OutputDevHandle, ofs, gpVT_CurTerm->Width*4, dest); - ofs += giVT_RealWidth * 4; - dest += gpVT_CurTerm->Width; - } - } - else - { - VFS_ReadAt(giVT_OutputDevHandle, - 0, gpVT_CurTerm->Height*giVT_RealWidth*4, - gpVT_CurTerm->Buffer - ); - } - } - - // Update current terminal ID - Log_Log("VTerm", "Changed terminal from %i to %i", giVT_CurrentTerminal, ID); - giVT_CurrentTerminal = ID; - gpVT_CurTerm = &gVT_Terminals[ID]; - - if( gpVT_CurTerm->Mode == TERM_MODE_TEXT ) - { - VT_SetMode( VIDEO_BUFFMT_TEXT ); - } - else - { - // Update the cursor image - if(gpVT_CurTerm->VideoCursor) - VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, gpVT_CurTerm->VideoCursor); - VT_SetMode( VIDEO_BUFFMT_FRAMEBUFFER ); - } - - if(gpVT_CurTerm->Buffer) - { - // TODO: Handle non equal sized - VFS_WriteAt( - giVT_OutputDevHandle, - 0, - gpVT_CurTerm->Width*gpVT_CurTerm->Height*sizeof(Uint32), - gpVT_CurTerm->Buffer - ); - } - - VT_int_UpdateCursor(gpVT_CurTerm, 1); - // Update the screen - VT_int_UpdateScreen(gpVT_CurTerm, 1); -} diff --git a/Kernel/drv/vterm.h b/Kernel/drv/vterm.h deleted file mode 100644 index cd37ebd9..00000000 --- a/Kernel/drv/vterm.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * drv/vterm.h - * - Virtual Terminal - Common - */ -#ifndef _VTERM_H_ -#define _VTERM_H_ - -#include -#include // tVT_Char -#include -#include - -// === CONSTANTS === -#define MAX_INPUT_CHARS32 64 -#define MAX_INPUT_CHARS8 (MAX_INPUT_CHARS32*4) -#define DEFAULT_COLOUR (VT_COL_BLACK|(0xAAA<<16)) - -/** - * \{ - */ -#define VT_FLAG_HIDECSR 0x01 //!< Hide the cursor -#define VT_FLAG_ALTBUF 0x02 //!< Alternate screen buffer -#define VT_FLAG_RAWIN 0x04 //!< Don't handle ^Z/^C/^V -#define VT_FLAG_HASFB 0x10 //!< Set if the VTerm has requested the Framebuffer -#define VT_FLAG_SHOWCSR 0x20 //!< Always show the text cursor -/** - * \} - */ - -enum eVT_InModes { - VT_INMODE_TEXT8, // UTF-8 Text Mode (VT100/xterm Emulation) - VT_INMODE_TEXT32, // UTF-32 Text Mode (Acess Native) - NUM_VT_INMODES -}; - - -// === TYPES == -typedef struct sVTerm tVTerm; - -// === STRUCTURES === -struct sVTerm -{ - int Mode; //!< Current Mode (see ::eTplTerminal_Modes) - int Flags; //!< Flags (see VT_FLAG_*) - - short NewWidth; //!< Un-applied dimensions (Width) - short NewHeight; //!< Un-applied dimensions (Height) - short Width; //!< Virtual Width - short Height; //!< Virtual Height - short TextWidth; //!< Text Virtual Width - short TextHeight; //!< Text Virtual Height - - Uint32 CurColour; //!< Current Text Colour - - int ViewPos; //!< View Buffer Offset (Text Only) - int WritePos; //!< Write Buffer Offset (Text Only) - tVT_Char *Text; - - tVT_Char *AltBuf; //!< Alternate Screen Buffer - int AltWritePos; //!< Alternate write position - short ScrollTop; //!< Top of scrolling region (smallest) - short ScrollHeight; //!< Length of scrolling region - - int VideoCursorX; - int VideoCursorY; - - tMutex ReadingLock; //!< Lock the VTerm when a process is reading from it - tTID ReadingThread; //!< Owner of the lock - int InputRead; //!< Input buffer read position - int InputWrite; //!< Input buffer write position - char InputBuffer[MAX_INPUT_CHARS8]; -// tSemaphore InputSemaphore; - - Uint32 *Buffer; - - // TODO: Do I need to keep this about? - // When should it be deallocated? on move to text mode, or some other time - // Call set again, it's freed, and if NULL it doesn't get reallocated. - tVideo_IOCtl_Bitmap *VideoCursor; - - char Name[2]; //!< Name of the terminal - tVFS_Node Node; -}; - -// === GOBALS === -extern tVTerm *gpVT_CurTerm; -extern int giVT_Scrollback; -extern short giVT_RealWidth; //!< Screen Width -extern short giVT_RealHeight; //!< Screen Width -extern char *gsVT_OutputDevice; -extern char *gsVT_InputDevice; -extern int giVT_OutputDevHandle; -extern int giVT_InputDevHandle; - -// === FUNCTIONS === -extern void VT_SetResolution(int Width, int Height); -extern void VT_SetTerminal(int ID); -// --- Output --- -extern void VT_InitOutput(void); -extern void VT_SetMode(int Mode); -extern void VT_int_ScrollFramebuffer( tVTerm *Term, int Count ); -extern void VT_int_UpdateCursor( tVTerm *Term, int bShow ); -extern void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ); -// --- Input --- -extern void VT_InitInput(void); -extern void VT_KBCallBack(Uint32 Codepoint); -// --- VT100 Emulation --- -extern void VT_int_ParseEscape_StandardLarge(tVTerm *Term, char CmdChar, int argc, int *args); -extern int VT_int_ParseEscape(tVTerm *Term, const char *Buffer); -// --- Terminal Buffer --- -extern void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count); -extern void VT_int_PutChar(tVTerm *Term, Uint32 Ch); -extern void VT_int_ScrollText(tVTerm *Term, int Count); -extern void VT_int_ClearLine(tVTerm *Term, int Num); -extern void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight); -extern void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled); - -#endif - diff --git a/Kernel/drv/vterm_font.c b/Kernel/drv/vterm_font.c deleted file mode 100644 index 841ea504..00000000 --- a/Kernel/drv/vterm_font.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * drv/vterm_font.c - * - Virtual Terminal - Font rendering code - */ -#include "vterm.h" -#include - -// --- -// Font Render -// --- -#define MONOSPACE_FONT 10816 - -#if MONOSPACE_FONT == 10808 // 8x8 -# include "vterm_font_8x8.h" -#elif MONOSPACE_FONT == 10816 // 8x16 -# include "vterm_font_8x16.h" -#endif - -// === PROTOTYPES === -Uint8 *VT_Font_GetChar(Uint32 Codepoint); - -// === GLOBALS === -int giVT_CharWidth = FONT_WIDTH; -int giVT_CharHeight = FONT_HEIGHT; - -// === CODE === -/** - * \brief Render a font character - */ -void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Depth, int Pitch, Uint32 BGC, Uint32 FGC) -{ - Uint8 *font; - int x, y; - - // 8-bpp and below - if( Depth <= 8 ) - { - Uint8 *buf = Buffer; - - font = VT_Font_GetChar(Codepoint); - - for(y = 0; y < FONT_HEIGHT; y ++) - { - for(x = 0; x < FONT_WIDTH; x ++) - { - if(*font & (1 << (FONT_WIDTH-x-1))) - buf[x] = FGC; - else - buf[x] = BGC; - } - buf = (void*)( (tVAddr)buf + Pitch ); - font ++; - } - } - // 16-bpp and below - else if( Depth <= 16 ) - { - Uint16 *buf = Buffer; - - font = VT_Font_GetChar(Codepoint); - - for(y = 0; y < FONT_HEIGHT; y ++) - { - for(x = 0; x < FONT_WIDTH; x ++) - { - if(*font & (1 << (FONT_WIDTH-x-1))) - buf[x] = FGC; - else - buf[x] = BGC; - } - buf = (void*)( (tVAddr)buf + Pitch ); - font ++; - } - } - // 24-bpp colour - // - Special handling to not overwrite the next pixel - //TODO: Endian issues here - else if( Depth == 24 ) - { - Uint8 *buf = Buffer; - Uint8 bg_r = (BGC >> 16) & 0xFF; - Uint8 bg_g = (BGC >> 8) & 0xFF; - Uint8 bg_b = (BGC >> 0) & 0xFF; - Uint8 fg_r = (FGC >> 16) & 0xFF; - Uint8 fg_g = (FGC >> 8) & 0xFF; - Uint8 fg_b = (FGC >> 0) & 0xFF; - - font = VT_Font_GetChar(Codepoint); - - for(y = 0; y < FONT_HEIGHT; y ++) - { - for(x = 0; x < FONT_WIDTH; x ++) - { - Uint8 r, g, b; - - if(*font & (1 << (FONT_WIDTH-x-1))) { - r = fg_r; g = fg_g; b = fg_b; - } - else { - r = bg_r; g = bg_g; b = bg_b; - } - buf[x*3+0] = b; - buf[x*3+1] = g; - buf[x*3+2] = r; - } - buf = (void*)( (tVAddr)buf + Pitch ); - font ++; - } - } - // 32-bpp colour (nice and easy) - else if( Depth == 32 ) - { - Uint32 *buf = Buffer; - - font = VT_Font_GetChar(Codepoint); - - for(y = 0; y < FONT_HEIGHT; y ++) - { - for(x = 0; x < FONT_WIDTH; x ++) - { - if(*font & (1 << (FONT_WIDTH-x-1))) - buf[x] = FGC; - else - buf[x] = BGC; - } - buf = (Uint32*)( (tVAddr)buf + Pitch ); - font ++; - } - } -} - -/** - * \fn Uint32 VT_Colour12to24(Uint16 Col12) - * \brief Converts a 12-bit colour into 24 bits - */ -Uint32 VT_Colour12to24(Uint16 Col12) -{ - Uint32 ret; - int tmp; - tmp = Col12 & 0xF; - ret = (tmp << 0) | (tmp << 4); - tmp = (Col12 & 0xF0) >> 4; - ret |= (tmp << 8) | (tmp << 12); - tmp = (Col12 & 0xF00) >> 8; - ret |= (tmp << 16) | (tmp << 20); - return ret; -} -/** - * \brief Converts a 12-bit colour into 15 bits - */ -Uint16 VT_Colour12to15(Uint16 Col12) -{ - Uint32 ret; - int tmp; - tmp = Col12 & 0xF; - ret = (tmp << 1) | (tmp & 1); - tmp = (Col12 & 0xF0) >> 4; - ret |= ( (tmp << 1) | (tmp & 1) ) << 5; - tmp = (Col12 & 0xF00) >> 8; - ret |= ( (tmp << 1) | (tmp & 1) ) << 10; - return ret; -} - -/** - * \brief Converts a 12-bit colour into any other depth - * \param Col12 12-bit source colour - * \param Depth Desired bit deptj - * \note Green then blue get the extra avaliable bits (16:5-6-5, 14:4-5-5) - */ -Uint32 VT_Colour12toN(Uint16 Col12, int Depth) -{ - Uint32 ret; - Uint32 r, g, b; - int rSize, gSize, bSize; - - // Fast returns - if( Depth == 24 ) return VT_Colour12to24(Col12); - if( Depth == 15 ) return VT_Colour12to15(Col12); - // - 32 is a special case, it's usually 24-bit colour with an unused byte - if( Depth == 32 ) return VT_Colour12to24(Col12); - - // Bounds checks - if( Depth < 8 ) return 0; - if( Depth > 32 ) return 0; - - r = Col12 & 0xF; - g = (Col12 & 0xF0) >> 4; - b = (Col12 & 0xF00) >> 8; - - rSize = gSize = bSize = Depth / 3; - if( rSize + gSize + bSize < Depth ) // Depth % 3 == 1 - gSize ++; - if( rSize + gSize + bSize < Depth ) // Depth % 3 == 2 - bSize ++; - - // Expand - r <<= rSize - 4; g <<= gSize - 4; b <<= bSize - 4; - // Fill with the lowest bit - if( Col12 & 0x001 ) r |= (1 << (rSize - 4)) - 1; - if( Col12 & 0x010 ) r |= (1 << (gSize - 4)) - 1; - if( Col12 & 0x100 ) r |= (1 << (bSize - 4)) - 1; - - // Create output - ret = r; - ret |= g << rSize; - ret |= b << (rSize + gSize); - - return ret; -} - -/** - * \fn Uint8 *VT_Font_GetChar(Uint32 Codepoint) - * \brief Gets an index into the font array given a Unicode Codepoint - * \note See http://en.wikipedia.org/wiki/CP437 - */ -Uint8 *VT_Font_GetChar(Uint32 Codepoint) -{ - int index = 0; - if(Codepoint < 128) - return &VTermFont[Codepoint*FONT_HEIGHT]; - switch(Codepoint) - { - case 0xC7: index = 128; break; // Ç - case 0xFC: index = 129; break; // ü - case 0xE9: index = 130; break; // é - case 0xE2: index = 131; break; // â - case 0xE4: index = 132; break; // ä - case 0xE0: index = 133; break; // à - case 0xE5: index = 134; break; // å - case 0xE7: index = 135; break; // ç - case 0xEA: index = 136; break; // ê - case 0xEB: index = 137; break; // ë - case 0xE8: index = 138; break; // è - case 0xEF: index = 139; break; // ï - case 0xEE: index = 140; break; // î - case 0xEC: index = 141; break; // ì - case 0xC4: index = 142; break; // Ä - case 0xC5: index = 143; break; // Å - } - - return &VTermFont[index*FONT_HEIGHT]; -} - -EXPORTAS(&giVT_CharWidth, giVT_CharWidth); -EXPORTAS(&giVT_CharHeight, giVT_CharHeight); -EXPORT(VT_Font_Render); -EXPORT(VT_Colour12to24); diff --git a/Kernel/drv/vterm_font_8x16.h b/Kernel/drv/vterm_font_8x16.h deleted file mode 100644 index 7c613325..00000000 --- a/Kernel/drv/vterm_font_8x16.h +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Taken from http://cvs.savannah.gnu.org/viewvc/vgabios/vgafonts.h?root=vgabios&view=markup - * Altered for Acess2 - */ -#define FONT_WIDTH 8 -#define FONT_HEIGHT 16 -static Uint8 VTermFont[256*16]= -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, - 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, - 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, - 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, - 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, - 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, - 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, - 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, - 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, - 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, - 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, - 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, - 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, - 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; diff --git a/Kernel/drv/vterm_font_8x8.h b/Kernel/drv/vterm_font_8x8.h deleted file mode 100644 index 2a5311d3..00000000 --- a/Kernel/drv/vterm_font_8x8.h +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Taken from http://cvs.savannah.gnu.org/viewvc/vgabios/vgafonts.h?root=vgabios&view=markup - * Altered for Acess2 - */ -#define FONT_WIDTH 8 -#define FONT_HEIGHT 8 -static Uint8 VTermFont[256*8]= -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, - 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, - 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, - 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, - 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c, - 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c, - 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, - 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, - 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, - 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, - 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, - 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, - 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0, - 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0, - 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99, - 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, - 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, - 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, - 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, - 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, - 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, - 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff, - 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, - 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, - 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, - 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, - 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, - 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, - 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, - 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, - 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, - 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00, - 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, - 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, - 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, - 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, - 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, - 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, - 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, - 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, - 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00, - 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, - 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00, - 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00, - 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, - 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00, - 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00, - 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, - 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00, - 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00, - 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, - 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, - 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, - 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, - 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, - 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00, - 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, - 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, - 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, - 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, - 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, - 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, - 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, - 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00, - 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00, - 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, - 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, - 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, - 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, - 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, - 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, - 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, - 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, - 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00, - 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, - 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00, - 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, - 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00, - 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, - 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00, - 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, - 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00, - 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, - 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, - 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, - 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, - 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, - 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, - 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00, - 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00, - 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, - 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, - 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00, - 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, - 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, - 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, - 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, - 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, - 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, - 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00, - 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, - 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, - 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, - 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, - 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00, - 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00, - 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00, - 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, - 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, - 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, - 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, - 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, - 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, - 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00, - 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, - 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00, - 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, - 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78, - 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, - 0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, - 0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00, - 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, - 0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, - 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, - 0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38, - 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, - 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, - 0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, - 0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, - 0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, - 0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, - 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, - 0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00, - 0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00, - 0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00, - 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00, - 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, - 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, - 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, - 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, - 0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, - 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, - 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00, - 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, - 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18, - 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00, - 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30, - 0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7, - 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70, - 0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, - 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, - 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, - 0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, - 0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00, - 0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00, - 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, - 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, - 0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00, - 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00, - 0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f, - 0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03, - 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, - 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00, - 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, - 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, - 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, - 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, - 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, - 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, - 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, - 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, - 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, - 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, - 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, - 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, - 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, - 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, - 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, - 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, - 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, - 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, - 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, - 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, - 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, - 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, - 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, - 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00, - 0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0, - 0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, - 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, - 0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00, - 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00, - 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0, - 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00, - 0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc, - 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00, - 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00, - 0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00, - 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00, - 0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0, - 0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00, - 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, - 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00, - 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00, - 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, - 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00, - 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, - 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00, - 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, - 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c, - 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, - 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; diff --git a/Kernel/drv/vterm_input.c b/Kernel/drv/vterm_input.c deleted file mode 100644 index d83cedee..00000000 --- a/Kernel/drv/vterm_input.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * drv/vterm_input.c - * - Virtual Terminal - Input code - */ -#include "vterm.h" -#include - -// === GLOBALS === -// --- Key States --- (Used for VT Switching/Magic Combos) - int gbVT_CtrlDown = 0; - int gbVT_AltDown = 0; - int gbVT_SysrqDown = 0; - -// === CODE === -/** - * \fn void VT_InitInput() - * \brief Initialises the input - */ -void VT_InitInput() -{ - giVT_InputDevHandle = VFS_Open(gsVT_InputDevice, VFS_OPENFLAG_READ); - if(giVT_InputDevHandle == -1) { - Log_Warning("VTerm", "Can't open the input device '%s'", gsVT_InputDevice); - return ; - } - VFS_IOCtl(giVT_InputDevHandle, KB_IOCTL_SETCALLBACK, VT_KBCallBack); -} - -/** - * \fn void VT_KBCallBack(Uint32 Codepoint) - * \brief Called on keyboard interrupt - * \param Codepoint Pseudo-UTF32 character - * - * Handles a key press and sends the key code to the user's buffer. - * If the code creates a kernel-magic sequence, it is not passed to the - * user and is handled in-kernel. - */ -void VT_KBCallBack(Uint32 Codepoint) -{ - tVTerm *term = gpVT_CurTerm; - - // Catch VT binds - switch( Codepoint & KEY_ACTION_MASK ) - { - case KEY_ACTION_RELEASE: - switch(Codepoint & KEY_CODEPOINT_MASK) - { - case KEY_LALT: gbVT_AltDown &= ~1; break; - case KEY_RALT: gbVT_AltDown &= ~2; break; - case KEY_LCTRL: gbVT_CtrlDown &= ~1; break; - case KEY_RCTRL: gbVT_CtrlDown &= ~2; break; - } - break; - - case KEY_ACTION_PRESS: - switch(Codepoint & KEY_CODEPOINT_MASK) - { - case KEY_LALT: gbVT_AltDown |= 1; break; - case KEY_RALT: gbVT_AltDown |= 2; break; - case KEY_LCTRL: gbVT_CtrlDown |= 1; break; - case KEY_RCTRL: gbVT_CtrlDown |= 2; break; - } - - if(!gbVT_AltDown || !gbVT_CtrlDown) - break; - switch(Codepoint & KEY_CODEPOINT_MASK) - { - case KEY_F1: VT_SetTerminal(0); return; - case KEY_F2: VT_SetTerminal(1); return; - case KEY_F3: VT_SetTerminal(2); return; - case KEY_F4: VT_SetTerminal(3); return; - case KEY_F5: VT_SetTerminal(4); return; - case KEY_F6: VT_SetTerminal(5); return; - case KEY_F7: VT_SetTerminal(6); return; - case KEY_F8: VT_SetTerminal(7); return; - case KEY_F9: VT_SetTerminal(8); return; - case KEY_F10: VT_SetTerminal(9); return; - case KEY_F11: VT_SetTerminal(10); return; - case KEY_F12: VT_SetTerminal(11); return; - } - - // Scrolling is only valid in text mode - if(gpVT_CurTerm->Mode != TERM_MODE_TEXT) - break; - - switch(Codepoint & KEY_CODEPOINT_MASK) - { - // Scrolling - case KEY_PGUP: - if( gpVT_CurTerm->Flags & VT_FLAG_ALTBUF ) - return ; - gpVT_CurTerm->ViewPos = MAX( - 0, - gpVT_CurTerm->ViewPos - gpVT_CurTerm->Width - ); - return; - case KEY_PGDOWN: - if( gpVT_CurTerm->Flags & VT_FLAG_ALTBUF ) - return ; - gpVT_CurTerm->ViewPos = MIN( - gpVT_CurTerm->ViewPos + gpVT_CurTerm->Width, - gpVT_CurTerm->Width * gpVT_CurTerm->Height*giVT_Scrollback - ); - return; - } - break; - } - - // Encode key - if(term->Mode == TERM_MODE_TEXT) - { - Uint8 buf[6] = {0}; - int len = 0; - - // Ignore anything that isn't a press or refire - if( (Codepoint & KEY_ACTION_MASK) != KEY_ACTION_PRESS - && (Codepoint & KEY_ACTION_MASK) != KEY_ACTION_REFIRE - ) - { - return ; - } - - Codepoint &= KEY_CODEPOINT_MASK; - - // Ignore Modifer Keys - if(Codepoint > KEY_MODIFIERS) return; - - // Get UTF-8/ANSI Encoding - switch(Codepoint) - { - // 0: No translation, don't send to user - case 0: break; - case KEY_LEFT: - buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'D'; - len = 3; - break; - case KEY_RIGHT: - buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'C'; - len = 3; - break; - case KEY_UP: - buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'A'; - len = 3; - break; - case KEY_DOWN: - buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'B'; - len = 3; - break; - - case KEY_PGUP: - buf[0] = '\x1B'; buf[1] = '['; buf[2] = '5'; buf[3] = '~'; - len = 4; - break; - case KEY_PGDOWN: - buf[0] = '\x1B'; buf[1] = '['; buf[2] = '6'; buf[3] = '~'; - len = 4; - break; - - // Attempt to encode in UTF-8 - default: - len = WriteUTF8( buf, Codepoint ); - if(len == 0) { - Warning("Codepoint (%x) is unrepresentable in UTF-8", Codepoint); - } - break; - } - - if(len == 0) { - // Unprintable / Don't Pass - return; - } - -#if 0 - // Handle meta characters - if( !(term->Flags & VT_FLAG_RAWIN) ) - { - switch(buf[0]) - { - case '\3': // ^C - - break; - } - } -#endif - - // Write - if( MAX_INPUT_CHARS8 - term->InputWrite >= len ) - memcpy( &term->InputBuffer[term->InputWrite], buf, len ); - else { - memcpy( &term->InputBuffer[term->InputWrite], buf, MAX_INPUT_CHARS8 - term->InputWrite ); - memcpy( &term->InputBuffer[0], buf, len - (MAX_INPUT_CHARS8 - term->InputWrite) ); - } - // Roll the buffer over - term->InputWrite += len; - term->InputWrite %= MAX_INPUT_CHARS8; - if( (term->InputWrite - term->InputRead + MAX_INPUT_CHARS8)%MAX_INPUT_CHARS8 < len ) { - term->InputRead = term->InputWrite + 1; - term->InputRead %= MAX_INPUT_CHARS8; - } - } - else - { - // Encode the raw key event - Uint32 *raw_in = (void*)term->InputBuffer; - - #if 0 - // Drop new keys - if( term->InputWrite == term->InputRead ) - return ; - #endif - - raw_in[ term->InputWrite ] = Codepoint; - term->InputWrite ++; - if(term->InputWrite >= MAX_INPUT_CHARS32) - term->InputWrite -= MAX_INPUT_CHARS32; - - #if 1 - // TODO: Should old or new be dropped? - if(term->InputRead == term->InputWrite) { - term->InputRead ++; - if( term->InputRead >= MAX_INPUT_CHARS32 ) - term->InputRead -= MAX_INPUT_CHARS32; - } - #endif - } - - VFS_MarkAvaliable(&term->Node, 1); -} - diff --git a/Kernel/drv/vterm_output.c b/Kernel/drv/vterm_output.c deleted file mode 100644 index 95a2bce8..00000000 --- a/Kernel/drv/vterm_output.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * drv/vterm_input.c - * - Virtual Terminal - Input code - */ -#include "vterm.h" -#include - -// === CODE === -/** - * \fn void VT_InitOutput() - * \brief Initialise Video Output - */ -void VT_InitOutput() -{ - giVT_OutputDevHandle = VFS_Open(gsVT_OutputDevice, VFS_OPENFLAG_WRITE); - if(giVT_OutputDevHandle == -1) { - Log_Warning("VTerm", "Oh F**k, I can't open the video device '%s'", gsVT_OutputDevice); - return ; - } - VT_SetResolution( giVT_RealWidth, giVT_RealHeight ); - VT_SetTerminal( 0 ); - VT_SetMode( VIDEO_BUFFMT_TEXT ); -} - -/** - * \brief Set video output buffer mode - */ -void VT_SetMode(int Mode) -{ - VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &Mode ); -} - -/** - * \fn void VT_int_ScrollFramebuffer( tVTerm *Term, int Count ) - * \note Scrolls the framebuffer down by \a Count text lines - */ -void VT_int_ScrollFramebuffer( tVTerm *Term, int Count ) -{ - int tmp; - struct { - Uint8 Op; - Uint16 DstX, DstY; - Uint16 SrcX, SrcY; - Uint16 W, H; - } PACKED buf; - - // Only update if this is the current terminal - if( Term != gpVT_CurTerm ) return; - - if( Count > Term->ScrollHeight ) Count = Term->ScrollHeight; - if( Count < -Term->ScrollHeight ) Count = -Term->ScrollHeight; - - // Switch to 2D Command Stream - tmp = VIDEO_BUFFMT_2DSTREAM; - VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp); - - // BLIT to 0,0 from 0,giVT_CharHeight - buf.Op = VIDEO_2DOP_BLIT; - buf.SrcX = 0; buf.DstX = 0; - // TODO: Don't assume character dimensions - buf.W = Term->TextWidth * giVT_CharWidth; - if( Count > 0 ) - { - buf.SrcY = (Term->ScrollTop+Count) * giVT_CharHeight; - buf.DstY = Term->ScrollTop * giVT_CharHeight; - } - else // Scroll up, move text down - { - Count = -Count; - buf.SrcY = Term->ScrollTop * giVT_CharHeight; - buf.DstY = (Term->ScrollTop+Count) * giVT_CharHeight; - } - buf.H = (Term->ScrollHeight-Count) * giVT_CharHeight; - VFS_WriteAt(giVT_OutputDevHandle, 0, sizeof(buf), &buf); - - // Restore old mode (this function is only called during text mode) - tmp = VIDEO_BUFFMT_TEXT; - VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp); -} - -void VT_int_UpdateCursor( tVTerm *Term, int bShow ) -{ - tVideo_IOCtl_Pos csr_pos; - - if( Term != gpVT_CurTerm ) return ; - - if( !bShow ) - { - csr_pos.x = -1; - csr_pos.y = -1; - } - else if( Term->Mode == TERM_MODE_TEXT ) - { - int offset; - -// if( !(Term->Flags & VT_FLAG_SHOWCSR) -// && ( (Term->Flags & VT_FLAG_HIDECSR) || !Term->Node.ReadThreads) -// ) - if( !Term->Text || Term->Flags & VT_FLAG_HIDECSR ) - { - csr_pos.x = -1; - csr_pos.y = -1; - } - else - { - if(Term->Flags & VT_FLAG_ALTBUF) - offset = Term->AltWritePos; - else - offset = Term->WritePos - Term->ViewPos; - - csr_pos.x = offset % Term->TextWidth; - csr_pos.y = offset / Term->TextWidth; - if( 0 > csr_pos.y || csr_pos.y >= Term->TextHeight ) - csr_pos.y = -1, csr_pos.x = -1; - } - } - else - { - csr_pos.x = Term->VideoCursorX; - csr_pos.y = Term->VideoCursorY; - } - VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &csr_pos); -} - -/** - * \fn void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) - * \brief Updates the video framebuffer - */ -void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) -{ - tVT_Char *buffer; - int view_pos, write_pos; - // Only update if this is the current terminal - if( Term != gpVT_CurTerm ) return; - - switch( Term->Mode ) - { - case TERM_MODE_TEXT: - view_pos = (Term->Flags & VT_FLAG_ALTBUF) ? 0 : Term->ViewPos; - write_pos = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltWritePos : Term->WritePos; - buffer = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text; - // Re copy the entire screen? - if(UpdateAll) { - VFS_WriteAt( - giVT_OutputDevHandle, - 0, - Term->TextWidth*Term->TextHeight*sizeof(tVT_Char), - &buffer[view_pos] - ); - } - // Only copy the current line - else { - int ofs = write_pos - write_pos % Term->TextWidth; - VFS_WriteAt( - giVT_OutputDevHandle, - (ofs - view_pos)*sizeof(tVT_Char), - Term->TextWidth*sizeof(tVT_Char), - &buffer[ofs] - ); - } - break; - case TERM_MODE_FB: - break; - } - - VT_int_UpdateCursor(Term, 1); -} - diff --git a/Kernel/drv/vterm_termbuf.c b/Kernel/drv/vterm_termbuf.c deleted file mode 100644 index 2511048c..00000000 --- a/Kernel/drv/vterm_termbuf.c +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * drv/vterm_termbuf.c - * - Virtual Terminal - Terminal buffer manipulation - */ -#include "vterm.h" - -// === CODE === - -/** - * \fn void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count) - * \brief Print a string to the Virtual Terminal - */ -void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count) -{ - Uint32 val; - int i; - - // Iterate - for( i = 0; i < Count; i++ ) - { - // Handle escape sequences - if( Buffer[i] == 0x1B ) - { - i ++; - i += VT_int_ParseEscape(Term, (const char*)&Buffer[i]) - 1; - continue; - } - - // Fast check for non UTF-8 - if( Buffer[i] < 128 ) // Plain ASCII - VT_int_PutChar(Term, Buffer[i]); - else { // UTF-8 - i += ReadUTF8(&Buffer[i], &val) - 1; - VT_int_PutChar(Term, val); - } - } - // Update Screen - VT_int_UpdateScreen( Term, 0 ); -} - -/** - * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch) - * \brief Write a single character to a VTerm - */ -void VT_int_PutChar(tVTerm *Term, Uint32 Ch) -{ - int i; - tVT_Char *buffer; - int write_pos; - - if(Term->Flags & VT_FLAG_ALTBUF) { - buffer = Term->AltBuf; - write_pos = Term->AltWritePos; - } - else { - buffer = Term->Text; - write_pos = Term->WritePos; - } - - switch(Ch) - { - case '\0': return; // Ignore NULL byte - case '\n': - VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining - write_pos += Term->TextWidth; - case '\r': - write_pos -= write_pos % Term->TextWidth; - break; - - case '\t': { int tmp = write_pos / Term->TextWidth; - write_pos %= Term->TextWidth; - do { - buffer[ write_pos ].Ch = '\0'; - buffer[ write_pos ].Colour = Term->CurColour; - write_pos ++; - } while(write_pos & 7); - write_pos += tmp * Term->TextWidth; - break; } - - case '\b': - // Backspace is invalid at Offset 0 - if(write_pos == 0) break; - - write_pos --; - // Singe Character - if(buffer[ write_pos ].Ch != '\0') { - buffer[ write_pos ].Ch = 0; - buffer[ write_pos ].Colour = Term->CurColour; - break; - } - // Tab - i = 7; // Limit it to 8 - do { - buffer[ write_pos ].Ch = 0; - buffer[ write_pos ].Colour = Term->CurColour; - write_pos --; - } while(write_pos && i-- && buffer[ write_pos ].Ch == '\0'); - if(buffer[ write_pos ].Ch != '\0') - write_pos ++; - break; - - default: - buffer[ write_pos ].Ch = Ch; - buffer[ write_pos ].Colour = Term->CurColour; - // Update the line before wrapping - if( (write_pos + 1) % Term->TextWidth == 0 ) - VT_int_UpdateScreen( Term, 0 ); - write_pos ++; - break; - } - - if(Term->Flags & VT_FLAG_ALTBUF) - { - Term->AltBuf = buffer; - Term->AltWritePos = write_pos; - - if(Term->AltWritePos >= Term->TextWidth*Term->TextHeight) - { - Term->AltWritePos -= Term->TextWidth; - VT_int_ScrollText(Term, 1); - } - - } - else - { - Term->Text = buffer; - Term->WritePos = write_pos; - // Move Screen - // - Check if we need to scroll the entire scrollback buffer - if(Term->WritePos >= Term->TextWidth*Term->TextHeight*(giVT_Scrollback+1)) - { - int base; - - // Update previous line - Term->WritePos -= Term->TextWidth; - VT_int_UpdateScreen( Term, 0 ); - - // Update view position - base = Term->TextWidth*Term->TextHeight*(giVT_Scrollback); - if(Term->ViewPos < base) - Term->ViewPos += Term->Width; - if(Term->ViewPos > base) - Term->ViewPos = base; - - VT_int_ScrollText(Term, 1); - } - // Ok, so we only need to scroll the screen - else if(Term->WritePos >= Term->ViewPos + Term->TextWidth*Term->TextHeight) - { - // Update the last line - Term->WritePos -= Term->TextWidth; - VT_int_UpdateScreen( Term, 0 ); - Term->WritePos += Term->TextWidth; - - VT_int_ScrollText(Term, 1); - - Term->ViewPos += Term->TextWidth; - } - } - - //LEAVE('-'); -} - -void VT_int_ScrollText(tVTerm *Term, int Count) -{ - tVT_Char *buf; - int height, init_write_pos; - int len, i; - int scroll_top, scroll_height; - - // Get buffer pointer and attributes - if( Term->Flags & VT_FLAG_ALTBUF ) - { - buf = Term->AltBuf; - height = Term->TextHeight; - init_write_pos = Term->AltWritePos; - scroll_top = Term->ScrollTop; - scroll_height = Term->ScrollHeight; - } - else - { - buf = Term->Text; - height = Term->TextHeight*(giVT_Scrollback+1); - init_write_pos = Term->WritePos; - scroll_top = 0; - scroll_height = height; - } - - // Scroll text downwards - if( Count > 0 ) - { - int base; - - // Set up - if(Count > scroll_height) Count = scroll_height; - base = Term->TextWidth*(scroll_top + scroll_height - Count); - len = Term->TextWidth*(scroll_height - Count); - - // Scroll terminal cache - memmove( - &buf[Term->TextWidth*scroll_top], - &buf[Term->TextWidth*(scroll_top+Count)], - len*sizeof(tVT_Char) - ); - // Clear last rows - for( i = 0; i < Term->TextWidth*Count; i ++ ) - { - buf[ base + i ].Ch = 0; - buf[ base + i ].Colour = Term->CurColour; - } - - // Update Screen - VT_int_ScrollFramebuffer( Term, Count ); - if( Term->Flags & VT_FLAG_ALTBUF ) - Term->AltWritePos = base; - else - Term->WritePos = Term->ViewPos + Term->TextWidth*(Term->TextHeight - Count); - for( i = 0; i < Count; i ++ ) - { - VT_int_UpdateScreen( Term, 0 ); - if( Term->Flags & VT_FLAG_ALTBUF ) - Term->AltWritePos += Term->TextWidth; - else - Term->WritePos += Term->TextWidth; - } - } - else - { - Count = -Count; - if(Count > scroll_height) Count = scroll_height; - - len = Term->TextWidth*(scroll_height - Count); - - // Scroll terminal cache - memmove( - &buf[Term->TextWidth*(scroll_top+Count)], - &buf[Term->TextWidth*scroll_top], - len*sizeof(tVT_Char) - ); - // Clear preceding rows - for( i = 0; i < Term->TextWidth*Count; i ++ ) - { - buf[ i ].Ch = 0; - buf[ i ].Colour = Term->CurColour; - } - - VT_int_ScrollFramebuffer( Term, -Count ); - if( Term->Flags & VT_FLAG_ALTBUF ) - Term->AltWritePos = Term->TextWidth*scroll_top; - else - Term->WritePos = Term->ViewPos; - for( i = 0; i < Count; i ++ ) - { - VT_int_UpdateScreen( Term, 0 ); - if( Term->Flags & VT_FLAG_ALTBUF ) - Term->AltWritePos += Term->TextWidth; - else - Term->WritePos += Term->TextWidth; - } - } - - if( Term->Flags & VT_FLAG_ALTBUF ) - Term->AltWritePos = init_write_pos; - else - Term->WritePos = init_write_pos; -} - -/** - * \brief Clears a line in a virtual terminal - * \param Term Terminal to modify - * \param Num Line number to clear - */ -void VT_int_ClearLine(tVTerm *Term, int Num) -{ - int i; - tVT_Char *cell; - - if( Num < 0 || Num >= Term->TextHeight * (giVT_Scrollback + 1) ) return ; - - cell = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text; - cell = &cell[ Num*Term->TextWidth ]; - - for( i = Term->TextWidth; i--; ) - { - cell[ i ].Ch = 0; - cell[ i ].Colour = Term->CurColour; - } -} - -/** - * \brief Update the screen mode - * \param Term Terminal to update - * \param NewMode New mode to set - * \param NewWidth New framebuffer width - * \param NewHeight New framebuffer height - */ -void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight) -{ - int oldW = Term->Width; - int oldTW = Term->TextWidth; - int oldH = Term->Height; - int oldTH = Term->TextHeight; - tVT_Char *oldTBuf = Term->Text; - Uint32 *oldFB = Term->Buffer; - int w, h, i; - - // TODO: Increase RealWidth/RealHeight when this happens - if(NewWidth > giVT_RealWidth) NewWidth = giVT_RealWidth; - if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight; - - Term->Mode = NewMode; - - // Fast exit if no resolution change - if(NewWidth == Term->Width && NewHeight == Term->Height) - return ; - - // Calculate new dimensions - Term->Width = NewWidth; - Term->Height = NewHeight; - Term->TextWidth = NewWidth / giVT_CharWidth; - Term->TextHeight = NewHeight / giVT_CharHeight; - Term->ScrollHeight = Term->TextHeight - (oldTH - Term->ScrollHeight) - Term->ScrollTop; - - // Allocate new buffers - // - Text - Term->Text = calloc( - Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1), - sizeof(tVT_Char) - ); - if(oldTBuf) { - // Copy old buffer - w = (oldTW > Term->TextWidth) ? Term->TextWidth : oldTW; - h = (oldTH > Term->TextHeight) ? Term->TextHeight : oldTH; - h *= giVT_Scrollback + 1; - for( i = 0; i < h; i ++ ) - { - memcpy( - &Term->Text[i*Term->TextWidth], - &oldTBuf[i*oldTW], - w*sizeof(tVT_Char) - ); - } - free(oldTBuf); - } - - // - Alternate Text - Term->AltBuf = realloc( - Term->AltBuf, - Term->TextWidth * Term->TextHeight * sizeof(tVT_Char) - ); - - // - Framebuffer - if(oldFB) { - Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) ); - // Copy old buffer - w = (oldW > Term->Width) ? Term->Width : oldW; - h = (oldH > Term->Height) ? Term->Height : oldH; - for( i = 0; i < h; i ++ ) - { - memcpy( - &Term->Buffer[i*Term->Width], - &oldFB[i*oldW], - w*sizeof(Uint32) - ); - } - free(oldFB); - } - - // Debug - switch(NewMode) - { - case TERM_MODE_TEXT: - Log_Log("VTerm", "Set VT %p to text mode (%ix%i)", - Term, Term->TextWidth, Term->TextHeight); - break; - case TERM_MODE_FB: - Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)", - Term, Term->Width, Term->Height); - break; - //case TERM_MODE_2DACCEL: - //case TERM_MODE_3DACCEL: - // return; - } -} - - -void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled) -{ - if(Enabled) - Term->Flags |= VT_FLAG_ALTBUF; - else - Term->Flags &= ~VT_FLAG_ALTBUF; - VT_int_UpdateScreen(Term, 1); -} - diff --git a/Kernel/drv/vterm_vt100.c b/Kernel/drv/vterm_vt100.c deleted file mode 100644 index fb152a30..00000000 --- a/Kernel/drv/vterm_vt100.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * drv/vterm_vt100.c - * - Virtual Terminal - VT100 (Kinda) Emulation - */ -#include "vterm.h" - -// === CONSTANTS === -const Uint16 caVT100Colours[] = { - // Black, Red, Green, Yellow, Blue, Purple, Cyan, Gray - // Same again, but bright - VT_COL_BLACK, 0x700, 0x070, 0x770, 0x007, 0x707, 0x077, 0xAAA, - VT_COL_GREY, 0xF00, 0x0F0, 0xFF0, 0x00F, 0xF0F, 0x0FF, VT_COL_WHITE - }; - -// === CODE === -/** - * \brief Handle a standard large escape code - * - * Handles any escape code of the form \x1B[n,...A where n is an integer - * and A is any letter. - */ -void VT_int_ParseEscape_StandardLarge(tVTerm *Term, char CmdChar, int argc, int *args) -{ - int tmp = 1; - switch(CmdChar) - { - // Left - case 'D': - tmp = -1; - // Right - case 'C': - if(argc == 1) tmp *= args[0]; - if( Term->Flags & VT_FLAG_ALTBUF ) - { - if( (Term->AltWritePos + tmp) % Term->TextWidth == 0 ) { - Term->AltWritePos -= Term->AltWritePos % Term->TextWidth; - Term->AltWritePos += Term->TextWidth - 1; - } - else - Term->AltWritePos += tmp; - } - else - { - if( (Term->WritePos + tmp) % Term->TextWidth == 0 ) { - Term->WritePos -= Term->WritePos % Term->TextWidth; - Term->WritePos += Term->TextWidth - 1; - } - else - Term->WritePos += tmp; - } - break; - - // Erase - case 'J': - switch(args[0]) - { - case 0: // Erase below - break; - case 1: // Erase above - break; - case 2: // Erase all - if( Term->Flags & VT_FLAG_ALTBUF ) - { - int i = Term->TextHeight; - while( i-- ) VT_int_ClearLine(Term, i); - Term->AltWritePos = 0; - VT_int_UpdateScreen(Term, 1); - } - else - { - int i = Term->TextHeight * (giVT_Scrollback + 1); - while( i-- ) VT_int_ClearLine(Term, i); - Term->WritePos = 0; - Term->ViewPos = 0; - VT_int_UpdateScreen(Term, 1); - } - break; - } - break; - - // Erase in line - case 'K': - switch(args[0]) - { - case 0: // Erase to right - if( Term->Flags & VT_FLAG_ALTBUF ) - { - int i, max; - max = Term->Width - Term->AltWritePos % Term->Width; - for( i = 0; i < max; i ++ ) - Term->AltBuf[Term->AltWritePos+i].Ch = 0; - } - else - { - int i, max; - max = Term->Width - Term->WritePos % Term->Width; - for( i = 0; i < max; i ++ ) - Term->Text[Term->WritePos+i].Ch = 0; - } - VT_int_UpdateScreen(Term, 0); - break; - case 1: // Erase to left - if( Term->Flags & VT_FLAG_ALTBUF ) - { - int i = Term->AltWritePos % Term->Width; - while( i -- ) - Term->AltBuf[Term->AltWritePos++].Ch = 0; - } - else - { - int i = Term->WritePos % Term->Width; - while( i -- ) - Term->Text[Term->WritePos++].Ch = 0; - } - VT_int_UpdateScreen(Term, 0); - break; - case 2: // Erase all - if( Term->Flags & VT_FLAG_ALTBUF ) - { - VT_int_ClearLine(Term, Term->AltWritePos / Term->Width); - } - else - { - VT_int_ClearLine(Term, Term->WritePos / Term->Width); - } - VT_int_UpdateScreen(Term, 0); - break; - } - break; - - // Set cursor position - case 'H': - if( Term->Flags & VT_FLAG_ALTBUF ) - Term->AltWritePos = args[0] + args[1]*Term->TextWidth; - else - Term->WritePos = args[0] + args[1]*Term->TextWidth; - //Log_Debug("VTerm", "args = {%i, %i}", args[0], args[1]); - break; - - // Scroll up `n` lines - case 'S': - tmp = -1; - // Scroll down `n` lines - case 'T': - if(argc == 1) tmp *= args[0]; - if( Term->Flags & VT_FLAG_ALTBUF ) - VT_int_ScrollText(Term, tmp); - else - { - if(Term->ViewPos/Term->TextWidth + tmp < 0) - break; - if(Term->ViewPos/Term->TextWidth + tmp > Term->TextHeight * (giVT_Scrollback + 1)) - break; - - Term->ViewPos += Term->TextWidth*tmp; - } - break; - - // Set Font flags - case 'm': - for( ; argc--; ) - { - int colour_idx; - // Flags - if( 0 <= args[argc] && args[argc] <= 8) - { - switch(args[argc]) - { - case 0: Term->CurColour = DEFAULT_COLOUR; break; // Reset - case 1: Term->CurColour |= 0x80000000; break; // Bright - case 2: Term->CurColour &= ~0x80000000; break; // Dim - } - } - // Foreground Colour - else if(30 <= args[argc] && args[argc] <= 37) { - // Get colour index, accounting for bright bit - colour_idx = args[argc]-30 + ((Term->CurColour>>28) & 8); - Term->CurColour &= 0x8000FFFF; - Term->CurColour |= (Uint32)caVT100Colours[ colour_idx ] << 16; - } - // Background Colour - else if(40 <= args[argc] && args[argc] <= 47) { - // Get colour index, accounting for bright bit - colour_idx = args[argc]-40 + ((Term->CurColour>>12) & 8); - Term->CurColour &= 0xFFFF8000; - Term->CurColour |= caVT100Colours[ colour_idx ]; - } - else { - Log_Warning("VTerm", "Unknown font flag %i", args[argc]); - } - } - break; - - // Set scrolling region - case 'r': - if( argc != 2 ) break; - Term->ScrollTop = args[0]; - Term->ScrollHeight = args[1] - args[0]; - break; - - default: - Log_Warning("VTerm", "Unknown control sequence '\\x1B[%c'", CmdChar); - break; - } -} - -/** - * \fn int VT_int_ParseEscape(tVTerm *Term, const char *Buffer) - * \brief Parses a VT100 Escape code - */ -int VT_int_ParseEscape(tVTerm *Term, const char *Buffer) -{ - char c; - int argc = 0, j = 1; - int args[6] = {0,0,0,0}; - int bQuestionMark = 0; - - switch(Buffer[0]) - { - //Large Code - case '[': - // Get Arguments - c = Buffer[j++]; - if(c == '?') { - bQuestionMark = 1; - c = Buffer[j++]; - } - if( '0' <= c && c <= '9' ) - { - do { - if(c == ';') c = Buffer[j++]; - while('0' <= c && c <= '9') { - args[argc] *= 10; - args[argc] += c-'0'; - c = Buffer[j++]; - } - argc ++; - } while(c == ';'); - } - - // Get Command - if( ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) - { - if( bQuestionMark ) - { - switch(c) - { - // DEC Private Mode Set - case 'h': - if(argc != 1) break; - switch(args[0]) - { - case 25: - Term->Flags &= ~VT_FLAG_HIDECSR; - break; - case 1047: - VT_int_ToggleAltBuffer(Term, 1); - break; - } - break; - case 'l': - if(argc != 1) break; - switch(args[0]) - { - case 25: - Term->Flags |= VT_FLAG_HIDECSR; - break; - case 1047: - VT_int_ToggleAltBuffer(Term, 0); - break; - } - break; - default: - Log_Warning("VTerm", "Unknown control sequence '\\x1B[?%c'", c); - break; - } - } - else - { - VT_int_ParseEscape_StandardLarge(Term, c, argc, args); - } - } - break; - - default: - Log_Notice("VTerm", "TODO: Handle short escape codes"); - break; - } - - //Log_Debug("VTerm", "j = %i, Buffer = '%s'", j, Buffer); - return j; -} diff --git a/Kernel/drvutil.c b/Kernel/drvutil.c deleted file mode 100644 index d19a0504..00000000 --- a/Kernel/drvutil.c +++ /dev/null @@ -1,714 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge - * - * drvutil.c - * - Common Driver/Filesystem Helper Functions - */ -#define DEBUG 0 -#include -#include -#include - -// === TYPES === - -// === PROTOTYPES === -//int DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers); -//size_t DrvUtil_Video_WriteLFB(int Mode, tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, void *Src); -//void DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap); -//void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y); -void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf); -//void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf); -void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour); -void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H); - -// === GLOBALS === -tDrvUtil_Video_2DHandlers gDrvUtil_Stub_2DFunctions = { - NULL, - DrvUtil_Video_2D_Fill, - DrvUtil_Video_2D_Blit -}; -tVideo_IOCtl_Bitmap gDrvUtil_TextModeCursor = { - 8, 16, - 0, 0, - { - 0, 0, 0 , 0, 0, 0, 0, 0, - 0,-1, 0xFF000000, 0, 0, 0, 0, 0, - 0,-1, 0xFF000000, 0, 0, 0, 0, 0, - 0,-1, 0xFF000000, 0, 0, 0, 0, 0, - 0,-1, 0xFF000000, 0, 0, 0, 0, 0, - 0,-1, 0xFF000000, 0, 0, 0, 0, 0, - 0,-1, 0xFF000000, 0, 0, 0, 0, 0, - 0,-1, 0xFF000000, 0, 0, 0, 0, 0, - 0,-1, 0xFF000000, 0, 0, 0, 0, 0, - 0,-1, 0xFF000000, 0, 0, 0, 0, 0, - 0,-1, 0xFF000000, 0, 0, 0, 0, 0, - 0,-1, 0xFF000000, 0, 0, 0, 0, 0, - 0,-1, 0xFF000000, 0, 0, 0, 0, 0, - 0,-1, 0xFF000000, 0, 0, 0, 0, 0, - 0, 0, 0 , 0, 0, 0, 0, 0, - 0, 0, 0 , 0, 0, 0, 0, 0 - } -}; - -// === CODE === -// --- Video Driver Helpers --- -int DrvUtil_Video_2DStream(void *Ent, const void *Buffer, int Length, - tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers) -{ - const Uint8 *stream = Buffer; - int rem = Length; - int op; - while( rem ) - { - rem --; - op = *stream; - stream ++; - - if(op > NUM_VIDEO_2DOPS) { - Log_Warning("DrvUtil", - "DrvUtil_Video_2DStream: Unknown operation %i", - op); - return Length-rem; - } - - if(op*sizeof(void*) > SizeofHandlers) { - Log_Warning("DrvUtil", - "DrvUtil_Video_2DStream: Driver does not support op %i", - op); - return Length-rem; - } - - switch(op) - { - case VIDEO_2DOP_NOP: break; - - case VIDEO_2DOP_FILL: - if(rem < 10) return Length-rem; - - if(!Handlers->Fill) { - Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver" - " does not support VIDEO_2DOP_FILL"); - return Length-rem; - } - - Handlers->Fill( - Ent, - ((const Uint16*)stream)[0], ((const Uint16*)stream)[1], - ((const Uint16*)stream)[2], ((const Uint16*)stream)[3], - ((const Uint32*)stream)[4] - ); - - rem -= 10; - stream += 10; - break; - - case VIDEO_2DOP_BLIT: - if(rem < 12) return Length-rem; - - if(!Handlers->Blit) { - Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver" - " does not support VIDEO_2DOP_BLIT"); - return Length-rem; - } - - Handlers->Blit( - Ent, - ((const Uint16*)stream)[0], ((const Uint16*)stream)[1], - ((const Uint16*)stream)[2], ((const Uint16*)stream)[3], - ((const Uint16*)stream)[4], ((const Uint16*)stream)[5] - ); - - rem -= 12; - stream += 12; - break; - - } - } - return 0; -} - -int DrvUtil_Video_WriteLFB(tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, const void *Buffer) -{ - Uint8 *dest; - const Uint32 *src = Buffer; - int csr_x, csr_y; - int x, y; - int bytes_per_px = (FBInfo->Depth + 7) / 8; - ENTER("pFBInfo xOffset xLength pBuffer", - Mode, FBInfo, Offset, Length, Buffer); - - csr_x = FBInfo->CursorX; - csr_y = FBInfo->CursorY; - - DrvUtil_Video_RemoveCursor(FBInfo); - - switch( FBInfo->BufferFormat ) - { - case VIDEO_BUFFMT_TEXT: - { - const tVT_Char *chars = Buffer; - int widthInChars = FBInfo->Width/giVT_CharWidth; - int heightInChars = FBInfo->Height/giVT_CharHeight; - int i; - - LOG("bytes_per_px = %i", bytes_per_px); - LOG("widthInChars = %i, heightInChars = %i", widthInChars, heightInChars); - - Length /= sizeof(tVT_Char); Offset /= sizeof(tVT_Char); - - x = Offset % widthInChars; y = Offset / widthInChars; - LOG("x = %i, y = %i", x, y); - - // Sanity Check - if(Offset > heightInChars * widthInChars) LEAVE_RET('i', 0); - if(y >= heightInChars) LEAVE_RET('i', 0); - - if( Offset + Length > heightInChars*widthInChars ) - { - Length = heightInChars*widthInChars - Offset; - } - - dest = FBInfo->Framebuffer; - LOG("dest = %p", dest); - dest += y * giVT_CharHeight * FBInfo->Pitch; - LOG("dest = %p", dest); - - for( i = 0; i < Length; i++ ) - { - if( y >= heightInChars ) - { - Log_Notice("DrvUtil", "Stopped at %i", i); - break; - } - - VT_Font_Render( - chars->Ch, - dest + x*giVT_CharWidth*bytes_per_px, FBInfo->Depth, FBInfo->Pitch, - VT_Colour12toN(chars->BGCol, FBInfo->Depth), - VT_Colour12toN(chars->FGCol, FBInfo->Depth) - ); - - chars ++; - x ++; - if( x >= widthInChars ) - { - x = 0; - y ++; - dest += FBInfo->Pitch*giVT_CharHeight; - LOG("dest = %p", dest); - } - } - Length = i * sizeof(tVT_Char); - } - break; - - case VIDEO_BUFFMT_FRAMEBUFFER: - if(FBInfo->Width*FBInfo->Height*4 < Offset+Length) - { - Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Framebuffer Overflow"); - return 0; - } - - switch(FBInfo->Depth) - { - case 15: - case 16: - Log_Warning("DrvUtil", "TODO: Support 15/16 bpp modes in LFB write"); - break; - case 24: - x = Offset % FBInfo->Width; - y = Offset / FBInfo->Width; - dest = (Uint8*)FBInfo->Framebuffer + y*FBInfo->Pitch; - for( ; Length >= 4; Length -= 4 ) - { - dest[x*3+0] = *src & 0xFF; - dest[x*3+1] = (*src >> 8) & 0xFF; - dest[x*3+2] = (*src >> 16) & 0xFF; - x ++; - if(x == FBInfo->Width) { - dest += FBInfo->Pitch; - x = 0; - } - } - break; - case 32: - // Copy to Frambuffer - if( FBInfo->Pitch != FBInfo->Width*4 ) - { - Uint32 *px; - // Pitch isn't 4*Width - x = Offset % FBInfo->Width; - y = Offset / FBInfo->Height; - - px = (Uint32*)FBInfo->Framebuffer + y*FBInfo->Pitch/4; - - for( ; Length >= 4; Length -= 4, x ) - { - px[x++] = *src ++; - if( x == FBInfo->Width ) { - x = 0; - px += FBInfo->Pitch; - } - } - } - else - { - dest = (Uint8 *)FBInfo->Framebuffer + Offset; - memcpy(dest, Buffer, Length); - } - break; - default: - Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Unknown bit depthn %i", FBInfo->Depth); - break; - } - break; - - case VIDEO_BUFFMT_2DSTREAM: - Length = DrvUtil_Video_2DStream( - FBInfo, Buffer, Length, - &gDrvUtil_Stub_2DFunctions, sizeof(gDrvUtil_Stub_2DFunctions) - ); - break; - - default: - LEAVE('i', -1); - return -1; - } - - DrvUtil_Video_DrawCursor(FBInfo, csr_x, csr_y); - - LEAVE('x', Length); - return Length; -} - -int DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap) -{ - int csrX = Buf->CursorX, csrY = Buf->CursorY; - size_t size; - - // Clear old bitmap - if( Buf->CursorBitmap ) - { - DrvUtil_Video_RemoveCursor(Buf); - if( !Bitmap || Bitmap->W != Buf->CursorBitmap->W || Bitmap->H != Buf->CursorBitmap->H ) - { - free( Buf->CursorSaveBuf ); - Buf->CursorSaveBuf = NULL; - } - if( Buf->CursorBitmap != &gDrvUtil_TextModeCursor) - free(Buf->CursorBitmap); - Buf->CursorBitmap = NULL; - } - - // If the new bitmap is null, disable drawing - if( !Bitmap ) - { - Buf->CursorX = -1; - Buf->CursorY = -1; - return 0; - } - - // Sanity check the bitmap - if( !CheckMem(Bitmap, sizeof(*Bitmap)) || !CheckMem(Bitmap->Data, Bitmap->W*Bitmap->H*sizeof(Uint32)) ) - { - Log_Warning("DrvUtil", "DrvUtil_Video_SetCursor: Bitmap (%p) is in invalid memory", Bitmap); - errno = -EINVAL; - return -1; - } - - // Don't take a copy of the DrvUtil provided cursor - if( Bitmap == &gDrvUtil_TextModeCursor ) - { - Buf->CursorBitmap = Bitmap; - } - else - { - size = sizeof(tVideo_IOCtl_Bitmap) + Bitmap->W*Bitmap->H*4; - - // Take a copy - Buf->CursorBitmap = malloc( size ); - memcpy(Buf->CursorBitmap, Bitmap, size); - } - - // Restore cursor position - DrvUtil_Video_DrawCursor(Buf, csrX, csrY); - return 0; -} - -void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y) -{ - int render_ox=0, render_oy=0, render_w, render_h; - - DrvUtil_Video_RemoveCursor(Buf); - - // X < 0 disables the cursor - if( X < 0 ) { - Buf->CursorX = -1; - return ; - } - - // Sanity checking - if( X < 0 || Y < 0 ) return; - if( X >= Buf->Width || Y >= Buf->Height ) return; - - // Ensure the cursor is enabled - if( !Buf->CursorBitmap ) return ; - - // Save cursor position (for changing the bitmap) - Buf->CursorX = X; Buf->CursorY = Y; - - // Apply cursor's center offset - X -= Buf->CursorBitmap->XOfs; - Y -= Buf->CursorBitmap->YOfs; - - // Get the width of the cursor on screen (clipping to right/bottom edges) - render_w = X > Buf->Width - Buf->CursorBitmap->W ? Buf->Width - X : Buf->CursorBitmap->W; - render_h = Y > Buf->Height - Buf->CursorBitmap->H ? Buf->Height - Y : Buf->CursorBitmap->H; - - // Clipp to left/top edges - if(X < 0) { render_ox = -X; X = 0; } - if(Y < 0) { render_oy = -Y; Y = 0; } - - // Save values - Buf->CursorRenderW = render_w; Buf->CursorRenderH = render_h; - Buf->CursorDestX = X; Buf->CursorDestY = Y; - Buf->CursorReadX = render_ox; Buf->CursorReadY = render_oy; - - // Call render routine - DrvUtil_Video_RenderCursor(Buf); -} - -void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf) -{ - int src_x = Buf->CursorReadX, src_y = Buf->CursorReadY; - int render_w = Buf->CursorRenderW, render_h = Buf->CursorRenderH; - int dest_x = Buf->CursorDestX, dest_y = Buf->CursorDestY; - int bytes_per_px = (Buf->Depth + 7) / 8; - int save_pitch = Buf->CursorBitmap->W * bytes_per_px; - void *dest; - Uint32 *src; - int x, y; - - dest = (Uint8*)Buf->Framebuffer + dest_y*Buf->Pitch + dest_x*bytes_per_px; - src = Buf->CursorBitmap->Data + src_y * Buf->CursorBitmap->W + src_x; - - // Allocate save buffer if not already - if( !Buf->CursorSaveBuf ) - Buf->CursorSaveBuf = malloc( Buf->CursorBitmap->W*Buf->CursorBitmap->H*bytes_per_px ); - - // Save behind the cursor - for( y = 0; y < render_h; y ++ ) - memcpy( - (Uint8*)Buf->CursorSaveBuf + y*save_pitch, - (Uint8*)dest + y*Buf->Pitch, - render_w*bytes_per_px - ); - - // Draw the cursor - switch(Buf->Depth) - { - case 15: - case 16: - Log_Warning("DrvUtil", "TODO: Support 15/16 bpp modes in cursor draw"); - break; - case 24: - for( y = 0; y < render_h; y ++ ) - { - Uint8 *px; - px = dest; - for(x = 0; x < render_w; x ++, px += 3) - { - Uint32 value = src[x]; - // TODO: Should I implement alpha blending? - if(value & 0xFF000000) - { - px[0] = value & 0xFF; - px[1] = (value >> 8) & 0xFF; - px[2] = (value >> 16) & 0xFF; - } - else - ; - } - src += Buf->CursorBitmap->W; - dest = (Uint8*)dest + Buf->Pitch; - } - break; - case 32: - for( y = 0; y < render_h; y ++ ) - { - Uint32 *px; - px = dest; - for(x = 0; x < render_w; x ++, px ++) - { - Uint32 value = src[x]; - // TODO: Should I implement alpha blending? - if(value & 0xFF000000) - *px = value; - else - ; // NOP, completely transparent - } - src += Buf->CursorBitmap->W; - dest = (Uint8*)dest + Buf->Pitch; - } - break; - default: - Log_Error("DrvUtil", "RenderCursor - Unknown bit depth %i", Buf->Depth); - Buf->CursorX = -1; - break; - } -} - -void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf) -{ - int bytes_per_px = (Buf->Depth + 7) / 8; - int y, save_pitch; - Uint8 *dest, *src; - - // Just a little sanity - if( !Buf->CursorBitmap || Buf->CursorX == -1 ) return ; - if( !Buf->CursorSaveBuf ) return ; - -// Debug("DrvUtil_Video_RemoveCursor: (Buf=%p) dest_x=%i, dest_y=%i", Buf, Buf->CursorDestX, Buf->CursorDestY); - - // Set up - save_pitch = Buf->CursorBitmap->W * bytes_per_px; - dest = (Uint8*)Buf->Framebuffer + Buf->CursorDestY * Buf->Pitch + Buf->CursorDestX*bytes_per_px; - src = Buf->CursorSaveBuf; - - // Copy each line back - for( y = 0; y < Buf->CursorRenderH; y ++ ) - { - memcpy( dest, src, Buf->CursorRenderW * bytes_per_px ); - src += save_pitch; - dest += Buf->Pitch; - } - - // Set the cursor as removed - Buf->CursorX = -1; -} - -void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour) -{ - tDrvUtil_Video_BufInfo *FBInfo = Ent; - - // TODO: Handle non-32bit modes - if( FBInfo->Depth != 32 ) return; - - // TODO: Be less hacky - int pitch = FBInfo->Pitch/4; - Uint32 *buf = (Uint32*)FBInfo->Framebuffer + Y*pitch + X; - while( H -- ) { - Uint32 *tmp; - int i; - tmp = buf; - for(i=W;i--;tmp++) *tmp = Colour; - buf += pitch; - } -} - -void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H) -{ - tDrvUtil_Video_BufInfo *FBInfo = Ent; - int scrnpitch = FBInfo->Pitch; - int bytes_per_px = (FBInfo->Depth + 7) / 8; - int dst = DstY*scrnpitch + DstX; - int src = SrcY*scrnpitch + SrcX; - int tmp; - - //Log("Vesa_2D_Blit: (Ent=%p, DstX=%i, DstY=%i, SrcX=%i, SrcY=%i, W=%i, H=%i)", - // Ent, DstX, DstY, SrcX, SrcY, W, H); - - if(SrcX + W > FBInfo->Width) W = FBInfo->Width - SrcX; - if(DstX + W > FBInfo->Width) W = FBInfo->Width - DstX; - if(SrcY + H > FBInfo->Height) H = FBInfo->Height - SrcY; - if(DstY + H > FBInfo->Height) H = FBInfo->Height - DstY; - - //Debug("W = %i, H = %i", W, H); - - if( dst > src ) { - // Reverse copy - dst += H*scrnpitch; - src += H*scrnpitch; - while( H -- ) { - dst -= scrnpitch; - src -= scrnpitch; - tmp = W*bytes_per_px; - for( tmp = W; tmp --; ) { - *((Uint8*)FBInfo->Framebuffer + dst + tmp) = *((Uint8*)FBInfo->Framebuffer + src + tmp); - } - } - } - else if(W == FBInfo->Width && FBInfo->Pitch == FBInfo->Width*bytes_per_px) { - memmove((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, H*FBInfo->Pitch); - } - else { - // Normal copy is OK - while( H -- ) { - memcpy((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, W*bytes_per_px); - dst += scrnpitch; - src += scrnpitch; - } - } - //Log("Vesa_2D_Blit: RETURN"); -} - - -// --- Disk Driver Helpers --- -Uint64 DrvUtil_ReadBlock(Uint64 Start, Uint64 Length, void *Buffer, - tDrvUtil_Read_Callback ReadBlocks, Uint64 BlockSize, Uint Argument) -{ - Uint8 tmp[BlockSize]; // C99 - Uint64 block = Start / BlockSize; - int offset = Start - block * BlockSize; - int leading = BlockSize - offset; - Uint64 num; - int tailings; - Uint64 ret; - - ENTER("XStart XLength pBuffer pReadBlocks XBlockSize xArgument", - Start, Length, Buffer, ReadBlocks, BlockSize, Argument); - - // Non aligned start, let's fix that! - if(offset != 0) - { - if(leading > Length) leading = Length; - LOG("Reading %i bytes from Block1+%i", leading, offset); - ret = ReadBlocks(block, 1, tmp, Argument); - if(ret != 1) { - LEAVE('i', 0); - return 0; - } - memcpy( Buffer, &tmp[offset], leading ); - - if(leading == Length) { - LEAVE('i', leading); - return leading; - } - - Buffer = (Uint8*)Buffer + leading; - block ++; - num = ( Length - leading ) / BlockSize; - tailings = Length - num * BlockSize - leading; - } - else { - num = Length / BlockSize; - tailings = Length % BlockSize; - } - - // Read central blocks - if(num) - { - LOG("Reading %i blocks", num); - ret = ReadBlocks(block, num, Buffer, Argument); - if(ret != num ) { - LEAVE('X', leading + ret * BlockSize); - return leading + ret * BlockSize; - } - } - - // Read last tailing block - if(tailings != 0) - { - LOG("Reading %i bytes from last block", tailings); - block += num; - Buffer = (Uint8*)Buffer + num * BlockSize; - ret = ReadBlocks(block, 1, tmp, Argument); - if(ret != 1) { - LEAVE('X', leading + num * BlockSize); - return leading + num * BlockSize; - } - memcpy( Buffer, tmp, tailings ); - } - - LEAVE('X', Length); - return Length; -} - -Uint64 DrvUtil_WriteBlock(Uint64 Start, Uint64 Length, const void *Buffer, - tDrvUtil_Read_Callback ReadBlocks, tDrvUtil_Write_Callback WriteBlocks, - Uint64 BlockSize, Uint Argument) -{ - Uint8 tmp[BlockSize]; // C99 - Uint64 block = Start / BlockSize; - int offset = Start - block * BlockSize; - int leading = BlockSize - offset; - Uint64 num; - int tailings; - Uint64 ret; - - ENTER("XStart XLength pBuffer pReadBlocks pWriteBlocks XBlockSize xArgument", - Start, Length, Buffer, ReadBlocks, WriteBlocks, BlockSize, Argument); - - // Non aligned start, let's fix that! - if(offset != 0) - { - if(leading > Length) leading = Length; - LOG("Writing %i bytes to Block1+%i", leading, offset); - // Read a copy of the block - ret = ReadBlocks(block, 1, tmp, Argument); - if(ret != 1) { - LEAVE('i', 0); - return 0; - } - // Modify - memcpy( &tmp[offset], Buffer, leading ); - // Write Back - ret = WriteBlocks(block, 1, tmp, Argument); - if(ret != 1) { - LEAVE('i', 0); - return 0; - } - - if(leading == Length) { - LEAVE('i', leading); - return leading; - } - - Buffer = (Uint8*)Buffer + leading; - block ++; - num = ( Length - leading ) / BlockSize; - tailings = Length - num * BlockSize - leading; - } - else { - num = Length / BlockSize; - tailings = Length % BlockSize; - } - - // Read central blocks - if(num) - { - LOG("Writing %i blocks", num); - ret = WriteBlocks(block, num, Buffer, Argument); - if(ret != num ) { - LEAVE('X', leading + ret * BlockSize); - return leading + ret * BlockSize; - } - } - - // Read last tailing block - if(tailings != 0) - { - LOG("Writing %i bytes to last block", tailings); - block += num; - Buffer = (Uint8*)Buffer + num * BlockSize; - // Read - ret = ReadBlocks(block, 1, tmp, Argument); - if(ret != 1) { - LEAVE('X', leading + num * BlockSize); - return leading + num * BlockSize; - } - // Modify - memcpy( tmp, Buffer, tailings ); - // Write - ret = WriteBlocks(block, 1, tmp, Argument); - if(ret != 1) { - LEAVE('X', leading + num * BlockSize); - return leading + num * BlockSize; - } - - } - - LEAVE('X', Length); - return Length; -} diff --git a/Kernel/events.c b/Kernel/events.c deleted file mode 100644 index a9eab786..00000000 --- a/Kernel/events.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * events.c - * - Thread level event handling - */ -#define DEBUG 0 -#include -#include -#include - -// === CODE === -void Threads_PostEvent(tThread *Thread, Uint32 EventMask) -{ - // Sanity checking - if( !Thread ) return ; - if( EventMask == 0 ) return ; - // TODO: Check that only one bit is set? - - ENTER("pThread xEventMask", Thread, EventMask); - - SHORTLOCK( &Thread->IsLocked ); - - Thread->EventState |= EventMask; - LOG("Thread->EventState = 0x%x", Thread->EventState); - - // Currently sleeping on an event? - if( Thread->Status == THREAD_STAT_EVENTSLEEP ) - { - // Waiting on this event? - if( (Uint32)Thread->RetStatus & EventMask ) - { - // Wake up - LOG("Waking thread %p(%i %s)", Thread, Thread->TID, Thread->ThreadName); - Threads_AddActive(Thread); - } - } - - SHORTREL( &Thread->IsLocked ); - LEAVE('-'); -} - -/** - * \brief Wait for an event to occur - */ -Uint32 Threads_WaitEvents(Uint32 EventMask) -{ - Uint32 rv; - tThread *us = Proc_GetCurThread(); - - ENTER("xEventMask", EventMask); - - // Early return check - if( EventMask == 0 ) - { - LEAVE('i', 0); - return 0; - } - - LOG("us = %p(%i %s)", us, us->TID, us->ThreadName); - - // Check if a wait is needed - SHORTLOCK( &us->IsLocked ); - while( !(us->EventState & EventMask) ) - { - LOG("Locked and preparing for wait"); - // Wait - us->RetStatus = EventMask; // HACK: Store EventMask in RetStatus - SHORTLOCK( &glThreadListLock ); - us = Threads_RemActive(); - us->Status = THREAD_STAT_EVENTSLEEP; - // Note stored anywhere because we're woken using other means - SHORTREL( &glThreadListLock ); - SHORTREL( &us->IsLocked ); - while(us->Status == THREAD_STAT_EVENTSLEEP) Threads_Yield(); - // Woken when lock is acquired - SHORTLOCK( &us->IsLocked ); - } - - // Get return value and clear changed event bits - rv = us->EventState & EventMask; - us->EventState &= ~EventMask; - - SHORTREL( &us->IsLocked ); - - LEAVE('x', rv); - return rv; -} - diff --git a/Kernel/heap.c b/Kernel/heap.c deleted file mode 100644 index eefd13cf..00000000 --- a/Kernel/heap.c +++ /dev/null @@ -1,753 +0,0 @@ -/* - * AcessOS Microkernel Version - * heap.c - */ -#include -#include -#include - -#define WARNINGS 1 -#define DEBUG_TRACE 0 -#define VERBOSE_DUMP 0 - -// === CONSTANTS === -#define HEAP_INIT_SIZE 0x8000 // 32 KiB -#define MIN_SIZE (sizeof(void*))*8 // 8 Machine Words -#define POW2_SIZES 0 -#define COMPACT_HEAP 0 // Use 4 byte header? -#define FIRST_FIT 0 - -//#define MAGIC_FOOT 0x2ACE5505 -//#define MAGIC_FREE 0xACE55000 -//#define MAGIC_USED 0x862B0505 // MAGIC_FOOT ^ MAGIC_FREE -#define MAGIC_FOOT 0x544F4F46 // 'FOOT' -#define MAGIC_FREE 0x45455246 // 'FREE' -#define MAGIC_USED 0x44455355 // 'USED' - -// === PROTOTYPES === -void Heap_Install(void); -void *Heap_Extend(int Bytes); -void *Heap_Merge(tHeapHead *Head); -//void *Heap_Allocate(const char *File, int Line, size_t Bytes); -//void *Heap_AllocateZero(const char *File, int Line, size_t Bytes); -//void *Heap_Reallocate(const char *File, int Line, void *Ptr, size_t Bytes); -//void Heap_Deallocate(void *Ptr); -void Heap_Dump(void); -void Heap_Stats(void); - -// === GLOBALS === -tMutex glHeap; -void *gHeapStart; -void *gHeapEnd; - -// === CODE === -void Heap_Install(void) -{ - gHeapStart = (void*)MM_KHEAP_BASE; - gHeapEnd = (void*)MM_KHEAP_BASE; - Heap_Extend(HEAP_INIT_SIZE); -} - -/** - * \brief Extend the size of the heap - */ -void *Heap_Extend(int Bytes) -{ - Uint i; - tHeapHead *head = gHeapEnd; - tHeapFoot *foot; - - // Bounds Check - if( (tVAddr)gHeapEnd == MM_KHEAP_MAX ) - return NULL; - - if( Bytes == 0 ) { - Log_Warning("Heap", "Heap_Extend called with Bytes=%i", Bytes); - return NULL; - } - - // Bounds Check - if( (tVAddr)gHeapEnd + ((Bytes+0xFFF)&~0xFFF) > MM_KHEAP_MAX ) { -// Bytes = MM_KHEAP_MAX - (tVAddr)gHeapEnd; - return NULL; - } - - // Heap expands in pages - for( i = 0; i < (Bytes+0xFFF) >> 12; i ++ ) - { - if( !MM_Allocate( (tVAddr)gHeapEnd+(i<<12) ) ) - { - Warning("OOM - Heap_Extend"); - return NULL; - } - } - - // Increas heap end - gHeapEnd = (Uint8*)gHeapEnd + (i << 12); - - // Create Block - head->Size = (Bytes+0xFFF)&~0xFFF; - head->Magic = MAGIC_FREE; - foot = (void*)( (Uint)gHeapEnd - sizeof(tHeapFoot) ); - foot->Head = head; - foot->Magic = MAGIC_FOOT; - - return Heap_Merge(head); // Merge with previous block -} - -/** - * \brief Merges two ajacent heap blocks - */ -void *Heap_Merge(tHeapHead *Head) -{ - tHeapFoot *foot; - tHeapFoot *thisFoot; - tHeapHead *head; - - //Log("Heap_Merge: (Head=%p)", Head); - - thisFoot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) ); - if((Uint)thisFoot > (Uint)gHeapEnd) return NULL; - - // Merge Left (Down) - foot = (void*)( (Uint)Head - sizeof(tHeapFoot) ); - if( ((Uint)foot < (Uint)gHeapEnd && (Uint)foot > (Uint)gHeapStart) - && foot->Head->Magic == MAGIC_FREE) { - foot->Head->Size += Head->Size; // Increase size - thisFoot->Head = foot->Head; // Change backlink - Head->Magic = 0; // Clear old head - Head->Size = 0; - Head = foot->Head; // Save new head address - foot->Head = NULL; // Clear central footer - foot->Magic = 0; - } - - // Merge Right (Upwards) - head = (void*)( (Uint)Head + Head->Size ); - if((Uint)head < (Uint)gHeapEnd && head->Magic == MAGIC_FREE) - { - Head->Size += head->Size; - foot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) ); - foot->Head = Head; // Update Backlink - thisFoot->Head = NULL; // Clear old footer - thisFoot->Magic = 0; - head->Size = 0; // Clear old header - head->Magic = 0; - } - - // Return new address - return Head; -} - -/** - * \param File Allocating source file - * \param Line Source line - * \param __Bytes Size of region to allocate - */ -void *Heap_Allocate(const char *File, int Line, size_t __Bytes) -{ - tHeapHead *head, *newhead; - tHeapFoot *foot, *newfoot; - tHeapHead *best = NULL; - Uint bestSize = 0; // Speed hack - size_t Bytes; - - if( __Bytes == 0 ) { - //return NULL; // TODO: Return a known un-mapped range. - return INVLPTR; - } - - // Get required size - #if POW2_SIZES - Bytes = __Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot); - Bytes = 1UUL << LOG2(__Bytes); - #else - Bytes = (__Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot) + MIN_SIZE-1) & ~(MIN_SIZE-1); - #endif - - // Lock Heap - Mutex_Acquire(&glHeap); - - // Traverse Heap - for( head = gHeapStart; - (Uint)head < (Uint)gHeapEnd; - head = (void*)((Uint)head + head->Size) - ) - { - // Alignment Check - #if POW2_SIZES - if( head->Size != 1UUL << LOG2(head->Size) ) { - #else - if( head->Size & (MIN_SIZE-1) ) { - #endif - Mutex_Release(&glHeap); // Release spinlock - #if WARNINGS - Log_Warning("Heap", "Size of heap address %p is invalid not aligned (0x%x)", head, head->Size); - Heap_Dump(); - #endif - return NULL; - } - - // Check if allocated - if(head->Magic == MAGIC_USED) continue; - // Error check - if(head->Magic != MAGIC_FREE) { - Mutex_Release(&glHeap); // Release spinlock - #if WARNINGS - Log_Warning("Heap", "Magic of heap address %p is invalid (%p = 0x%x)", - head, &head->Magic, head->Magic); - Heap_Dump(); - #endif - return NULL; - } - - // Size check - if(head->Size < Bytes) continue; - - // Perfect fit - if(head->Size == Bytes) { - head->Magic = MAGIC_USED; - head->File = File; - head->Line = Line; - head->ValidSize = __Bytes; - head->AllocateTime = now(); - Mutex_Release(&glHeap); // Release spinlock - #if DEBUG_TRACE - Debug("[Heap ] Malloc'd %p (%i bytes), returning to %p", - head->Data, head->Size, __builtin_return_address(0)); - #endif - return head->Data; - } - - // Break out of loop - #if FIRST_FIT - best = head; - bestSize = head->Size; - break; - #else - // or check if the block is the best size - if(bestSize > head->Size) { - best = head; - bestSize = head->Size; - } - #endif - } - - // If no block large enough is found, create one - if(!best) - { - best = Heap_Extend( Bytes ); - // Check for errors - if(!best) { - Mutex_Release(&glHeap); // Release spinlock - return NULL; - } - // Check size - if(best->Size == Bytes) { - best->Magic = MAGIC_USED; // Mark block as used - best->File = File; - best->Line = Line; - best->ValidSize = __Bytes; - best->AllocateTime = now(); - Mutex_Release(&glHeap); // Release spinlock - #if DEBUG_TRACE - Debug("[Heap ] Malloc'd %p (%i bytes), returning to %s:%i", best->Data, best->Size, File, Line); - #endif - return best->Data; - } - } - - // Split Block - newhead = (void*)( (Uint)best + Bytes ); - newfoot = (void*)( (Uint)best + Bytes - sizeof(tHeapFoot) ); - foot = (void*)( (Uint)best + best->Size - sizeof(tHeapFoot) ); - - newfoot->Head = best; // Create new footer - newfoot->Magic = MAGIC_FOOT; - newhead->Size = best->Size - Bytes; // Create new header - newhead->Magic = MAGIC_FREE; - foot->Head = newhead; // Update backlink in old footer - best->Size = Bytes; // Update size in old header - best->ValidSize = __Bytes; - best->Magic = MAGIC_USED; // Mark block as used - best->File = File; - best->Line = Line; - best->AllocateTime = now(); - - Mutex_Release(&glHeap); // Release spinlock - #if DEBUG_TRACE - Debug("[Heap ] Malloc'd %p (0x%x bytes), returning to %s:%i", - best->Data, best->Size, File, Line); - #endif - return best->Data; -} - -/** - * \brief Free an allocated memory block - */ -void Heap_Deallocate(void *Ptr) -{ - tHeapHead *head = (void*)( (Uint)Ptr - sizeof(tHeapHead) ); - tHeapFoot *foot; - - // INVLPTR is returned from Heap_Allocate when the allocation - // size is zero. - if( Ptr == INVLPTR ) return; - - #if DEBUG_TRACE - Debug("[Heap ] free: %p freed by %p (%i old)", Ptr, __builtin_return_address(0), now()-head->AllocateTime); - #endif - - // Alignment Check - if( (Uint)Ptr & (sizeof(Uint)-1) ) { - Log_Warning("Heap", "free - Passed a non-aligned address (%p)", Ptr); - return; - } - - // Sanity check - if((Uint)Ptr < (Uint)gHeapStart || (Uint)Ptr > (Uint)gHeapEnd) - { - Log_Warning("Heap", "free - Passed a non-heap address by %p (%p < %p < %p)\n", - __builtin_return_address(0), gHeapStart, Ptr, gHeapEnd); - return; - } - - // Check memory block - Header - head = (void*)( (Uint)Ptr - sizeof(tHeapHead) ); - if(head->Magic == MAGIC_FREE) { - Log_Warning("Heap", "free - Passed a freed block (%p) by %p", head, __builtin_return_address(0)); - return; - } - if(head->Magic != MAGIC_USED) { - Log_Warning("Heap", "free - Magic value is invalid (%p, 0x%x)", head, head->Magic); - Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line); - return; - } - - // Check memory block - Footer - foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) ); - if(foot->Head != head) { - Log_Warning("Heap", "free - Footer backlink is incorrect (%p, 0x%x)", head, foot->Head); - Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line); - return; - } - if(foot->Magic != MAGIC_FOOT) { - Log_Warning("Heap", "free - Footer magic is invalid (%p, %p = 0x%x)", head, &foot->Magic, foot->Magic); - Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line); - return; - } - - // Lock - Mutex_Acquire( &glHeap ); - - // Mark as free - head->Magic = MAGIC_FREE; - //head->File = NULL; - //head->Line = 0; - head->ValidSize = 0; - // Merge blocks - Heap_Merge( head ); - - // Release - Mutex_Release( &glHeap ); -} - -/** - * \brief Increase/Decrease the size of an allocation - * \param File Calling File - * \param Line Calling Line - * \param __ptr Old memory - * \param __size New Size - */ -void *Heap_Reallocate(const char *File, int Line, void *__ptr, size_t __size) -{ - tHeapHead *head = (void*)( (Uint)__ptr-sizeof(tHeapHead) ); - tHeapHead *nextHead; - tHeapFoot *foot; - Uint newSize = (__size + sizeof(tHeapFoot)+sizeof(tHeapHead)+MIN_SIZE-1)&~(MIN_SIZE-1); - - // Check for reallocating NULL - if(__ptr == NULL) return Heap_Allocate(File, Line, __size); - - // Check if resize is needed - if(newSize <= head->Size) return __ptr; - - // Check if next block is free - nextHead = (void*)( (Uint)head + head->Size ); - - // Extend into next block - if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize) - { - Uint size = nextHead->Size + head->Size; - foot = (void*)( (Uint)nextHead + nextHead->Size - sizeof(tHeapFoot) ); - // Exact Fit - if(size == newSize) { - head->Size = newSize; - head->ValidSize = __size; - head->File = File; - head->Line = Line; - foot->Head = head; - nextHead->Magic = 0; - nextHead->Size = 0; - return __ptr; - } - // Create a new heap block - nextHead = (void*)( (Uint)head + newSize ); - nextHead->Size = size - newSize; - nextHead->Magic = MAGIC_FREE; - foot->Head = nextHead; // Edit 2nd footer - head->Size = newSize; // Edit first header - head->File = File; - head->Line = Line; - head->ValidSize = __size; - // Create new footer - foot = (void*)( (Uint)head + newSize - sizeof(tHeapFoot) ); - foot->Head = head; - foot->Magic = MAGIC_FOOT; - return __ptr; - } - - // Extend downwards? - foot = (void*)( (Uint)head - sizeof(tHeapFoot) ); - nextHead = foot->Head; - if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize) - { - Uint size = nextHead->Size + head->Size; - // Inexact fit, split things up - if(size > newSize) - { - // TODO - Warning("[Heap ] TODO: Space efficient realloc when new size is smaller"); - } - - // Exact fit - if(size >= newSize) - { - Uint oldDataSize; - // Set 1st (new/lower) header - nextHead->Magic = MAGIC_USED; - nextHead->Size = newSize; - nextHead->File = File; - nextHead->Line = Line; - nextHead->ValidSize = __size; - // Get 2nd (old) footer - foot = (void*)( (Uint)nextHead + newSize ); - foot->Head = nextHead; - // Save old data size - oldDataSize = head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead); - // Clear old header - head->Size = 0; - head->Magic = 0; - // Copy data - memcpy(nextHead->Data, __ptr, oldDataSize); - // Return - return nextHead->Data; - } - // On to the expensive then - } - - // Well, darn - nextHead = Heap_Allocate( File, Line, __size ); - nextHead -= 1; - nextHead->File = File; - nextHead->Line = Line; - nextHead->ValidSize = __size; - - memcpy( - nextHead->Data, - __ptr, - head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead) - ); - - free(__ptr); - - return nextHead->Data; -} - -/** - * \fn void *Heap_AllocateZero(const char *File, int Line, size_t Bytes) - * \brief Allocate and Zero a buffer in memory - * \param File Allocating file - * \param Line Line of allocation - * \param Bytes Size of the allocation - */ -void *Heap_AllocateZero(const char *File, int Line, size_t Bytes) -{ - void *ret = Heap_Allocate(File, Line, Bytes); - if(ret == NULL) return NULL; - - memset( ret, 0, Bytes ); - - return ret; -} - -/** - * \fn int Heap_IsHeapAddr(void *Ptr) - * \brief Checks if an address is a heap pointer - */ -int Heap_IsHeapAddr(void *Ptr) -{ - tHeapHead *head; - if((Uint)Ptr < (Uint)gHeapStart) return 0; - if((Uint)Ptr > (Uint)gHeapEnd) return 0; - if((Uint)Ptr & (sizeof(Uint)-1)) return 0; - - head = (void*)( (Uint)Ptr - sizeof(tHeapHead) ); - if(head->Magic != MAGIC_USED && head->Magic != MAGIC_FREE) - return 0; - - return 1; -} - -/** - */ -void Heap_Validate(void) -{ - Heap_Dump(); -} - -#if WARNINGS -void Heap_Dump(void) -{ - tHeapHead *head, *badHead; - tHeapFoot *foot = NULL; - - head = gHeapStart; - while( (Uint)head < (Uint)gHeapEnd ) - { - foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) ); - #if VERBOSE_DUMP - Log_Log("Heap", "%p (0x%P): 0x%08x (%i) %4C", - head, MM_GetPhysAddr((tVAddr)head), head->Size, head->ValidSize, &head->Magic); - Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic); - if(head->File) { - Log_Log("Heap", "%sowned by %s:%i", - (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line); - } - #endif - - // Sanity Check Header - if(head->Size == 0) { - Log_Warning("Heap", "HALTED - Size is zero"); - break; - } - if(head->Size & (MIN_SIZE-1)) { - Log_Warning("Heap", "HALTED - Size is malaligned"); - break; - } - if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) { - Log_Warning("Heap", "HALTED - Head Magic is Bad"); - break; - } - - // Check footer - if(foot->Magic != MAGIC_FOOT) { - Log_Warning("Heap", "HALTED - Foot Magic is Bad"); - break; - } - if(head != foot->Head) { - Log_Warning("Heap", "HALTED - Footer backlink is invalid"); - break; - } - - #if VERBOSE_DUMP - Log_Log("Heap", ""); - #endif - - // All OK? Go to next - head = foot->NextHead; - } - - // If the heap is valid, ok! - if( (tVAddr)head == (tVAddr)gHeapEnd ) - return ; - - // Check for a bad return - if( (tVAddr)head >= (tVAddr)gHeapEnd ) - return ; - - #if !VERBOSE_DUMP - Log_Log("Heap", "%p (%P): 0x%08lx %i %4C", - head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic); - if(foot) - Log_Log("Heap", "Foot %p = {Head:%p,Magic:%4C}", foot, foot->Head, &foot->Magic); - if(head->File) { - Log_Log("Heap", "%sowned by %s:%i", - (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line); - } - Log_Log("Heap", ""); - #endif - - - badHead = head; - - // Work backwards - foot = (void*)( (tVAddr)gHeapEnd - sizeof(tHeapFoot) ); - Log_Log("Heap", "==== Going Backwards ==== (from %p)", foot); - head = foot->Head; - while( (tVAddr)head >= (tVAddr)badHead ) - { - Log_Log("Heap", "%p (%P): 0x%08lx %i %4C", - head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic); - Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic); - if(head->File) - Log_Log("Heap", "%sowned by %s:%i", - (head->Magic!=MAGIC_USED?"was ":""), - head->File, head->Line); - Log_Log("Heap", ""); - - // Sanity Check Header - if(head->Size == 0) { - Log_Warning("Heap", "HALTED - Size is zero"); - break; - } - if(head->Size & (MIN_SIZE-1)) { - Log_Warning("Heap", " - Size is malaligned (&0x%x)", ~(MIN_SIZE-1)); - break ; - } - if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) { - Log_Warning("Heap", "HALTED - Head Magic is Bad"); - break; - } - - // Check footer - if(foot->Magic != MAGIC_FOOT) { - Log_Warning("Heap", "HALTED - Foot Magic is Bad"); - break; - } - if(head != foot->Head) { - Log_Warning("Heap", "HALTED - Footer backlink is invalid"); - break; - } - - if(head == badHead) break; - - foot = (void*)( (tVAddr)head - sizeof(tHeapFoot) ); - head = foot->Head; - Log_Debug("Heap", "head=%p", head); - } - - Panic("Heap_Dump - Heap is corrupted, kernel panic!"); -} -#endif - -#if 1 -void Heap_Stats(void) -{ - tHeapHead *head; - int nBlocks = 0; - int nFree = 0; - int totalBytes = 0; - int freeBytes = 0; - int maxAlloc=0, minAlloc=-1; - int avgAlloc, frag, overhead; - - for(head = gHeapStart; - (Uint)head < (Uint)gHeapEnd; - head = (void*)( (Uint)head + head->Size ) - ) - { - nBlocks ++; - totalBytes += head->Size; - if( head->Magic == MAGIC_FREE ) - { - nFree ++; - freeBytes += head->Size; - } - else if( head->Magic == MAGIC_USED) { - if(maxAlloc < head->Size) maxAlloc = head->Size; - if(minAlloc == -1 || minAlloc > head->Size) - minAlloc = head->Size; - } - else { - Log_Warning("Heap", "Magic on %p invalid, skipping remainder of heap", head); - break; - } - - // Print the block info? - #if 1 - if( head->Magic == MAGIC_FREE ) - Log_Debug("Heap", "%p (%P) - 0x%x free", - head->Data, MM_GetPhysAddr((tVAddr)&head->Data), head->Size); - else - Log_Debug("Heap", "%p (%P) - 0x%x (%i) Owned by %s:%i (%lli ms old)", - head->Data, MM_GetPhysAddr((tVAddr)&head->Data), head->Size, head->ValidSize, head->File, head->Line, - now() - head->AllocateTime - ); - #endif - } - - Log_Log("Heap", "%i blocks (0x%x bytes)", nBlocks, totalBytes); - Log_Log("Heap", "%i free blocks (0x%x bytes)", nFree, freeBytes); - if(nBlocks != 0) - frag = (nFree-1)*10000/nBlocks; - else - frag = 0; - Log_Log("Heap", "%i.%02i%% Heap Fragmentation", frag/100, frag%100); - if(nBlocks <= nFree) - avgAlloc = 0; - else - avgAlloc = (totalBytes-freeBytes)/(nBlocks-nFree); - if(avgAlloc != 0) - overhead = (sizeof(tHeapFoot)+sizeof(tHeapHead))*10000/avgAlloc; - else - overhead = 0; - Log_Log("Heap", "Average allocation: %i bytes, Average Overhead: %i.%02i%%", - avgAlloc, overhead/100, overhead%100 - ); - Log_Log("Heap", "Smallest Block: %i bytes, Largest: %i bytes", - minAlloc, maxAlloc); - - // Scan and get distribution - #if 1 - if(nBlocks > 0) - { - struct { - Uint Size; - Uint Count; - } sizeCounts[nBlocks]; - int i; - - memset(sizeCounts, 0, nBlocks*sizeof(sizeCounts[0])); - - for(head = gHeapStart; - (Uint)head < (Uint)gHeapEnd; - head = (void*)( (Uint)head + head->Size ) - ) - { - for( i = 0; i < nBlocks; i ++ ) { - if( sizeCounts[i].Size == 0 ) - break; - if( sizeCounts[i].Size == head->Size ) - break; - } - // Should never reach this part (in a non-concurrent case) - if( i == nBlocks ) continue; - sizeCounts[i].Size = head->Size; - sizeCounts[i].Count ++; - #if 1 - //Log("Heap_Stats: %i %p - 0x%x bytes (%s) (%i)", nBlocks, head, - // head->Size, (head->Magic==MAGIC_FREE?"FREE":"used"), i - // ); - //Log("Heap_Stats: sizeCounts[%i] = {Size:0x%x, Count: %i}", i, - // sizeCounts[i].Size, sizeCounts[i].Count); - #endif - } - - for( i = 0; i < nBlocks && sizeCounts[i].Count; i ++ ) - { - Log("Heap_Stats: 0x%x - %i blocks", - sizeCounts[i].Size, sizeCounts[i].Count - ); - } - } - #endif -} -#endif - -// === EXPORTS === -EXPORT(Heap_Allocate); -EXPORT(Heap_AllocateZero); -EXPORT(Heap_Reallocate); -EXPORT(Heap_Deallocate); -EXPORT(Heap_IsHeapAddr); diff --git a/Kernel/include/acess.h b/Kernel/include/acess.h deleted file mode 100644 index 2cd2cff1..00000000 --- a/Kernel/include/acess.h +++ /dev/null @@ -1,524 +0,0 @@ -/* - * AcessOS Microkernel Version - * acess.h - */ -#ifndef _ACESS_H -#define _ACESS_H -/** - * \file acess.h - * \brief Acess2 Kernel API Core - */ - -//! NULL Pointer -#define NULL ((void*)0) -//! Pack a structure -#define PACKED __attribute__((packed)) -//! Mark a function as not returning -#define NORETURN __attribute__((noreturn)) -//! Mark a parameter as unused -#define UNUSED(x) UNUSED_##x __attribute__((unused)) -//! Get the offset of a member in a structure -#define offsetof(st, m) ((Uint)((char *)&((st *)(0))->m - (char *)0 )) - -/** - * \name Boolean constants - * \{ - */ -#define TRUE 1 -#define FALSE 0 -/** - * \} - */ - -#include -#include -#include "errno.h" - -// --- Types --- -typedef Uint32 tPID; //!< Process ID type -typedef Uint32 tTID; //!< Thread ID Type -typedef Uint32 tUID; //!< User ID Type -typedef Uint32 tGID; //!< Group ID Type -typedef Sint64 tTimestamp; //!< Timestamp (miliseconds since 00:00 1 Jan 1970) -typedef Sint64 tTime; //!< Same again -typedef struct sShortSpinlock tShortSpinlock; //!< Opaque (kinda) spinlock -typedef int bool; //!< Boolean type - -// --- Helper Macros --- -/** - * \name Helper Macros - * \{ - */ -#define CONCAT(x,y) x ## y -#define EXPAND_CONCAT(x,y) CONCAT(x,y) -#define STR(x) #x -#define EXPAND_STR(x) STR(x) - -extern char __buildnum[]; -#define BUILD_NUM ((int)(Uint)&__buildnum) -extern const char gsGitHash[]; - -#define VER2(major,minor) ((((major)&0xFF)<<8)|((minor)&0xFF)) -/** - * \} - */ - -//! \brief Error number -#define errno (*Threads_GetErrno()) - -// === CONSTANTS === -// --- Memory Flags -- -/** - * \name Memory Flags - * \{ - * \todo Move to mm_virt.h - */ -#define MM_PFLAG_RO 0x01 // Writes disallowed -#define MM_PFLAG_EXEC 0x02 // Allow execution -#define MM_PFLAG_NOPAGE 0x04 // Prevent from being paged out -#define MM_PFLAG_COW 0x08 // Copy-On-Write -#define MM_PFLAG_KERNEL 0x10 // Kernel-Only (Ring0) -/** - * \} - */ -// --- Interface Flags & Macros -/** - * \name Flags for Proc_Clone - * \{ - */ -//! Clone the entire process -#define CLONE_VM 0x10 -//! Don't copy user pages -#define CLONE_NOUSER 0x20 -/** - * \} - */ - -// === Types === -/** - * \brief Thread root function - * - * Function pointer prototype of a thread entrypoint. When it - * returns, Threads_Exit is called - */ -typedef void (*tThreadFunction)(void*); - -// === Kernel Export Macros === -/** - * \name Kernel exports - * \{ - */ -//! Kernel symbol definition -typedef struct sKernelSymbol { - const char *Name; //!< Symbolic name - tVAddr Value; //!< Value of the symbol -} tKernelSymbol; -//! Export a pointer symbol (function/array) -#define EXPORT(_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (tVAddr)_name} -//! Export a variable -#define EXPORTV(_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (tVAddr)&_name} -//! Export a symbol under another name -#define EXPORTAS(_sym,_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (tVAddr)_sym} -/** - * \} - */ - -// === FUNCTIONS === -// --- IRQs --- -/** - * \name IRQ hander registration - * \{ - */ -extern int IRQ_AddHandler(int Num, void (*Callback)(int, void*), void *Ptr); -extern void IRQ_RemHandler(int Handle); -/** - * \} - */ - -// --- Logging --- -/** - * \name Logging to kernel ring buffer - * \{ - */ -extern void Log_KernelPanic(const char *Ident, const char *Message, ...); -extern void Log_Panic(const char *Ident, const char *Message, ...); -extern void Log_Error(const char *Ident, const char *Message, ...); -extern void Log_Warning(const char *Ident, const char *Message, ...); -extern void Log_Notice(const char *Ident, const char *Message, ...); -extern void Log_Log(const char *Ident, const char *Message, ...); -extern void Log_Debug(const char *Ident, const char *Message, ...); -/** - * \} - */ - -// --- Debug --- -/** - * \name Debugging and Errors - * \{ - */ -extern void Debug_KernelPanic(void); //!< Initiate a kernel panic -extern void Panic(const char *Msg, ...); //!< Print a panic message (initiates a kernel panic) -extern void Warning(const char *Msg, ...); //!< Print a warning message -extern void LogF(const char *Fmt, ...); //!< Print a log message without a trailing newline -extern void Log(const char *Fmt, ...); //!< Print a log message -extern void Debug(const char *Fmt, ...); //!< Print a debug message (doesn't go to KTerm) -extern void LogV(const char *Fmt, va_list Args); //!< va_list Log message -extern void Debug_Enter(const char *FuncName, const char *ArgTypes, ...); -extern void Debug_Log(const char *FuncName, const char *Fmt, ...); -extern void Debug_Leave(const char *FuncName, char RetType, ...); -extern void Debug_HexDump(const char *Header, const void *Data, Uint Length); -#define UNIMPLEMENTED() Warning("'%s' unimplemented", __func__) -#if DEBUG -# define ENTER(_types...) Debug_Enter((char*)__func__, _types) -# define LOG(_fmt...) Debug_Log((char*)__func__, _fmt) -# define LEAVE(_t...) Debug_Leave((char*)__func__, _t) -# define LEAVE_RET(_t,_v...) do{LEAVE(_t,_v);return _v;}while(0) -# define LEAVE_RET0() do{LEAVE('-');return;}while(0) -#else -# define ENTER(...) -# define LOG(...) -# define LEAVE(...) -# define LEAVE_RET(_t,_v...) return (_v) -# define LEAVE_RET0() return -#endif -#if SANITY -# define ASSERT(expr) do{if(!(expr))Panic("%s: Assertion '"#expr"' failed",(char*)__func__);}while(0) -#else -# define ASSERT(expr) -#endif -/** - * \} - */ - -// --- IO --- -#if NO_IO_BUS -#define inb(a) (Log_Panic("Arch", STR(ARCHDIR)" does not support in*/out* (%s:%i)", __FILE__, __LINE__),0) -#define inw(a) inb(a) -#define ind(a) inb(a) -#define inq(a) inb(a) -#define outb(a,b) inb(a) -#define outw(a,b) inb(a) -#define outd(a,b) inb(a) -#define outq(a,b) inb(a) -#else -/** - * \name I/O Memory Access - * \{ - */ -extern void outb(Uint16 Port, Uint8 Data); -extern void outw(Uint16 Port, Uint16 Data); -extern void outd(Uint16 Port, Uint32 Data); -extern void outq(Uint16 Port, Uint64 Data); -extern Uint8 inb(Uint16 Port); -extern Uint16 inw(Uint16 Port); -extern Uint32 ind(Uint16 Port); -extern Uint64 inq(Uint16 Port); -/** - * \} - */ -#endif -// --- Memory Management --- -/** - * \name Memory Management - * \{ - * \todo Move to mm_virt.h - */ -/** - * \brief Allocate a physical page at \a VAddr - * \param VAddr Virtual Address to allocate at - * \return Physical address allocated - */ -extern tPAddr MM_Allocate(tVAddr VAddr) __attribute__ ((warn_unused_result)); -/** - * \brief Deallocate a page - * \param VAddr Virtual address to unmap - */ -extern void MM_Deallocate(tVAddr VAddr); -/** - * \brief Map a physical page at \a PAddr to \a VAddr - * \param VAddr Target virtual address - * \param PAddr Physical address to map - * \return Boolean Success - */ -extern int MM_Map(tVAddr VAddr, tPAddr PAddr); -/** - * \brief Get the physical address of \a Addr - * \param Addr Address of the page to get the physical address of - * \return Physical page mapped at \a Addr - */ -extern tPAddr MM_GetPhysAddr(tVAddr Addr); -/** - * \brief Set the access flags on a page - * \param VAddr Virtual address of the page - * \param Flags New flags value - * \param Mask Flags to set - */ -extern void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask); -/** - * \brief Get the flags on a flag - * \param VAddr Virtual address of page - * \return Flags value of the page - */ -extern Uint MM_GetFlags(tVAddr VAddr); -/** - * \brief Checks is a memory range is user accessable - * \param VAddr Base address to check - * \return 1 if the memory is all user-accessable, 0 otherwise - */ -#define MM_IsUser(VAddr) (!(MM_GetFlags((VAddr))&MM_PFLAG_KERNEL)) -/** - * \brief Temporarily map a page into the address space - * \param PAddr Physical addres to map - * \return Virtual address of page in memory - * \note There is only a limited ammount of slots avaliable - */ -extern tVAddr MM_MapTemp(tPAddr PAddr); -/** - * \brief Free a temporarily mapped page - * \param VAddr Allocate virtual addres of page - */ -extern void MM_FreeTemp(tVAddr VAddr); -/** - * \brief Map a physcal address range into the virtual address space - * \param PAddr Physical address to map in - * \param Number Number of pages to map - */ -extern tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number); -/** - * \brief Allocates DMA physical memory - * \param Pages Number of pages required - * \param MaxBits Maximum number of bits the physical address can have - * \param PhysAddr Pointer to the location to place the physical address allocated - * \return Virtual address allocate - */ -extern tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr); -/** - * \brief Unmaps an allocated hardware range - * \param VAddr Virtual address allocate by ::MM_MapHWPages or ::MM_AllocDMA - * \param Number Number of pages to free - */ -extern void MM_UnmapHWPages(tVAddr VAddr, Uint Number); -/** - * \brief Allocate a single physical page - * \return Physical address allocated - */ -extern tPAddr MM_AllocPhys(void); -/** - * \brief Allocate a contiguous range of physical pages - * \param Pages Number of pages to allocate - * \param MaxBits Maximum number of address bits allowed - * \return First physical address allocated - */ -extern tPAddr MM_AllocPhysRange(int Pages, int MaxBits); -/** - * \brief Reference a physical page - * \param PAddr Page to mark as referenced - */ -extern void MM_RefPhys(tPAddr PAddr); -/** - * \brief Dereference a physical page - * \param PAddr Page to dereference - */ -extern void MM_DerefPhys(tPAddr PAddr); -/** - * \brief Get the number of times a page has been referenced - * \param PAddr Address to check - * \return Reference count for the page - */ -extern int MM_GetRefCount(tPAddr PAddr); -/** - * \brief Set the node associated with a page - * \param PAddr Physical address of page - * \param Node Node pointer (tVFS_Node) - * \return Boolean failure - * \retval 0 Success - * \retval 1 Page not allocated - */ -extern int MM_SetPageNode(tPAddr PAddr, void *Node); -/** - * \brief Get the node associated with a page - * \param PAddr Physical address of page - * \param Node Node pointer (tVFS_Node) destination - * \return Boolean failure - * \retval 0 Success - * \retval 1 Page not allocated - */ -extern int MM_GetPageNode(tPAddr PAddr, void **Node); -/** - * \} - */ - -// --- Memory Manipulation --- -/** - * \name Memory Manipulation - * \{ - */ -extern int memcmp(const void *m1, const void *m2, size_t count); -extern void *memcpy(void *dest, const void *src, size_t count); -extern void *memcpyd(void *dest, const void *src, size_t count); -extern void *memmove(void *dest, const void *src, size_t len); -extern void *memset(void *dest, int val, size_t count); -extern void *memsetd(void *dest, Uint32 val, size_t count); -/** - * \} - */ -/** - * \name Memory Validation - * \{ - */ -extern int CheckString(const char *String); -extern int CheckMem(const void *Mem, int Num); -/** - * \} - */ - -// --- Endianness --- -/** - * \name Endianness Swapping - * \{ - */ -#ifdef __BIG_ENDIAN__ -#define LittleEndian16(_val) SwapEndian16(_val) -#define LittleEndian32(_val) SwapEndian32(_val) -#define BigEndian16(_val) (_val) -#define BigEndian32(_val) (_val) -#else -#define LittleEndian16(_val) (_val) -#define LittleEndian32(_val) (_val) -#define BigEndian16(_val) SwapEndian16(_val) -#define BigEndian32(_val) SwapEndian32(_val) -#endif -extern Uint16 SwapEndian16(Uint16 Val); -extern Uint32 SwapEndian32(Uint32 Val); -/** - * \} - */ - -// --- Strings --- -/** - * \name Strings - * \{ - */ -extern int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args); -extern int sprintf(char *__s, const char *__format, ...); -extern size_t strlen(const char *Str); -extern char *strcpy(char *__dest, const char *__src); -extern char *strncpy(char *__dest, const char *__src, size_t max); -extern char *strcat(char *__dest, const char *__src); -extern char *strncat(char *__dest, const char *__src, size_t n); -extern int strcmp(const char *__str1, const char *__str2); -extern int strncmp(const char *Str1, const char *Str2, size_t num); -extern int strucmp(const char *Str1, const char *Str2); -// strdup macro is defined in heap.h -extern char *_strdup(const char *File, int Line, const char *Str); -extern char **str_split(const char *__str, char __ch); -extern char *strchr(const char *__s, int __c); -extern int strpos(const char *Str, char Ch); -extern int strpos8(const char *str, Uint32 search); -extern void itoa(char *buf, Uint64 num, int base, int minLength, char pad); -extern int atoi(const char *string); -extern int ParseInt(const char *string, int *Val); -extern int ReadUTF8(const Uint8 *str, Uint32 *Val); -extern int WriteUTF8(Uint8 *str, Uint32 Val); -extern int ModUtil_SetIdent(char *Dest, const char *Value); -extern int ModUtil_LookupString(const char **Array, const char *Needle); - -extern Uint8 ByteSum(const void *Ptr, int Size); -extern int Hex(char *Dest, size_t Size, const Uint8 *SourceData); -extern int UnHex(Uint8 *Dest, size_t DestSize, const char *SourceString); -/** - * \} - */ - -/** - * \brief Get a random number - * \return Random number - * \note Current implementation is a linear congruency - */ -extern int rand(void); -/** - * \brief Call a function with a variable number of arguments - * \param Function Function address - * \param NArgs Number of entries in \a Args - * \param Args Array of arguments - * \return Integer from called Function - */ -extern int CallWithArgArray(void *Function, int NArgs, Uint *Args); - -// --- Heap --- -#include -/** - * \brief Magic heap allocation function - */ -extern void *alloca(size_t Size); - -// --- Modules --- -/** - * \name Modules - * \{ - */ -extern int Module_LoadMem(void *Buffer, Uint Length, const char *ArgStr); -extern int Module_LoadFile(const char *Path, const char *ArgStr); -/** - * \} - */ - -// --- Timing --- -/** - * \name Time and Timing - * \{ - */ -/** - * \brief Create a timestamp from a time - */ -extern tTime timestamp(int sec, int mins, int hrs, int day, int month, int year); -/** - * \brief Extract the date/time from a timestamp - */ -extern void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms); -/** - * \brief Gets the current timestamp (miliseconds since Midnight 1st January 1970) - */ -extern Sint64 now(void); -/** - * \} - */ - -// --- Threads --- -/** - * \name Threads and Processes - * \{ - */ -extern int Proc_SpawnWorker(void (*Fcn)(void*), void *Data); -extern int Proc_Spawn(const char *Path); -extern int Proc_SysSpawn(const char *Binary, const char **ArgV, const char **EnvP, int nFD, int *FDs); -extern int Proc_Execve(const char *File, const char **ArgV, const char **EnvP, int DataSize); -extern void Threads_Exit(int TID, int Status); -extern void Threads_Yield(void); -extern void Threads_Sleep(void); -extern int Threads_WakeTID(tTID Thread); -extern tPID Threads_GetPID(void); -extern tTID Threads_GetTID(void); -extern tUID Threads_GetUID(void); -extern tGID Threads_GetGID(void); -extern int SpawnTask(tThreadFunction Function, void *Arg); -extern int *Threads_GetErrno(void); -extern int Threads_SetName(const char *NewName); -/** - * \} - */ - -// --- Simple Math --- -//! Divide and round up -extern int DivUp(int num, int dem); -//! Divide and Modulo 64-bit unsigned integer -extern Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem); - -static inline int MIN(int a, int b) { return a < b ? a : b; } -static inline int MAX(int a, int b) { return a > b ? a : b; } - -#include -#include -#include - -#endif diff --git a/Kernel/include/adt.h b/Kernel/include/adt.h deleted file mode 100644 index 67e12665..00000000 --- a/Kernel/include/adt.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Acess2 - * - Abstract Data Types - */ -#ifndef _ADT_H_ -#define _ADT_H_ - -/** - * \name Ring Buffers - * \{ - */ -typedef struct sRingBuffer -{ - size_t Start; //!< Start of data in ring buffer - size_t Length; //!< Number of data bytes in buffer - size_t Space; //!< Allocated space in buffer - tShortSpinlock Lock; //!< Lock to prevent collisions - char Data[]; //!< Buffer -} tRingBuffer; - -/** - * \brief Create a ring buffer \a Space bytes large - * \param Space Ammount of space to allocate within the buffer - * \return Pointer to the buffer structure - */ -extern tRingBuffer *RingBuffer_Create(size_t Space); -/** - * \brief Read at most \a Length bytes from the buffer - * \param Dest Destinaton buffer - * \param Buffer Source ring buffer - * \param Length Requested number of bytes - * \return Number of bytes read - */ -extern size_t RingBuffer_Read(void *Dest, tRingBuffer *Buffer, size_t Length); -/** - * \brief Write at most \a Length bytes to the buffer - * \param Buffer Destination ring buffer - * \param Source Source buffer - * \param Length Provided number of bytes - * \return Number of bytes written - */ -extern size_t RingBuffer_Write(tRingBuffer *Buffer, const void *Source, size_t Length); -/** - * \} - */ - - -#endif diff --git a/Kernel/include/api_drv_common.h b/Kernel/include/api_drv_common.h deleted file mode 100644 index 59bef5fe..00000000 --- a/Kernel/include/api_drv_common.h +++ /dev/null @@ -1,141 +0,0 @@ -/** - * \file api_drv_common.h - * \brief Common Driver Interface Definitions - * \author John Hodge (thePowersGang) - * - * \section Introduction - * There are two ways Acess drivers can communicate with userspace - * applications, both are through the VFS. The first is by exposing a - * device as a file buffer, the second is by sending commands via the - * ioctl() system call. - * All drivers in Acess must at least conform to the specifcation in this - * file (even if it is just implementing eTplDrv_IOCtl.DRV_IOCTL_TYPE and - * returning eTplDrv_Type.DRV_TYPE_NULL, however, doing so is discouraged) - * - * \section ioctls Core IOCtl calls - * As said, the core Acess driver specifcation defines specific IOCtl calls - * that all drivers should implement. The four core IOCtls (defined in - * ::eTplDrv_IOCtl) allow another binary (wether it be a user-mode application - * or another driver) to tell what type of device a driver provides, the - * basic identifcation of the driver (4 character ID and BCD version number) - * and be able to use externally standardised calls that may not have - * standardised call numbers. - * NOTE: All ioctl calls WILL return -1 if the driver ran into an error - * of its own fault while executing the call. If the user was at fault - * (e.g. by passing a bad buffer) the call will return -2. - * - * \section types Driver Types - * When the eTplDrv_IOCtl.DRV_IOCTL_TYPE call is made, the driver should - * return the relevant entry in the ::eTplDrv_Type enumeration that describes - * what sub-specifcation (and hence, what device type) it implements. - * These device types are described in their own files, which are liked - * from their entries in ::eTplDrv_Type. - */ -#ifndef _API_DRV_COMMON_H -#define _API_DRV_COMMON_H - -/** - * \enum eTplDrv_IOCtl - * \brief Common IOCtl Calls - */ -enum eTplDrv_IOCtl { - /** - * ioctl(...) - * \brief Get driver type - * \return The relevant entry from ::eTplDrv_Type - */ - DRV_IOCTL_TYPE, - - /** - * ioctl(..., char *dest[32]) - * \brief Get driver identifier string - * \return 0 on no error - * - * This call sets the 32-byte array \a dest to the drivers 31 charater - * identifier. This identifier must be unique to the driver series. - */ - DRV_IOCTL_IDENT, - - /** - * ioctl(...) - * \brief Get driver version number - * \return 24-bit BCD version number (2.2.2) - * - * This call returns the 6-digit (2 major, 2 minor, 2 patch) version - * number of the driver. - */ - DRV_IOCTL_VERSION, - - /** - * ioctl(..., char *name) - * \brief Get a IOCtl call ID from a symbolic name - * \return ID number of the call, or 0 if not found - * - * This call allows user applications to not need to know the ID numbers - * of this driver's IOCtl calls by taking a string and returning the - * IOCtl call number associated with that method name. - */ - DRV_IOCTL_LOOKUP, - - /** - * \brief First non-reserved IOCtl number for driver extension - */ - DRV_IOCTL_USERMIN = 0x1000, -}; - -/** - * \brief eTplDrv_IOCtl.DRV_IOCTL_LOOKUP names for the core IOCtls - * These are the official lookup names of the core calls - */ -#define DRV_IOCTLNAMES "type", "ident", "version", "lookup" - -/** - * \brief Helper macro for the base IOCtl calls - * \param _type Type number from eTplDrv_Type to return - * \param _ident String of max 32-characters that identifies this driver - * \param _version Driver's 8.8.8 BCD version number - * \param _ioctls Pointer to the IOCtls string array - * \warning If you have DEBUG enabled in the calling file, this function - * will do LEAVE()s before returning, so make sure that the - * IOCtl function is ENTER()ed when using debug with this macro - * - * Usage: (Id is the IOCtl call ID) - * \code - * switch(Id) - * { - * BASE_IOCTLS(DRV_TYPE_MISC, "Ident", 0x100, csaIOCtls) - * // Your IOCtls go here, starting at index 4 - * } - * \endcode - */ -#define BASE_IOCTLS(_type, _ident, _version, _ioctls) \ - case DRV_IOCTL_TYPE: LEAVE('i', (_type)); return (_type);\ - case DRV_IOCTL_IDENT: {\ - int tmp = ModUtil_SetIdent(Data, (_ident));\ - LEAVE('i', tmp); return tmp;\ - }\ - case DRV_IOCTL_VERSION: LEAVE('x', (_version)); return (_version);\ - case DRV_IOCTL_LOOKUP:{\ - int tmp = ModUtil_LookupString( _ioctls, (const char*)Data );\ - LEAVE('i', tmp);\ - return tmp;\ - } - -/** - * \enum eTplDrv_Type - * \brief Driver Types returned by DRV_IOCTL_TYPE - */ -enum eTplDrv_Type { - DRV_TYPE_NULL, //!< NULL Type - Custom Interface - DRV_TYPE_MISC, //!< Miscelanious Compilant - Supports the core calls - DRV_TYPE_TERMINAL, //!< Terminal - see api_drv_terminal.h - DRV_TYPE_VIDEO, //!< Video - see api_drv_video.h - DRV_TYPE_SOUND, //!< Audio - DRV_TYPE_DISK, //!< Disk - see api_drv_disk.h - DRV_TYPE_KEYBOARD, //!< Keyboard - see api_drv_keyboard.h - DRV_TYPE_MOUSE, //!< Mouse - DRV_TYPE_JOYSTICK, //!< Joystick / Gamepad - DRV_TYPE_NETWORK //!< Network Device - see api_drv_network.h -}; - -#endif diff --git a/Kernel/include/api_drv_disk.h b/Kernel/include/api_drv_disk.h deleted file mode 100644 index 94ea2c94..00000000 --- a/Kernel/include/api_drv_disk.h +++ /dev/null @@ -1,185 +0,0 @@ -/** - * \file api_drv_disk.h - * \brief Disk Driver Interface Definitions - * \author John Hodge (thePowersGang) - * - * \section Nomeclature - * All addreses are 64-bit counts of bytes from the logical beginning of - * the disk unless explicitly stated. - * - * \section dirs VFS Layout - * Disk drivers have a flexible directory layout. The root directory can - * contain subdirectories, with the only conditions being that all nodes - * must support ::eTplDrv_IOCtl with DRV_IOCTL_TYPE returning DRV_TYPE_DISK. - * And all file nodes representing disk devices (or partitions) and implemeting - * ::eTplDisk_IOCtl fully - * - * \section files Files - * When a read or write occurs on a normal file in the disk driver it will - * read/write the represented device. The accesses need not be aligned to - * the block size, however aligned reads/writes should be handled specially - * to improve speed (this can be aided by using ::DrvUtil_ReadBlock and - * ::DrvUtil_WriteBlock) - */ -#ifndef _API_DRV_DISK_H -#define _API_DRV_DISK_H - -#include - -/** - * \enum eTplDisk_IOCtl - * \brief Common Disk IOCtl Calls - * \extends eTplDrv_IOCtl - */ -enum eTplDisk_IOCtl { - /** - * ioctl(..., void) - * \brief Get the block size - * \return Size of a hardware block for this device - */ - DISK_IOCTL_GETBLOCKSIZE = 4, - - /** - * ioctl(..., tTplDisk_CacheRegion *RegionInfo) - * \brief Sets the cache importantce and protocol for a section of - * memory. - * \param RegionInfo Pointer to a region information structure - * \return Boolean failure - */ - DISK_IOCTL_SETCACHEREGION, - - /** - * ioctl(..., Uint64 *Info[2]) - * \brief Asks the driver to precache a region of disk. - * \param Region 64-bit Address and Size pair describing the area to cache - * \return Number of blocks cached - */ - DISK_IOCTL_PRECACHE, - - /** - * ioclt(..., Uint64 *Region[2]) - * \brief Asks to driver to flush the region back to disk - * \param Region 64-bit Address and Size pair describing the area to flush - * \note If Region[0] == -1 then the entire disk's cache is flushed - * \return Number of blocks flushed (or 0 for entire disk) - */ - DISK_IOCTL_FLUSH -}; - -/** - * \brief Describes the cache parameters of a region on the disk - */ -typedef struct sTplDisk_CacheRegion -{ - Uint64 Base; //!< Base of cache region - Uint64 Length; //!< Size of cache region - /** - * \brief Cache Protocol & Flags - * - * The low 4 bits denot the cache protocol to be used by the - * region (see ::eTplDisk_CacheProtocols for a list). - * The high 4 bits denote flags to apply to the cache (see - * ::eTplDisk_CacheFlags) - */ - Uint8 Flags; - Uint8 Priority; //!< Lower is a higher proritory - /** - * \brief Maximum size of cache, in blocks - * \note If CacheSize is zero, the implemenation defined limit is used - */ - Uint16 CacheSize; -} tTplDisk_CacheRegion; - -/** - * \brief Cache protocols to use - */ -enum eTplDisk_CacheProtocols -{ - /** - * \brief Don't cache the region - */ - - DISK_CACHEPROTO_DONTCACHE, - /** - * \brief Most recently used blocks cached - * \note This is the default action for undefined regions - */ - DISK_CACHEPROTO_RECENTLYUSED, - /** - * \brief Cache the entire region in memory - * - * This is a faster version of setting Length to CacheSize*BlockSize - */ - DISK_CACHEPROTO_FULLCACHE, - - /** - * \brief Cache only on demand - * - * Only cache when the ::DISK_IOCTL_PRECACHE IOCtl is used - */ - DISK_CACHEPROTO_EXPLICIT -}; - -/** - * \brief Flags for the cache - */ -enum eTplDisk_CacheFlags -{ - /** - * \brief Write all changes to the region straight back to media - */ - DISK_CACHEFLAG_WRITETHROUGH = 0x10 -}; - -/** - * \brief IOCtl name strings - */ -#define DRV_DISK_IOCTLNAMES "get_block_size","set_cache_region","set_precache" - -/** - * \name Disk Driver Utilities - * \{ - */ - -/** - * \brief Callback function type used by DrvUtil_ReadBlock and DrvUtil_WriteBlock - * \param Address Zero based block number to read - * \param Count Number of blocks to read - * \param Buffer Destination for read blocks - * \param Argument Argument provided in ::DrvUtil_ReadBlock and ::DrvUtil_WriteBlock - */ -typedef Uint (*tDrvUtil_Read_Callback)(Uint64 Address, Uint Count, void *Buffer, Uint Argument); -typedef Uint (*tDrvUtil_Write_Callback)(Uint64 Address, Uint Count, const void *Buffer, Uint Argument); - -/** - * \brief Reads a range from a block device using aligned reads - * \param Start Base byte offset - * \param Length Number of bytes to read - * \param Buffer Destination for read data - * \param ReadBlocks Callback function to read a sequence of blocks - * \param BlockSize Size of an individual block - * \param Argument An argument to pass to \a ReadBlocks - * \return Number of bytes read - */ -extern Uint64 DrvUtil_ReadBlock(Uint64 Start, Uint64 Length, void *Buffer, - tDrvUtil_Read_Callback ReadBlocks, Uint64 BlockSize, Uint Argument); -/** - * \brief Writes a range to a block device using aligned writes - * \param Start Base byte offset - * \param Length Number of bytes to write - * \param Buffer Destination for read data - * \param ReadBlocks Callback function to read a sequence of blocks - * \param WriteBlocks Callback function to write a sequence of blocks - * \param BlockSize Size of an individual block - * \param Argument An argument to pass to \a ReadBlocks and \a WriteBlocks - * \return Number of bytes written - */ -extern Uint64 DrvUtil_WriteBlock(Uint64 Start, Uint64 Length, const void *Buffer, - tDrvUtil_Read_Callback ReadBlocks, tDrvUtil_Write_Callback WriteBlocks, - Uint64 BlockSize, Uint Argument); - -/** - * \} - */ - -#endif diff --git a/Kernel/include/api_drv_joystick.h b/Kernel/include/api_drv_joystick.h deleted file mode 100644 index 3ec8597b..00000000 --- a/Kernel/include/api_drv_joystick.h +++ /dev/null @@ -1,150 +0,0 @@ -/** - * \file api_drv_joystick.h - * \brief Joystick Driver Interface Definitions - * \author John Hodge (thePowersGang) - * - * \section dirs VFS Layout - * Joystick drivers define a single VFS node, that acts as a fixed size file. - * Reads from this file return the current state, writes are ignored. - * - * \section File Structure - * The device file must begin with a valid sJoystick_FileHeader structure. - * The file header is followed by \a NAxies instances of sJoystick_Axis, one - * for each axis. - * This is followed by \a NButtons boolean values (represented using \a Uint8), - * each representing the state of a single button (where 0 is unpressed, - * 0xFF is fully depressed - intermediate values are valid in the case of - * variable-pressure buttons) - */ -#ifndef _API_DRV_JOYSTICK_H -#define _API_DRV_JOYSTICK_H - -#include - -/** - * \enum eTplJoystick_IOCtl - * \brief Common Joystick IOCtl Calls - * \extends eTplDrv_IOCtl - */ -enum eTplJoystick_IOCtl { - /** - * ioctl(..., tJoystick_Callback *Callback) - * \brief Sets the callback - * \note Can be called from kernel mode only - * - * Sets the callback that is called when a event occurs (button or axis - * change). This function pointer must be in kernel mode (although, - * kernel->user or kernel->ring3driver abstraction functions can be used) - * - * Axis events depend on the axis limit, if non-zero, the callback fires - * if the cursor position changes. Otherwise it fires when the axis value - * (cursor accelleration) changes. - */ - JOY_IOCTL_SETCALLBACK = 4, - - /** - * ioctl(..., int *Argument) - * \brief Set the argument passed as the first parameter to the callback - * \note Kernel mode only - */ - JOY_IOCTL_SETCALLBACKARG, - - /** - * ioctl(..., tJoystickNumValue *) - * \brief Set maximum value for sJoystick_Axis.CursorPos - * \note If \a Value is equal to -1 (all bits set), the value is not changed - */ - JOY_IOCTL_GETSETAXISLIMIT, - - /** - * ioctl(..., tJoystickNumValue *) - * \brief Set the value of sJoystick_Axis.CursorPos - * \note If \a Value is equal to -1 (all bits set), the value is not changed - */ - JOY_IOCTL_GETSETAXISPOSITION, - - /** - * ioctl(..., tJoystickNumValue *) - * \brief Set axis flags - * \note If \a Value is equal to -1 (all bits set), the value is not changed - * \todo Define flag values - */ - JOY_IOCTL_GETSETAXISFLAGS, - - /** - * ioctl(..., tJoystickNumValue *) - * \brief Set Button Flags - * \note If \a Value is equal to -1 (all bits set), the value is not changed - * \todo Define flag values - */ - JOY_IOCTL_GETSETBUTTONFLAGS, -}; - -/** - * \brief Symbolic names for Joystick IOCtls - */ -#define DRV_JOY_IOCTLNAMES "set_callback", "set_callback_arg", "getset_axis_limit", "getset_axis_position", \ - "getset_axis_flags", "getset_button_flags" - -// === TYPES === -typedef struct sJoystick_NumValue tJoystick_NumValue; -typedef struct sJoystick_FileHeader tJoystick_FileHeader; -typedef struct sJoystick_Axis tJoystick_Axis; - -/** - * \brief Number/Value pair for joystick IOCtls - */ -struct sJoystick_NumValue -{ - int Num; //!< Axis/Button number - int Value; //!< Value (see IOCtl defs for meaning) -}; - -/** - * \brief Callback type for JOY_IOCTL_SETCALLBACK - * \param Ident Ident value passed to JOY_IOCTL_SETCALLBACK - * - */ -typedef void (*tJoystick_Callback)(int Ident, int bIsAxis, int Num, int Delta); - -/** - * \struct sJoystick_FileHeader - * \brief Format of the joystick VFS node's first bytes - */ -struct sJoystick_FileHeader -{ - Uint16 NAxies; //!< Number of Axies - Uint16 NButtons; //!< Number of buttons -}; - -/** - * \brief Axis Definition in file data - * - * Describes the current state of an axis on the joystick. - * \a CursorPos is between zero and the current limit set by the - * JOY_IOCTL_GETSETAXISLIMIT IOCtl, while \a CurValue indicates the - * current position of the joystick axis. This is defined to be between - * \a MinValue and \a MaxValue. - */ -struct sJoystick_Axis -{ - Sint16 MinValue; //!< Minumum value for \a CurValue - Sint16 MaxValue; //!< Maximum value for \a CurValue - Sint16 CurValue; //!< Current value (joystick position) - Uint16 CursorPos; //!< Current state (cursor position) -}; - -/** - * \brief Macro to define a structure for a joystick's node - * \param _naxies Number of axies - * \param _nbuttons Number of buttons - * \note This just defines the structure, it's up to the driver to set the - * sJoystick_FileHeader.NAxies and sJoystick_FileHeader.NButtons fields. - */ -#define JOY_INFOSTRUCT(_naxies, _nbuttons) struct { \ - Uint16 NAxies, NButtons;\ - tJoystick_Axis Axies[_naxies];\ - Uint16 Buttons[_nbuttons];\ - } - -#endif diff --git a/Kernel/include/api_drv_keyboard.h b/Kernel/include/api_drv_keyboard.h deleted file mode 100644 index 57ca247e..00000000 --- a/Kernel/include/api_drv_keyboard.h +++ /dev/null @@ -1,141 +0,0 @@ -/** - * \file api_drv_keyboard.h - * \brief Keyboard Driver Interface Definitions - * \author John Hodge (thePowersGang) - * - * \section dirs VFS Layout - * Keyboard drivers consist of only a single node, which is a normal file - * node with a size of zero. All reads and writes to this node are ignored - * (tVFS_Node.Read and tVFS_Node.Write are NULL) - */ -#ifndef _API_DRV_KEYBOARD_H -#define _API_DRV_KEYBOARD_H - -#include - -/** - * \enum eTplKeyboard_IOCtl - * \brief Common Keyboard IOCtl Calls - * \extends eTplDrv_IOCtl - */ -enum eTplKeyboard_IOCtl { - /** - * ioctl(..., int *Rate) - * \brief Get/Set Repeat Rate - * \param Rate New repeat rate (pointer) - * \return Current/New Repeat rate - * - * Gets/Set the repeat rate (actually the time in miliseconds between - * repeats) of a held down key. - * If the rate is set to zero, repeating will be disabled. - */ - KB_IOCTL_REPEATRATE = 4, - - /** - * ioctl(..., int *Delay) - * \brief Get/Set Repeat Delay - * \param Delay New repeat delay (pointer) - * \return Current/New repeat delay - * - * Gets/Set the time in miliseconds before a key starts repeating - * after a key is pressed. - * Setting the delay to a negative number will cause the function to - * return -1 - */ - KB_IOCTL_REPEATDELAY, - - - /** - * ioctl(..., tKeybardCallback *Callback) - * \brief Sets the callback - * \note Can be called from kernel mode only - * - * Sets the function to be called when a key event occurs (press, release - * or repeat). This function pointer must be in kernel mode (although, - * kernel->user or kernel->ring3driver abstraction functions can be used) - * - * This function is called when a key is pressed, repeated or released. - * If the raw scancode is to be included with the key event, it should precede - * the event. - */ - KB_IOCTL_SETCALLBACK -}; - -/** - * \brief Symbolic names for Keyboard IOCtls - */ -#define DRV_KEYBAORD_IOCTLNAMES "getset_repeat_rate", "getset_repeat_delay", "set_callback" - -/** - * \brief Callback type for KB_IOCTL_SETCALLBACK - * \param Key Key symbol (Unicode or eTplKeyboard_KeyCodes) - */ -typedef void (*tKeybardCallback)(Uint32 Key); - -/** - * \name Callback key flags - * \brief Flags for values passed to the callback - * \{ - */ -#define KEY_CODEPOINT_MASK 0x3FFFFFFF -#define KEY_ACTION_MASK 0xC0000000 -#define KEY_ACTION_PRESS 0x00000000 //!< Key pressed -#define KEY_ACTION_RELEASE 0x40000000 //!< Key released -#define KEY_ACTION_REFIRE 0x80000000 //!< Repeated key -#define KEY_ACTION_RAWSYM 0xC0000000 //!< Raw key symbol (comes before a press/repeat/release) -/** - * \} - */ - -/** - * \brief Symbolic key codes - * - * These key codes represent non-pritable characters and are placed above - * the Unicode character space. - * If the using driver recieves a key code with the 31st bit set, it means - * that that key has been released. - */ -enum eTplKeyboard_KeyCodes { - KEY_ESC = 0x1B, //!< Escape Character - - KEY_NP_MASK = 0x20000000, //! Mask for non-printable characters - - /** - * \name Special Keys - * \brief These keys are usually used on their own - * \{ - */ - KEY_CAPSLOCK, - KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, - KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, - KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, - KEY_NUMLOCK, KEY_SCROLLLOCK, - KEY_HOME, KEY_END, KEY_INS, KEY_DEL, - KEY_PAUSE, KEY_BREAK, - KEY_PGUP, KEY_PGDOWN, - KEY_KPENTER, KEY_KPSLASH, KEY_KPMINUS, KEY_KPPLUS, KEY_KPSTAR, - KEY_KPHOME, KEY_KPUP, KEY_KPPGUP, KEY_KPLEFT, KEY_KP5, KEY_KPRIGHT, - KEY_KPEND, KEY_KPDOWN, KEY_KPPGDN, KEY_KPINS, KEY_KPDEL, - KEY_LWIN, KEY_RWIN, - KEY_MENU, - /** - * \} - */ - - // Modifiers - /** - * \name Modifiers - * \brief These keye usually alter the character stream sent to the user - * \{ - */ - KEY_MODIFIERS = 0x30000000, - KEY_LCTRL, KEY_RCTRL, - KEY_LALT, KEY_RALT, - KEY_LSHIFT, KEY_RSHIFT, - /** - * \} - */ -}; - - -#endif diff --git a/Kernel/include/api_drv_network.h b/Kernel/include/api_drv_network.h deleted file mode 100644 index 6cb8c349..00000000 --- a/Kernel/include/api_drv_network.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * \file api_drv_network.h - * \brief Network Interface Driver Interface Definitions - * - * \section dirs VFS Layout - * All network drivers must have the following basic VFS structure - * The root of the driver will only contain files that are named from zero - * upwards that represent the present network adapters that this driver - * controls. All VFS nodes must implement ::eTplDrv_IOCtl with - * DRV_IOCTL_TYPE returning DRV_TYPE_NETWORK. - * The adapter nodes must also implement ::eTplNetwork_IOCtl fully - * (unless it is noted in the ::eTplNetwork_IOCtl documentation that a - * call is optional) - * - * \section files Adapter Files - * \subsection Reading - * When an adapter file is read from, the driver will block the reading - * thread until a packet arrives (if there is not already an unhandled - * packet in the queue) this will then be read into the destination buffer. - * If the packet does not fit in the buffer, the end of it is discarded. - * Likewise, if the packet does not completely fill the buffer, the call - * will still read to the buffer and then return the size of the packet. - * \subsection Writing - * When an adapter is written to, the data written is encoded as a packet - * and sent, if the data is not the correct size to be sent (if the packet - * is too small, or if it is too large) -1 should be returned and the packet - * will not be sent. - */ -#ifndef _API_DRV_NETWORK_H -#define _API_DRV_NETWORK_H - -#include - -/** - * \enum eTplNetwork_IOCtl - * \brief Common Network IOCtl Calls - * \extends eTplDrv_IOCtl - */ -enum eTplNetwork_IOCtl { - /** - * ioctl(..., Uint8 *MAC[6]) - * \brief Get the MAC address of the interface - * \return 1 on success, 0 if the file is the root, -1 on error - * - * Copies the six byte Media Access Control (MAC) address of the - * adapter to the \a MAC array. - */ - NET_IOCTL_GETMAC = 4 -}; - -/** - * \brief IOCtl name strings for use with eTplDrv_IOCtl.DRV_IOCTL_LOOKUP - */ -#define DRV_NETWORK_IOCTLNAMES "get_mac_addr" - -#endif diff --git a/Kernel/include/api_drv_terminal.h b/Kernel/include/api_drv_terminal.h deleted file mode 100644 index 3142d33c..00000000 --- a/Kernel/include/api_drv_terminal.h +++ /dev/null @@ -1,166 +0,0 @@ -/** - * \file api_drv_terminal.h - * \brief Terminal Driver Interface Definitions -*/ -#ifndef _API_DRV_TERMINAL_H -#define _API_DRV_TERMINAL_H - -#include - -/** - * \brief Common Terminal IOCtl Calls - * \extends eTplDrv_IOCtl - */ -enum eTplTerminal_IOCtl { - /** - * ioctl(..., int *mode) - * \brief Get/Set the current video mode type - * \param mode Pointer to an integer with the new mode number (or NULL) - * If \a mode is non-NULL the current terminal mode is changed/updated - * to the mode indicated by \a *mode - * \note See ::eTplTerminal_Modes - * \return Current/new terminal mode - */ - TERM_IOCTL_MODETYPE = 4, - - /** - * ioctl(..., int *width) - * \brief Get/set the display width - * \param width Pointer to an integer containing the new width (or NULL) - * \return Current/new width - * - * If \a width is non-NULL the current width is updated (but is not - * applied until ::TERM_IOCTL_MODETYPE is called with \a mode non-NULL. - */ - TERM_IOCTL_WIDTH, - - /** - * ioctl(..., int *height) - * \brief Get/set the display height - * \param height Pointer to an integer containing the new height - * \return Current height - * - * If \a height is non-NULL the current height is updated (but is not - * applied until ::TERM_IOCTL_MODETYPE is called with a non-NULL \a mode. - */ - TERM_IOCTL_HEIGHT, - - /** - * ioctl(..., tTerm_IOCtl_Mode *info) - * \brief Queries the current driver about it's native modes - * \param info A pointer to a ::tTerm_IOCtl_Mode with \a ID set to - * the mode index (or NULL) - * \return Number of modes - * - * If \a info is NULL, the number of avaliable vative display modes - * is returned. These display modes will have sequential ID numbers - * from zero up to this value. - * - * \note The id field of \a info is not for use with ::TERM_IOCTL_MODETYPE - * This field is just for indexing the mode to get its information. - */ - TERM_IOCTL_QUERYMODE, - - /** - * ioctl(...) - * \brief Forces the current terminal to be shown - */ - TERM_IOCTL_FORCESHOW, - - /** - * ioctl(..., tVideo_IOCtl_Pos *pos) - * \brief Returns the current text cursor position - * \param pos New cursor position. If NULL, the position is not changed - * \return Cursor position (as X+Y*Width) - */ - TERM_IOCTL_GETSETCURSOR, - - /** - * ioctl(..., tVideo_IOCtl_Bitmap *Bmp) - * \brief Set the video cursor bitmap - * \param Bmp New bitmap (if NULL, the current bitmap is removed) - * \return Boolean failure - */ - TERM_IOCTL_SETCURSORBITMAP, -}; - -/** - * \brief Virtual Terminal Mode - * Describes a VTerm mode to the caller of ::TERM_IOCTL_QUERYMODE - */ -typedef struct sTerm_IOCtl_Mode -{ - short ID; //!< Zero Based index of mode - short DriverID; //!< Driver's ID number (from ::tVideo_IOCtl_Mode) - Uint16 Height; //!< Height - Uint16 Width; //!< Width - Uint8 Depth; //!< Bits per cell - struct { - unsigned bText: 1; //!< Text Mode marker - unsigned unused: 7; - }; -} tTerm_IOCtl_Mode; - -/** - * \brief Terminal Modes - */ -enum eTplTerminal_Modes { - /** - * \brief UTF-8 Text Mode - * Any writes to the terminal file are treated as UTF-8 encoded - * strings and reads will also return UTF-8 strings. - */ - TERM_MODE_TEXT, - - /** - * \brief 32bpp Framebuffer - * Writes to the terminal file will write to the framebuffer. - * Reads will return UTF-32 characters - */ - TERM_MODE_FB, - - /** - * \brief 32bpp 2D Accellerated mode - * Writes to the terminal file will be read as a command stream - * defined in ::eTplTerminal_2D_Commands - */ - TERM_MODE_2DACCEL, - - /** - * \brief OpenGL 2D/3D - * Writes to the terminal file will send 3D commands - * Reads will return UTF-32 characters - * \note May or may not stay in the spec - */ - TERM_MODE_3D, - - /** - * \brief Number of terminal modes - */ - NUM_TERM_MODES -}; - -/** - * \brief 2D Command IDs - * \todo Complete this structure - * - * Command IDs for when the terminal type is eTplTerminal_Modes.TERM_MODE_2DACCEL - */ -enum eTplTerminal_2D_Commands -{ - /** - * \brief No Operation - Used for padding - */ - TERM_2DCMD_NOP, - - /** - * (Uint16 X, Y, W, H, Uint32 Data[]) - * \brief Blits a bitmap to the display - * \param X,Y Coordinates of Top-Left corner - * \param W,H Dimensions - * \param Data 32-bpp pixel data - */ - TERM_2DCMD_PUSH -}; - -#endif diff --git a/Kernel/include/api_drv_video.h b/Kernel/include/api_drv_video.h deleted file mode 100644 index ff9c395b..00000000 --- a/Kernel/include/api_drv_video.h +++ /dev/null @@ -1,484 +0,0 @@ -/** - * \file api_drv_video.h - * \brief Video Driver Interface Definitions - * \note For AcessOS Version 1 - * - * Video drivers extend the common driver interface api_drv_common.h - * and must support the IOCtl numbers defined in this file to be - * compatable with Acess (drivers may implement more IOCtls above - * DRV_IOCTL_USERMIN). - * - * \section IOCtls - * As said, a compatable driver must implement these calls correctly, - * but they may choose not to allow direct user access to the framebuffer. - * - * \section Screen Contents - * Writes to the driver's file while in component colour modes - * must correspond to a change of the contents of the screen. The framebuffer - * must start at offset 0 in the file. - * Reading from the screen must either return zero, or read from the - * framebuffer. - * - * \section Mode Support - * All video drivers must support text output for every resolution (hardware - * accelerated or software), and at least the _BLIT and _FILL 2D operations - * (these may be implemented specifically for text mode). - */ -#ifndef _API_DRV_VIDEO_H -#define _API_DRV_VIDEO_H - -#include - -/** - * \enum eTplVideo_IOCtl - * \brief Common Video IOCtl Calls - * \extends eTplDrv_IOCtl - */ -enum eTplVideo_IOCtl { - /** - * ioctl(..., int *mode) - * \brief Get/Set Mode - * \return Current mode ID or -1 on error - * - * If \a mode is non-NULL, the current video mode is set to \a *mode. - * This updated ID is then returned to the user. - */ - VIDEO_IOCTL_GETSETMODE = 4, - - /** - * ioctl(..., tVideo_IOCtl_Mode *info) - * \brief Find a matching mode - * \return 1 if a mode was found, 0 otherwise - * - * Using avaliable modes matching the \a bpp and \a flags fields - * set the \a id, \a width and \a heights fields to the closest - * matching mode. - */ - VIDEO_IOCTL_FINDMODE, - - /** - * ioctl(..., tVideo_IOCtl_Mode *info) - * \brief Get mode info - * \return 1 if the mode exists, 0 otherwise - * - * Set \a info's fields to the mode specified by the \a id field. - */ - VIDEO_IOCTL_MODEINFO, - - /** - * ioctl(..., int *NewFormat) - * \brief Switches between Text, Framebuffer and 3D modes - * \param NewFormat Pointer to the new format code (see eTplVideo_BufFormats) - * \return Original format - * - * Enabes and disables the video text mode, changing the behavior of - * writes to the device file. - */ - VIDEO_IOCTL_SETBUFFORMAT, - - /** - * ioctl(..., tVideo_IOCtl_Pos *pos) - * \brief Sets the cursor position - * \return Boolean success - * - * Set the text mode cursor position (if it is supported) - * If the \a pos is set to (-1,-1) the cursor is hidden, otherwise - * \a pos MUST be within the current screen size (as given by the - * current mode's tVideo_IOCtl_Mode.width and tVideo_IOCtl_Mode.height - * fields). - */ - VIDEO_IOCTL_SETCURSOR, - - /** - * ioctl(..., tVideo_IOCtl_Bitmap *Image) - * \brief Sets the cursor image - * \return Boolean success - * - * Sets the graphics mode cursor image - */ - VIDEO_IOCTL_SETCURSORBITMAP -}; - -/** - * \brief Symbolic names for Video IOCtls (#4 onwards) - */ -#define DRV_VIDEO_IOCTLNAMES "getset_mode", "find_mode", "mode_info", "set_buf_format", "set_cursor", "set_cursor_bitmap" - -/** - * \brief Mode Structure used in IOCtl Calls - * - * Defines a video mode supported by (or requested of) this driver (depending - * on what ioctl call is used) - */ -typedef struct sVideo_IOCtl_Mode -{ - short id; //!< Mode ID - Uint16 width; //!< Width - Uint16 height; //!< Height - Uint8 bpp; //!< Bits per pixel - Uint8 flags; //!< Mode Flags (none defined, should be zero) -} tVideo_IOCtl_Mode; - -/** - * \brief Buffer Format Codes - */ -enum eTplVideo_BufFormats -{ - /** - * \brief Text Mode - * - * The device file presents itself as an array of ::tVT_Char - * each describing a character cell on the screen. - * These cells are each \a giVT_CharWidth pixels wide and - * \a giVT_CharHeight high. - */ - VIDEO_BUFFMT_TEXT, - /** - * \brief Framebuffer Mode - * - * The device file presents as an array of 32-bpp pixels describing - * the entire screen. The format of a single pixel is in xRGB format - * (top 8 bits ignored, next 8 bits red, next 8 bits green and - * the bottom 8 bits blue) - */ - VIDEO_BUFFMT_FRAMEBUFFER, - /** - * \brief 2D Accelerated Mode - * - * The device file acts as a character device, accepting a stream of - * commands described in eTplVideo_2DCommands when written to. - */ - VIDEO_BUFFMT_2DSTREAM, - /** - * \brief 3D Accelerated Mode - * - * The device file acts as a character device, accepting a stream of - * commands described in eTplVideo_3DCommands when written to. - */ - VIDEO_BUFFMT_3DSTREAM -}; - -/** - * \brief 2D Accellerated Video Commands - * - * Commands passed in the command stream for ::VIDEO_BUFFMT_2DSTREAM - */ -enum eTplVideo_2DCommands -{ - /** - * \brief No Operation - */ - VIDEO_2DOP_NOP, - /** - * \brief Fill a region - * \param X Uint16 - Leftmost pixels of the region - * \param Y Uint16 - Topmost pixels of the region - * \param W Uint16 - Width of the region - * \param H Uint16 - Height of the region - * \param Colour Uint32 - Value to fill with - */ - VIDEO_2DOP_FILL, - /** - * \brief Copy a region from one part of the framebuffer to another - * \param DestX Uint16 - Leftmost pixels of the destination - * \param DestY Uint16 - Topmost pixels of the destination - * \param SrcX Uint16 - Leftmost pixels of the source - * \param SrcY Uint16 - Topmost pixels of the source - * \param Width Uint16 - Width of the region - * \param Height Uint16 - Height of the region - */ - VIDEO_2DOP_BLIT, - - - /** - * \brief Copy a region from video memory to the framebuffer - */ - VIDEO_2DOP_BLITBUF, - - /** - * \brief Copy and scale a region from video memory to the framebuffer - */ - VIDEO_2DOP_BLITSCALEBUF, - - NUM_VIDEO_2DOPS -}; - -/** - * \brief Describes a position in the video framebuffer - */ -typedef struct sVideo_IOCtl_Pos -{ - Sint16 x; //!< X Coordinate - Sint16 y; //!< Y Coordinate -} tVideo_IOCtl_Pos; - -/** - * \brief Bitmap object (out of band image) - */ -typedef struct sVideo_IOCtl_Bitmap -{ - Sint16 W; //!< Width of image - Sint16 H; //!< Height of image - Sint16 XOfs; //!< X Offset of center - Sint16 YOfs; //!< Y Offset of center - Uint32 Data[]; //!< Image data (ARGB array) -} tVideo_IOCtl_Bitmap; - -/** - * \brief Virtual Terminal Representation of a character - */ -typedef struct sVT_Char -{ - Uint32 Ch; //!< UTF-32 Character - union { - struct { - Uint16 BGCol; //!< 12-bit Foreground Colour - Uint16 FGCol; //!< 12-bit Background Colour - }; - Uint32 Colour; //!< Compound colour for ease of access - }; -} tVT_Char; - -/** - * \name Basic builtin colour definitions - * \{ - */ -#define VT_COL_BLACK 0x0000 -#define VT_COL_GREY 0x0888 -#define VT_COL_LTGREY 0x0CCC -#define VT_COL_WHITE 0x0FFF -/** - * \} - */ - -//! \brief Defines the width of a rendered character -extern int giVT_CharWidth; -//! \brief Defines the height of a rendered character -extern int giVT_CharHeight; -/** - * \name Font rendering - * \{ - */ -/** - * \brief Driver helper that renders a character to a buffer - * \param Codepoint Unicode character to render - * \param Buffer Buffer to render to - * \param Depth Bit depth of the destination buffer - * \param Pitch Number of bytes per line - * \param BGC 32-bit Background Colour - * \param FGC 32-bit Foreground Colour - * - * This function is provided to help video drivers to support a simple - * text mode by keeping the character rendering abstracted from the driver, - * easing the driver development and reducing code duplication. - */ -extern void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Depth, int Pitch, Uint32 BGC, Uint32 FGC); -/** - * \fn Uint32 VT_Colour12to24(Uint16 Col12) - * \brief Converts a colour from 12bpp to 24bpp - * \param Col12 12-bpp input colour - * \return Expanded 32-bpp (24-bit colour) version of \a Col12 - */ -extern Uint32 VT_Colour12to24(Uint16 Col12); -/** - * \brief Converts a colour from 12bpp to 14bpp - * \param Col12 12-bpp input colour - * \return 15 bits per pixel value - */ -extern Uint16 VT_Colour12to15(Uint16 Col12); -/** - * \brief Converts a colour from 12bpp to 32bpp - * \param Col12 12-bpp input colour - * \param Depth Desired bit depth - * \return \a Depth bit number, denoting Col12 - * - * Expands the source colour into a \a Depth bits per pixel representation. - * The colours are expanded with preference to Green, Blue and Red in that order - * (so, green gets the first spare pixel, blue gets the next, and red never gets - * the spare). \n - * The final bit of each component is used to fill the lower bits of the output. - */ -extern Uint32 VT_Colour12toN(Uint16 Col12, int Depth); -/** - * \} - */ - -/** - * \brief Maximum cursor width for using the DrvUtil software cursor - */ -#define DRVUTIL_MAX_CURSOR_W 32 -/** - * \brief Maximum cursor width for using the DrvUtil software cursor - */ -#define DRVUTIL_MAX_CURSOR_H 32 - -/** - * \brief Framebuffer information used by all DrvUtil_Video functions - */ -typedef struct sDrvUtil_Video_BufInfo -{ - /** - * \name Framebuffer state - * \{ - */ - /** - * \brief Framebuffer virtual address - */ - void *Framebuffer; - /** - * \brief Bytes between the start of each line - */ - int Pitch; - /** - * \brief Number of pixels in each line - */ - short Width; - /** - * \brief Total number of lines - */ - short Height; - /** - * \brief Bit depth of the framebuffer - */ - short Depth; - /* - * \} - */ - - /** - * \brief Buffer write format - */ - short BufferFormat; - - /** - * \name Software cursor controls - * \{ - */ - /** - * \brief X coordinate of the cursor - */ - short CursorX; - /** - * \brief Y coordinate of the cursor - */ - short CursorY; - - /** - * \brief Cursor bitmap - */ - tVideo_IOCtl_Bitmap *CursorBitmap; - /* - * \} - */ - - /* - * \name Internal fields - * \{ - */ - - /** - * \brief Buffer to store the area under the cursor - */ - void *CursorSaveBuf; - - int CursorReadX; //!< X offset in cursor bitmap corresponding to \a CursorDestX - int CursorReadY; //!< Same as \a CursorReadX but for Y - int CursorRenderW; //!< Width of rendered cursor - int CursorRenderH; //!< Height of rendered cursor - int CursorDestX; //!< X coordinate Destination for rendered cursor - int CursorDestY; //!< Y coordinate destination for rendered cursor - - /* - * \} - */ -} tDrvUtil_Video_BufInfo; - -/** - * \brief Handlers for eTplVideo_2DCommands - */ -typedef struct sDrvUtil_Video_2DHandlers -{ - /** - * \brief No Operation, Ignored - * \see VIDEO_2DOP_NOP - */ - void *Nop; - /** - * \brief Fill a buffer region - * \param X Lefthand edge - * \param Y Top edge - * \param W Width - * \param H Height - * \param Colour Colour to fill with - * \see VIDEO_2DOP_FILL - */ - void (*Fill)(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour); - /** - * \brief Fill a buffer region - * \param DestX Lefthand edge of destination - * \param DestY Top edge of destination - * \param SrcX Lefthand edge of source - * \param SrcY Top edge of source - * \param W Width - * \param H Height - * \see VIDEO_2DOP_BLIT - */ - void (*Blit)(void *Ent, Uint16 DestX, Uint16 DestY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H); -} tDrvUtil_Video_2DHandlers; - -/** - * \brief Handle a 2D operation stream for a driver - * \param Ent Value to pass to handlers - * \param Buffer Stream buffer - * \param Length Length of stream - * \param Handlers Handlers to use for the stream - * \param SizeofHandlers Size of \a tDrvUtil_Video_2DHandlers according - * to the driver. Used as version control and error avoidence. - */ -extern int DrvUtil_Video_2DStream(void *Ent, const void *Buffer, int Length, - tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers); - -/** - * \brief Perform write operations to a LFB - * \param FBInfo Framebuffer descriptor, see type for details - * \param Offset Offset provided by VFS call - * \param Length Length provided by VFS call - * \param Src Data from VFS call - * \return Number of bytes written - * - * Handles all write modes in software, using the VT font calls for rendering. - * \note Calls the cursor clear and redraw if the cursor area is touched - */ -extern int DrvUtil_Video_WriteLFB(tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, const void *Src); - -/** - * \name Software cursor rendering - * \{ - */ -/** - * \brief Set the cursor bitmap for a buffer - * \param Buf Framebuffer descriptor - * \param Bitmap New cursor bitmap - */ -extern int DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap); -/** - * \brief Render the cursor at (\a X, \a Y) - * \param Buf Framebuffer descriptor, see type for details - * \param X X coord of the cursor - * \param Y Y coord of the cursor - */ -extern void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y); -/** - * \brief Removes the rendered cursor from the screen - * \param Buf Framebuffer descriptor, see type for details - */ -extern void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf); - -/** - * \brief Text mode cursor image - */ -extern tVideo_IOCtl_Bitmap gDrvUtil_TextModeCursor; -/** - * \} - */ -#endif diff --git a/Kernel/include/apidoc/arch_x86.h b/Kernel/include/apidoc/arch_x86.h deleted file mode 100644 index 7818c8c5..00000000 --- a/Kernel/include/apidoc/arch_x86.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * \file apidoc/arch_x86.h - * \brief x86(-64) Specific Functions - * \author John Hodge (thePowersGang) - * - * \section toc Table of Contents - * - \ref portio "Port IO" - * - \ref dma "DMA - Direct Memory Access" - * - * \section portio Port IO - * The x86 architecture has two memory spaces, the first is the system - * memory accessable using standard loads and stores. The second is the - * 16-bit IO Bus. This bus is accessed using the \a in and \a out opcodes - * and is used to configure devices attached to the system. - * A driver should not use \a in and \a out directly, but instead use - * the provided \a in* and \a out* functions to access the IO Bus. - * This allows the kernel to run a driver in userspace if requested without - * the binary needing to be altered. - * - * \section dma DMA - Direct Memory Access - */ - -/** - * \name IO Bus Access - * \{ - */ -extern Uint8 inb(Uint16 Port); //!< Read 1 byte from the IO Bus -extern Uint16 inw(Uint16 Port); //!< Read 2 bytes from the IO Bus -extern Uint32 inl(Uint16 Port); //!< Read 4 bytes from the IO Bus -extern Uint64 inq(Uint16 Port); //!< Read 8 bytes from the IO Bus\ - -extern void outb(Uint16 Port, Uint8 Value); //!< Write 1 byte to the IO Bus -extern void outw(Uint16 Port, Uint16 Value); //!< Write 2 bytes to the IO Bus -extern void outl(Uint16 Port, Uint32 Value); //!< Write 4 bytes to the IO Bus -extern void outq(Uint16 Port, Uint64 Value); //!< Write 8 bytes to the IO Bus -/** - * \} - */ diff --git a/Kernel/include/apidoc_mainpage.h b/Kernel/include/apidoc_mainpage.h deleted file mode 100644 index 0060bafa..00000000 --- a/Kernel/include/apidoc_mainpage.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * \file apidoc_mainpage.h - * \brief API Documentation Home Page - * \author John Hodge (thePowersGang) - * - * \mainpage Acess 2 Kernel API Documentation - * - * \section intro Introduction - * These documents attempt to describe the standard Acess 2 (and hopefully - * future versions) Kernel mode API. - * The documentation covers filesystem drivers, binary formats and the - * various device driver interface standards. - * - * \section index "Sections" - * - \ref modules.h "Module Definitions" - * - Describes how a module is defined in Acess - * - \ref binary.h "Binary Formats" - * - Explains how to register a new binary format with the kernel - * - \ref vfs.h "VFS - The Virtual File System" - * - The VFS is the core of Acess's driver architecture - * - \ref drivers "Device Drivers" - * - Describes how drivers should use the VFS to expose themselves to the - * user. - * - Drivers for specific types of hardware must behave in the specific - * way described here. - * - * \page drivers Device Drivers - * - * \section toc Contents - * - \ref drvintro "Introduction" - * - \ref drv_misc "Miscelanious Devices" - * - \ref drv_video "Video Drivers" - * - * \section drvintro Introduction - * All Acess2 device drivers communicate with user-level (and other parts - * of the greater kernel) via the VFS. They register themselves in a similar - * way to how filesystem drivers do, however instead of registering with - * the VFS core, they register with a special filesystem driver called the - * \ref fs_devfs.h "Device Filesystem" (devfs). The DevFS provides the - * ::DevFS_AddDevice function that takes a ::tDevFS_Driver structure as - * an agument. This structure specifies the driver's name and its root - * VFS node. This node is used to provide the user access to the - * driver's functions via IOCtl calls and Reading or Writing to the driver - * file. Drivers are also able to expose a readonly buffer by using - * \ref fs_sysfs.h "ProcDev", usually to provide state information or device - * capabilities for the the user. - * - * The device driver interfaces are all based on the core specifcation - * in api_drv_common.h (Common Device Driver definitions). - * The following subsections define the various specific types of driver - * interfaces. These definitions only define the bare minimum of what the - * driver must implement, if the driver author so wants to, they can add - * IOCtl calls and/or files (where allowed by the type specifcation) to - * their device's VFS layout. - * - * \subsection drv_misc Miscelanious Devices - * If a device type does not have a specifcation for it, the driver can - * identify itself as a miscelanious device by returning DRV_TYPE_MISC - * from \ref DRV_IOCTL_TYPE. - * A misc device must at least implement the IOCtl calls defined in the - * \ref api_drv_common.h "Common Device Driver definitions", allowing it - * to be identified easily by the user and for interfacing programs to - * utilise the DRV_IOCTL_LOOKUP call. - * - * \subsection drv_video Video Devices - * Video drivers are based on a framebuffer model (unless in 3D mode, - * which is not yet fully standardised, so should be ignored). - * The driver will contain only one VFS node, that exposes the video - * framebuffer (this may not be the true framebuffer, to allow for double-buffering) - * to the user. See the full documentation in api_drv_video.h for the - * complete specifcation. - * - * \subsection drv_disk Disk/Storage Devices - * Storage devices present themselves as a linear collection of bytes. - * Reads and writes to the device need not be aligned to the stated block - * size, but it is suggested that users of a storage device file align - * their accesses to block boundaries. - * The functions DrvUtil_ReadBlock and DrvUtil_WriteBlock are provided - * to storage drivers to assist in handling non-alinged reads and writes. - * - * \see api_drv_common.h Common Spec. - * \see api_drv_video.h Video Device Spec. - * \see api_drv_keyboard.h Keyboard Device Spec. - * \see api_drv_disk.h Disk/Storage Device Spec. - * \see api_drv_network.h Network Device Spec. - * \see api_drv_terminal.h Virtual Terminal Spec. - */ diff --git a/Kernel/include/binary.h b/Kernel/include/binary.h deleted file mode 100644 index c94a1b09..00000000 --- a/Kernel/include/binary.h +++ /dev/null @@ -1,173 +0,0 @@ -/** - * \file binary.h - * \brief Binary Loader Definitions - * \author John Hodge (thePowersGang) - */ -#ifndef _BINARY_H -#define _BINARY_H - -// === TYPES === -/** - * \brief Representation of a section in a binary file - * - * Tells the binary loader where the page data resides on disk and where - * to load it to (relative to the binary base). Once the data is read, - * the \a Physical field contains the physical address of the page. - */ -typedef struct sBinarySection -{ - Uint64 Offset; //!< File offset of the section - tVAddr Virtual; //!< Virtual load address - size_t FileSize; //!< Number of bytes to load from the file - size_t MemSize; //!< Number of bytes in memory - Uint Flags; //!< Load Flags -} tBinarySection; - -/** - * \brief Flags for ::tBinarySection.Flags - * \name Binary Section Flags - * \{ - */ -//! \brief Read-only -#define BIN_SECTFLAG_RO 0x0001 -//! \brief Executable -#define BIN_SECTFLAG_EXEC 0x0002 -/** - * \} - */ - -/** - * \brief Defines a binary file - * - * This structure defines and maintains the state of a binary during and - * after loading. - * Before the binary is loaded into memory (when it has just been returned - * from tBinaryType.Load) the \a Pages array will contain the file offsets - * to the page data in the \a Physical fields (or -1 for uninitialised - * data) and the \a Size fields define how much data is stored in-file - * for the page (to allow partial pages to be loaded from disk) - * Once the binary is loaded (NOTE: Drivers do not need to know about this, - * it is here for information only) the \a Physical fields now contain the - * physical addresses of the pages filled with the data. The \a Virtual - * fields contain the preferred virtual address of the pages (a given - * process may have these pages mapped to a different location). - */ -typedef struct sBinary -{ - struct sBinary *Next; //!< Pointer used by the kernel - - tMount MountID; //!< Mount ID - tInode Inode; //!< Inode (Used for fast reopen) - - /** - * \brief Interpreter used to load the file - * \note This can be either requested by the individual file, or a per-driver option - */ - const char *Interpreter; - /** - * \brief Entrypoint of the binary (at requested base); - */ - tVAddr Entry; - /** - * \brief File's requested load base - */ - tVAddr Base; - /** - * \brief Number of times this binary has been mapped - */ - int ReferenceCount; - /** - * \brief Number of sections defined in the file - */ - int NumSections; - /** - * \brief Array of sections defined by this binary - * \note Contains \a NumSections entries - */ - tBinarySection LoadSections[]; -} tBinary; - -/** - * \brief Binary type definition - * - * This structure is used to define a loader for a specific binary type - * so that the kernel's core binary loader can understand that type. - * The tBinaryType.Relocate and tBinaryType.GetSymbol need only be non-NULL - * if the binary type is to be used for kernel modules, otherwise it will - * only be able to load binaries for user space. - */ -typedef struct sBinaryType -{ - /** - * \brief Pointer used by the kernel - * \note Should not be touched by the driver (initialise to NULL) - */ - struct sBinaryType *Next; - /** - * \brief Identifying DWord - * - * If he first 32-bits of the file match this value (when ANDed with - * tBinaryType.Mask), this binary loader will be used to load the file. - */ - Uint32 Ident; - Uint32 Mask; //!< Mask value for tBinaryType.Ident - const char *Name; //!< Name of this executable type (for debug purpouses) - /** - * \brief Read a binary from a file - * \param FD VFS File handle to file to load - * \return Pointer to a ::tBinary that describes how to load the file - * - * This function reads a binary file and returns a ::tBinary pointer - * that tells the core binary loader how to read the data from the file - * and where to map it to. - */ - tBinary *(*Load)(int FD); - - /** - * \brief Prepares a mapped binary for execution at this address - * \param Base Binary loaded in memory - * \return Boolean Success - * \note This pointer can be NULL, but then the binary cannot be used - * to load a kernel module. - * - * tBinaryType.Relocate takes a binary that was loaded according to - * tBinaryType.Load and prepares it to run at the address it is - * loaded to, attempting to satisfy any external unresolved symbols - * required, if a symbol cannot be located, the function will return - * zero. - */ - int (*Relocate)(void *Base); - - /** - * \brief Gets a symbol's address from a loaded binary - * \note The binary pointed to by \a Base may not have been through - * tBinaryType.Relocate at this time, so the driver should - * accomodate this. - */ - int (*GetSymbol)(void *Base, const char *Name, Uint *Dest); -} tBinaryType; - -/** - * \brief Registers an interpreter path with the binary loader - * \param Path Path to the requested interpreter (need not be a "true" path) - * \return Pointer to the cached string - * - * Speeds up checking if the intepreter is loaded in the kernel by allowing - * the search to use pointer comparisons instead of string comparisons. - */ -extern char *Binary_RegInterp(char *Path); - -/** - * \brief Registers a binary type with the kernel's loader - * \param Type Pointer to the loader's type structure - * \return Boolean success - * \note The structure \a Type must be persistant (usually it will be a - * constant global variable) - * - * This function tells the binary loader about a new file type, and gives - * it the functions to read the type into a ::tBinary structure, relocate - * it and to find the value of symbols defined within the binary. - */ -extern int Binary_RegisterType(tBinaryType *Type); - -#endif diff --git a/Kernel/include/binary_ext.h b/Kernel/include/binary_ext.h deleted file mode 100644 index b2fc89d8..00000000 --- a/Kernel/include/binary_ext.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Acess 2 - * binary_ext.h - * - Exported Symbols from the binary loader - */ -#ifndef _BINARY_EXT_H -#define _BINARY_EXT_H - -// === FUNCTIONS === -extern void *Binary_LoadFile(const char *Path); -extern void *Binary_LoadKernel(const char *Path); -extern Uint Binary_Relocate(void *Mem); -extern void Binary_Unload(void *Base); -extern int Binary_GetSymbol(const char *Name, Uint *Dest); -extern Uint Binary_FindSymbol(void *Base, const char *Name, Uint *Val); - -#endif diff --git a/Kernel/include/drv_pci.h b/Kernel/include/drv_pci.h deleted file mode 100644 index 33c4a3ec..00000000 --- a/Kernel/include/drv_pci.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * \file drv_pci.h - * \brief PCI Bus Driver - * \author John Hodge (thePowersGang) - */ -#ifndef _DRV_PCI_H -#define _DRV_PCI_H - -/** - * \brief PCI Class Codes - */ -enum ePCIClasses -{ - PCI_CLASS_PRE20 = 0x00, - PCI_CLASS_STORAGE, - PCI_CLASS_NETWORK, - PCI_CLASS_DISPLAY, - PCI_CLASS_MULTIMEDIA, - PCI_CLASS_MEMORY, - PCI_CLASS_BRIDGE, - PCI_CLASS_COMM, - PCI_CLASS_PREPH, - PCI_CLASS_INPUT, - PCI_CLASS_DOCKING, - PCI_CLASS_PROCESSORS, - PCI_CLASS_SERIALBUS, - PCI_CLASS_MISC = 0xFF -}; - -enum ePCIOverClasses -{ - PCI_OC_PCIBRIDGE = 0x060400, - PCI_OC_SCSI = 0x010000 -}; - -typedef int tPCIDev; - -/** - * \brief Count PCI Devices - * - * Counts the number of devices with specified Vendor and Device IDs - */ -extern int PCI_CountDevices(Uint16 VendorID, Uint16 DeviceID); -extern tPCIDev PCI_GetDevice(Uint16 VendorID, Uint16 DeviceID, int index); -/** - * \param ClassCode (Class:SubClass:PI) - */ -extern tPCIDev PCI_GetDeviceByClass(Uint32 ClassCode, Uint32 Mask, tPCIDev prev); - -extern int PCI_GetDeviceInfo(tPCIDev id, Uint16 *Vendor, Uint16 *Device, Uint32 *Class); -extern int PCI_GetDeviceVersion(tPCIDev id, Uint8 *Revision); -extern int PCI_GetDeviceSubsys(tPCIDev id, Uint16 *SubsystemVendor, Uint16 *SubsystemID); - -extern Uint32 PCI_ConfigRead(tPCIDev id, int Offset, int Size); -extern void PCI_ConfigWrite(tPCIDev id, int Offset, int Size, Uint32 Value); - -extern Uint8 PCI_GetIRQ(tPCIDev id); -extern Uint32 PCI_GetBAR(tPCIDev id, int BAR); -//extern Uint16 PCI_AssignPort(tPCIDev id, int bar, int count); - -#endif diff --git a/Kernel/include/drv_pci_int.h b/Kernel/include/drv_pci_int.h deleted file mode 100644 index f1681417..00000000 --- a/Kernel/include/drv_pci_int.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * drv_pci_int.h - * - PCI internal definitions - */ -#ifndef _DRV_PCI_INT_H -#define _DRV_PCI_INT_H - -#include - -extern Uint32 PCI_CfgReadDWord(Uint32 Addr); -extern void PCI_CfgWriteDWord(Uint32 Addr, Uint32 data); - -#endif - diff --git a/Kernel/include/errno.h b/Kernel/include/errno.h deleted file mode 100644 index 3652f19f..00000000 --- a/Kernel/include/errno.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Acess2 - * errno.h - */ -#ifndef _ERRNO_H -#define _ERRNO_H - -enum eErrorNums -{ - EOK, - - ENOSYS, // Invalid Instruction - EINVAL, // Invalid Paramater - ENOMEM, // No free memory - EACCES, // Not permitted - ENOTFOUND, // Item not found - EREADONLY, // Read only - ENOTIMPL, // Not implemented - ENOENT, // No entry? - EEXIST, // Already exists - ENFILE, // Too many open files - ENOTDIR, // Not a directory - - EALREADY, // Operation was a NOP - EINTERNAL, // Internal Error - - NUM_ERRS -}; - -#endif diff --git a/Kernel/include/events.h b/Kernel/include/events.h deleted file mode 100644 index 675c3dbc..00000000 --- a/Kernel/include/events.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * events.h - * - Thread Events - */ -#ifndef _EVENTS_H_ -#define _EVENTS_H_ - -#include - -#define THREAD_EVENT_VFS 0x00000001 -#define THREAD_EVENT_IPCMSG 0x00000002 -#define THREAD_EVENT_SIGNAL 0x00000004 -#define THREAD_EVENT_TIMER 0x00000008 - -// === FUNCTIONS === -extern void Threads_PostEvent(tThread *Thread, Uint32 EventMask); -extern Uint32 Threads_WaitEvents(Uint32 EventMask); - -#endif - diff --git a/Kernel/include/fs_devfs.h b/Kernel/include/fs_devfs.h deleted file mode 100644 index c099fc6c..00000000 --- a/Kernel/include/fs_devfs.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * \file fs_devfs.h - * \brief Acess Device Filesystem interface - * \author John Hodge (thePowersGang) - */ -#ifndef _FS_DEVFS_H -#define _FS_DEVFS_H -#include - -// === TYPES === -/** - * \brief DevFS driver definition - */ -typedef struct sDevFS_Driver -{ - struct sDevFS_Driver *Next; //!< Set to NULL by drivers (used internally) - const char *Name; //!< Name of the driver file/folder (must be unique) - tVFS_Node RootNode; //!< Root node of driver -} tDevFS_Driver; - -// === FUNCTIONS === -/** - * \fn int DevFS_AddDevice(tDevFS_Driver *Device) - * \brief Registers a device in the Device Filesystem - * \param Device Pointer to a persistant structure that represents the driver - * \return Boolean success - */ -extern int DevFS_AddDevice(tDevFS_Driver *Device); - -/** - * \brief Unregisters a device with the Device Filesystem - */ -extern void DevFS_DelDevice(tDevFS_Driver *Device); - -#endif diff --git a/Kernel/include/fs_sysfs.h b/Kernel/include/fs_sysfs.h deleted file mode 100644 index aac64d91..00000000 --- a/Kernel/include/fs_sysfs.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Acess2 - * - SysFS Export Header - */ -/** - * \file fs_sysfs.h - * \brief ProcDev/SysFS Interface - * \author John Hodge (thePowersGang) - * - * - */ -#ifndef _FS_SYSFS_H_ -#define _FS_SYSFS_H_ - -/** - * \brief Registers a file in the SysFS tree - * \param Path Path relative to the SysFS root (no .. or .) - * \param Data File buffer address - * \param Length Length of the file buffer - * \return An ID number to refer to the file, or -1 on error - * \note \a Data must be maintained until ::SysFS_UpdateFile is called - * with a different buffer, or ::SysFS_RemoveFile is called. - */ -extern int SysFS_RegisterFile(const char *Path, const char *Data, int Length); - -/** - * \brief Updates the size/pointer associated with a SysFD file - * \param ID Number returned by ::SysFS_RegisterFile - * \param Data New buffer address - * \param Length New length of the file - * \return Boolean Success - */ -extern int SysFS_UpdateFile(int ID, const char *Data, int Length); - -/** - * \brief Removes a file from the SysFS tree - * \param ID Number returned by ::SysFS_RegisterFile - */ -extern int SysFS_RemoveFile(int ID); - -#endif diff --git a/Kernel/include/hal_proc.h b/Kernel/include/hal_proc.h deleted file mode 100644 index 0f21a1c7..00000000 --- a/Kernel/include/hal_proc.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Acess2 - * - By John Hodge (thePowersGang) - * - * include/hal_proc.h - * - HAL Process management functions - * - */ -#ifndef _HAL_PROC_H_ -#define _HAL_PROC_H_ -/** - * \file hal_proc.h - * \brief Achitecture defined thread/process management functions - */ - -#include - -/** - * \brief Initialise the architecture dependent side of threading - */ -extern void ArchThreads_Init(void); -/** - * \brief Start preemptive multithreading (if needed) - */ -extern void Proc_Start(void); -/** - * \brief Called just before a thread is freed - */ -extern void Proc_ClearThread(tThread *Thread); -/** - * \brief Called just before a process is freed - */ -extern void Proc_ClearProcess(tProcess *Process); -/** - * \brief Get the ID of this CPU - * \return Zero based CPU ID - */ -extern int GetCPUNum(void); -/** - * \brief Create a copy of the current process - * \param Flags Options for the clone - * \return ID of the new thread/process - */ -extern tTID Proc_Clone(Uint Flags); -/** - * \brief Create a new kernel thread for the process - * \param Fnc Thread root function - * \param Ptr Argument to pass the root function - * \return ID of new thread - */ -extern tTID Proc_NewKThread( void (*Fnc)(void*), void *Ptr ); -/** - * \brief Start a user task - * \param Entrypoint User entrypoint - * \param Base Base of executable (argument for ld-acess) - * \param ArgC Number of arguments when the program was invoked - * \param ArgV Heap allocated arguments and environment (two NULL terminated lists) - * \param DataSize Size of the \a ArgV buffer in bytes - * \note This function should free \a ArgV - */ -extern void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) NORETURN; -/** - * \brief Call the fault handler for a thread - * \param Thread Thread that is at fault :) - */ -extern void Proc_CallFaultHandler(tThread *Thread); -/** - * \brief Dump the CPU state for a thread - */ -extern void Proc_DumpThreadCPUState(tThread *Thread); -/** - * \brief Select a new task and run it, suspending this - */ -extern void Proc_Reschedule(void); - -/** - * \brief Clear the user's memory space back to the minimum required to run - */ -extern void MM_ClearUser(void); -/** - * \brief Dump the address space to the debug channel - * \param Start First address - * \param End Last address - */ -extern void MM_DumpTables(tVAddr Start, tVAddr End); - -/** - * \brief Check if a buffer is valid (and all user if originally user) - * \param Addr Base address - * \param Size Size of the buffer in bytes - * \return Boolean valid (0: invalid, non-0: Valid) - */ -extern int MM_IsValidBuffer(tVAddr Addr, size_t Size); -#endif diff --git a/Kernel/include/heap.h b/Kernel/include/heap.h deleted file mode 100644 index b058e8b4..00000000 --- a/Kernel/include/heap.h +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Acess2 Kernel - * heap.h - * - Dynamic Memory Allocation exports - */ - -#ifndef _HEAP_H_ -#define _HEAP_H_ - -extern void *Heap_Allocate(const char *File, int Line, size_t Bytes); -extern void *Heap_AllocateZero(const char *File, int Line, size_t Bytes); -extern void *Heap_Reallocate(const char *File, int Line, void *Ptr, size_t Bytes); -extern void Heap_Deallocate(void *Ptr); -extern int Heap_IsHeapAddr(void *Ptr); -extern void Heap_Validate(void); - -#define malloc(size) Heap_Allocate(_MODULE_NAME_"/"__FILE__, __LINE__, (size)) -#define calloc(num,size) Heap_AllocateZero(_MODULE_NAME_"/"__FILE__, __LINE__, (num)*(size)) -#define realloc(ptr,size) Heap_Reallocate(_MODULE_NAME_"/"__FILE__, __LINE__, (ptr), (size)) -#define free(ptr) Heap_Deallocate((ptr)) -#define IsHeap(ptr) Heap_IsHeapAddr((ptr)) - -#define strdup(Str) _strdup(_MODULE_NAME_"/"__FILE__, __LINE__, (Str)) - -#endif diff --git a/Kernel/include/heap_int.h b/Kernel/include/heap_int.h deleted file mode 100644 index 12a709b3..00000000 --- a/Kernel/include/heap_int.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * AcessOS Microkernel Version - * heap_int.h - * - Internal Heap Header - */ -#ifndef _HEAP_INT_H -#define _HEAP_INT_H - -typedef struct { - Uint Size; - int ValidSize; - const char *File; - int Line; - Uint Magic; - tTime AllocateTime; - char Data[]; -} tHeapHead; - -typedef struct { - Uint Magic; - tHeapHead *Head; - tHeapHead NextHead[]; // Array to make it act like an element, but have no size and refer to the next block -} tHeapFoot; - -#endif diff --git a/Kernel/include/init.h b/Kernel/include/init.h deleted file mode 100644 index 9f2fc108..00000000 --- a/Kernel/include/init.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * AcessOS Microkernel Version - * init.h - */ -#ifndef _INIT_H -#define _INIT_H - -extern void Arch_LoadBootModules(void); -extern void StartupPrint(const char *String); - -#endif diff --git a/Kernel/include/iocache.h b/Kernel/include/iocache.h deleted file mode 100644 index f008a2d0..00000000 --- a/Kernel/include/iocache.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Acess2 Kernel - * - IO Cache - * - * By thePowersGang (John Hodge) - */ -/** - * \file iocache.h - * \brief I/O Caching Helper Subsystem - * - * The IO Cache abstracts caching of disk sectors away from the device - * driver to reduce code duplication and allow a central location for - * disk cache management that can be flushed simply by the kernel, without - * having to ask each driver to do it indivitually. - */ -#ifndef _IOCHACHE_H_ -#define _IOCHACHE_H_ - -// === TYPES === -/** - * \brief IO Cache Handle - */ -typedef struct sIOCache tIOCache; -/** - * \brief Write Callback - * - * Called to write a sector back to the device - */ -typedef int (*tIOCache_WriteCallback)(Uint32 ID, Uint64 Sector, void *Buffer); - -// === CONSTANTS === -/** - * \brief I/O Cache handling modes - */ -enum eIOCache_Modess { - /** - * \brief Writeback - * - * Transparently writes data straight to the device when the cache - * is written to. - */ - IOCACHE_WRITEBACK, - /** - * \brief Delay Write - * - * Only writes when instructed to (by ::IOCache_Flush) or when a - * cached sector is being reallocated. - */ - IOCACHE_DELAYWRITE, - /** - * \brief Virtual - No Writes - * - * Changes to the cache contents are only reflected in memory, - * any calls to ::IOCache_Flush will silently return without doing - * anything and if a sector is reallocated, all changes will be lost - */ - IOCACHE_VIRTUAL -}; - -// === FUNCTIONS === -/** - * \brief Creates a new IO Cache - * \param Write Function to call to write a sector to the device - * \param ID ID to pass to \a Write - * \param SectorSize Size of a cached sector - * \param CacheSize Maximum number of objects that can be in the cache at one time - */ -tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize ); - -/** - * \brief Reads from a cached sector - * \param Cache Cache handle returned by ::IOCache_Create - * \param Sector Sector's ID number - * \param Buffer Destination for the data read - * \return 1 if the data was read, 0 if the sector is not cached, -1 on error - */ - int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer ); - -/** - * \brief Adds a sector to the cache - * \param Cache Cache handle returned by ::IOCache_Create - * \param Sector Sector's ID number - * \param Buffer Data to cache - * \return 1 on success, 0 if the sector is already cached, -1 on error - */ - int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer ); - -/** - * \brief Writes to a cached sector - * \param Cache Cache handle returned by ::IOCache_Create - * \param Sector Sector's ID number - * \param Buffer Data to write to the cache - * \return 1 if the data was read, 0 if the sector is not cached, -1 on error - * - * If the sector is in the cache, it is updated. - * Wether the Write callback is called depends on the selected caching - * behaviour. - */ - int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer ); - -/** - * \brief Flush altered sectors out to the device - * \param Cache Cache handle returned by ::IOCache_Create - * - * This will call the cache's write callback on each altered sector - * to flush the write cache. - */ -void IOCache_Flush( tIOCache *Cache ); - -/** - * \brief Flushes the cache and then removes it - * \param Cache Cache handle returned by ::IOCache_Create - * \note After this is called \a Cache is no longer valid - * - * IOCache_Destroy writes all changed sectors to the device and then - * deallocates the cache for other systems to use. - */ -void IOCache_Destroy( tIOCache *Cache ); - -#endif diff --git a/Kernel/include/lib/keyvalue.h b/Kernel/include/lib/keyvalue.h deleted file mode 100644 index ef37d5b3..00000000 --- a/Kernel/include/lib/keyvalue.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge - * - * include/keyvalue.h - * - Key/Value pair parsing - */ -#ifndef _ACESS_KEYVALUE_H_ -#define _ACESS_KEYVALUE_H_ - -typedef struct sKeyVal_ParseRules tKeyVal_ParseRules; -typedef struct sKeyVal_int_Rule tKeyVal_int_Rule; -typedef void (*tKeyVal_UnkCb)(char *String); -typedef void (*tKeyVal_KeyCb)(const char *Key, char *Value); - -/** - * \brief Handling rule for a key - */ -struct sKeyVal_int_Rule -{ - const char *Key; - const char *Type; // Acess printf format, with 'F' being a tKeyVal_KeyCb - void *Data; -}; - -struct sKeyVal_ParseRules -{ - /** - * \brief Function to call when no match is found - */ - tKeyVal_UnkCb Unknown; - tKeyVal_int_Rule Rules[]; -}; - -/** - * \brief Parse a NULL terminated list of strings as Key/Value pairs - * \param Rules Parsing rules - * \param Strings Input string list - */ -extern int KeyVal_ParseNull(tKeyVal_ParseRules *Rules, char **Strings); - -#endif - diff --git a/Kernel/include/mboot.h b/Kernel/include/mboot.h deleted file mode 100644 index c7f33ddb..00000000 --- a/Kernel/include/mboot.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * AcessOS Microkernel Version - * mboot.h - */ -#ifndef _MBOOT_H -#define _MBOOT_H - -#define MULTIBOOT_MAGIC 0x2BADB002 - -// === TYPES === -typedef struct { - Uint32 Flags; - Uint32 LowMem; - Uint32 HighMem; - Uint32 BootDevice; - Uint32 CommandLine; - Uint32 ModuleCount; - Uint32 Modules; - Uint32 SymbolInfo[4]; // #32 UNUSED - Uint32 MMapLength; - Uint32 MMapAddr; // #40 -} tMBoot_Info; - -typedef struct { - Uint32 Start; - Uint32 End; - Uint32 String; - Uint32 Resvd; -} tMBoot_Module; - -typedef struct { - Uint32 Size; // (May be at offset -4) - Uint64 Base; - Uint64 Length; - Uint32 Type; //1:RAM,Else Reserved -} __attribute__ ((packed)) tMBoot_MMapEnt; - -#endif diff --git a/Kernel/include/modules.h b/Kernel/include/modules.h deleted file mode 100644 index d7281336..00000000 --- a/Kernel/include/modules.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * AcessOS 2 - * - Module Loader - */ -/** - * \file modules.h - * \brief Module Handling and Loader Definitions - * \author John Hodge (thePowersGang) - * - * This file serves two pourposes. First it defines the format for native - * Acess2 modules and the functions to create them. - * Second, it defines the structure and register function for new module - * loaders, allowing Acess to understand many different module / driver - * formats. - * - * Modules are defined by including this file in the module's main source - * file and using the ::MODULE_DEFINE macro to create the module header. - * - * To register a new module loader with the kernel, the loader module must - * create and populate an instance of ::tModuleLoader then pass it to - * ::Module_RegisterLoader - */ -#ifndef _MODULE_H -#define _MODULE_H - -/** - * \brief Module header magic value - */ -#define MODULE_MAGIC ('A'|('M'<<8)|('D'<<16)|('\2'<<24)) - -/** - * \def MODULE_ARCH_ID - * \brief Architecture ID - */ -// IA32 - Architecture 1 -#if ARCHDIR == x86 -# define MODULE_ARCH_ID 1 -// IA64 - Architecture 2 -#elif ARCHDIR == x86_64 -# define MODULE_ARCH_ID 2 -#else -# error "Unknown architecture when determining MODULE_ARCH_ID ('" #ARCHDIR "')" -#endif - -/** - * \brief Define a module - * \param _flags Module Flags - * \param _ver Module Version - * \param _ident Unique Module Name - * \param _entry Module initialiser / entrypoint - * \param _deinit Module cleanup / unloader - * \param _deps NULL terminated list of this's module's dependencies - * Contains the identifiers of the required modules. - */ -#define MODULE_DEFINE(_flags,_ver,_ident,_entry,_deinit,_deps...) \ - const char *EXPAND_CONCAT(_DriverDeps_,_ident)[]={_deps};\ - tModule __attribute__ ((section ("KMODULES"),unused))\ - EXPAND_CONCAT(_DriverInfo_,_ident)=\ - {MODULE_MAGIC,MODULE_ARCH_ID,_flags,_ver,NULL,EXPAND_STR(_ident),\ - _entry,_deinit,EXPAND_CONCAT(_DriverDeps_,_ident)} - -/** - * \brief Module header - * \note There is no reason for a module to touch this structure beyond - * using ::MODULE_DEFINE to create it. - */ -typedef struct sModule -{ - Uint32 Magic; //!< Identifying magic value (See ::MODULE_MAGIC) - Uint8 Arch; //!< Achitecture ID (See ::MODULE_ARCH_ID) - Uint8 Flags; //!< Module Flags - Uint16 Version; //!< Module Version in Major.Minor 8.8 form - struct sModule *Next; //!< Next module in list (not to be touched by the driver) - const char *Name; //!< Module Name/Identifier - int (*Init)(char **Arguments); //!< Module initialiser / entrypoint - void (*Deinit)(void); //!< Cleanup Function - const char **Dependencies; //!< NULL terminated list of dependencies -} PACKED tModule; - -/** - * \brief Return values for tModule.Init - */ -enum eModuleErrors -{ - MODULE_ERR_OK, //!< No Error - MODULE_ERR_MISC, //!< Misc Error - MODULE_ERR_NOTNEEDED, //!< Module not needed - MODULE_ERR_MALLOC, //!< Error with malloc/realloc/calloc - - MODULE_ERR_BADMODULE, //!< Bad module (only used by loader) - MODULE_ERR_MAX //!< Maximum defined error code -}; - -/** - * \brief Module Loader definition - * - * Allows a module to extend the loader to recognise other module types - * E.g. EDI, UDI, Windows, Linux, ... - */ -typedef struct sModuleLoader -{ - struct sModuleLoader *Next; //!< Kernel Only - Next loader in list - char *Name; //!< Friendly name for the loader - int (*Detector)(void *Base); //!< Simple detector function - int (*Loader)(void *Base); //!< Initialises the module - int (*Unloader)(void *Base); //!< Calls module's cleanup -} PACKED tModuleLoader; - -/** - * \brief Registers a tModuleLoader with the kernel - * \param Loader Pointer to loader structure (must be persistent) - * \return Boolean Success - */ -extern int Module_RegisterLoader(tModuleLoader *Loader); - -/** - * \brief Initialises (if needed) a named module - * \param Name Module name to initialise - * \return -1 on not existing, 0 if the module initialised (or if it was already initialised) - */ -extern int Module_EnsureLoaded(const char *Name); - -#endif diff --git a/Kernel/include/mutex.h b/Kernel/include/mutex.h deleted file mode 100644 index 326dbd2c..00000000 --- a/Kernel/include/mutex.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Acess2 Kernel - * mutex.h - * - Mutual Exclusion syncronisation primitive - */ -#ifndef _MUTEX_H -#define _MUTEX_H - -#include - -typedef struct sMutex tMutex; - -struct sMutex -{ - tShortSpinlock Protector; //!< Protector for the lock strucure - const char *Name; //!< Human-readable name - struct sThread *volatile Owner; //!< Owner of the lock (set upon getting the lock) - struct sThread *Waiting; //!< Waiting threads - struct sThread *LastWaiting; //!< Waiting threads -}; - -/** - * \brief Acquire a heavy mutex - * \param Mutex Mutex to acquire - * \return zero on success, -1 if terminated - * - * This type of mutex checks if the mutex is avaliable, and acquires it - * if it is. Otherwise, the current thread is added to the mutex's wait - * queue and the thread suspends. When the holder of the mutex completes, - * the oldest thread (top thread) on the queue is given the lock and - * restarted. - */ -extern int Mutex_Acquire(tMutex *Mutex); - -/** - * \brief Release a held mutex - * \param Mutex Mutex to release - * \note Releasing a non-held mutex has no effect - */ -extern void Mutex_Release(tMutex *Mutex); - -/** - * \brief Is this mutex locked? - * \param Mutex Mutex pointer - */ -extern int Mutex_IsLocked(tMutex *Mutex); - -#endif diff --git a/Kernel/include/semaphore.h b/Kernel/include/semaphore.h deleted file mode 100644 index db0b279b..00000000 --- a/Kernel/include/semaphore.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Acess2 Kernel - * semaphore.h - * - Semaphore syncronisation primitive - */ -#ifndef _SEMAPHORE_H -#define _SEMAPHORE_H - -#include - -/** - * \brief Semaphore typedef - */ -typedef struct sSemaphore tSemaphore; - -/** - * \brief Semaphore structure - */ -struct sSemaphore { - tShortSpinlock Protector; //!< Protector for the lock strucure - const char *ModName; //!< Human-readable module name - const char *Name; //!< Human-readable name - volatile int Value; //!< Current value - volatile int MaxValue; //!< Maximum value (signal will wait if it will go over this) - - struct sThread *Waiting; //!< Waiting threads - struct sThread *LastWaiting; //!< Waiting threads - struct sThread *Signaling; //!< Waiting threads (from Semaphore_Signal) - struct sThread *LastSignaling; //!< Last waiting thread (from Semaphore_Signal) -}; - -/** - * \brief Initialise the semaphore - * \param Sem Semaphore structure to initialsie - * \param InitValue Initial value of the semaphore - * \param MaxValue Maximum value for the semaphore - * \param Module Module name - * \param Name Symbolic name - * \note Not always needed, as initialising to 0 is valid, but it is preferred - * if all semaphores have \a Name set - * - * \note \a Module and \a Name must be avaliable for as long as the semaphore is used - */ -extern void Semaphore_Init(tSemaphore *Sem, int InitValue, int MaxValue, const char *Module, const char *Name); -/** - * \brief Acquire items from the semaphore - * \param Sem Semaphore structure to use - * \param MaxToTake Maximum number of items to take off the list (if zero, as much as possible is taken) - * \return Number of items fetched - * \retval 0 Semaphore interrupted (signal/message) - * \retval -1 Unspecified error - */ -extern int Semaphore_Wait(tSemaphore *Sem, int MaxToTake); -/** - * \brief Add an "item" to the semaphore - * \param Sem Semaphore to use - * \param AmmountToAdd Number of items to add - * \return Actual number of items added - * \retval 0 Semaphore interrupted - * \retval -1 Unspecified error - */ -extern int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd); -/** - * \brief Get the number of items waiting in a semaphore - * \param Sem Semaphore to use - * \return Number of waiting items - */ -extern int Semaphore_GetValue(tSemaphore *Sem); - -#endif diff --git a/Kernel/include/signal.h b/Kernel/include/signal.h deleted file mode 100644 index d97d6dcb..00000000 --- a/Kernel/include/signal.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Acess2 Kernel - * Signal List - */ -#ifndef _SIGNAL_H_ -#define _SIGNAL_H_ - -enum eSignals { - SIGKILL, - SIGSTOP, - SIGCONT, - SIGCHLD, - NSIG -}; - -#endif diff --git a/Kernel/include/syscalls.h b/Kernel/include/syscalls.h deleted file mode 100644 index 5ce28149..00000000 --- a/Kernel/include/syscalls.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Acess2 - * syscalls.h - * - System Call List - * - * NOTE: Generated from Kernel/syscalls.lst - */ -#ifndef _SYSCALLS_H -#define _SYSCALLS_H - -#define SYS_EXIT 0 // Kill this thread -#define SYS_CLONE 1 // Create a new thread -#define SYS_KILL 2 // Send a signal -#define SYS_SETFAULTHANDLER 3 // Set signal Handler -#define SYS_YIELD 4 // Yield remainder of timestamp -#define SYS_SLEEP 5 // Sleep until messaged or signaled -#define SYS_WAITEVENT 6 // Wait for an event -#define SYS_WAITTID 7 // Wait for a thread to do something -#define SYS_SETNAME 8 // Sets the name of the current thread -#define SYS_GETNAME 9 // Gets the name of a thread -#define SYS_GETTID 10 // Get current thread ID -#define SYS_GETPID 11 // Get current thread group ID -#define SYS_SETPRI 12 // Set process priority -#define SYS_SENDMSG 13 // Send an IPC message -#define SYS_GETMSG 14 // Recieve an IPC message -#define SYS_GETTIME 15 // Get the current timestamp -#define SYS_SPAWN 16 // Spawn a new process -#define SYS_EXECVE 17 // Replace the current process -#define SYS_LOADBIN 18 // Load a binary into the current address space -#define SYS_UNLOADBIN 19 // Unload a loaded binary -#define SYS_LOADMOD 20 // Load a module into the kernel -#define SYS_GETPHYS 32 // Get the physical address of a page -#define SYS_MAP 33 // Map a physical address -#define SYS_ALLOCATE 34 // Allocate a page -#define SYS_UNMAP 35 // Unmap a page -#define SYS_PREALLOC 36 // Preallocate a page -#define SYS_SETFLAGS 37 // Set a page's flags -#define SYS_SHAREWITH 38 // Share a page with another thread -#define SYS_GETUID 39 // Get current User ID -#define SYS_GETGID 40 // Get current Group ID -#define SYS_SETUID 41 // Set current user ID -#define SYS_SETGID 42 // Set current Group ID -#define SYS_OPEN 64 // Open a file -#define SYS_REOPEN 65 // Close a file and reuse its handle -#define SYS_CLOSE 66 // Close a file -#define SYS_READ 67 // Read from an open file -#define SYS_WRITE 68 // Write to an open file -#define SYS_IOCTL 69 // Perform an IOCtl Call -#define SYS_SEEK 70 // Seek to a new position in the file -#define SYS_READDIR 71 // Read from an open directory -#define SYS_OPENCHILD 72 // Open a child entry in a directory -#define SYS_GETACL 73 // Get an ACL Value -#define SYS_SETACL 74 // Set an ACL Value -#define SYS_FINFO 75 // Get file information -#define SYS_MKDIR 76 // Create a new directory -#define SYS_LINK 77 // Create a new link to a file -#define SYS_SYMLINK 78 // Create a symbolic link -#define SYS_UNLINK 79 // Delete a file -#define SYS_TELL 80 // Return the current file position -#define SYS_CHDIR 81 // Change current directory -#define SYS_GETCWD 82 // Get current directory -#define SYS_MOUNT 83 // Mount a filesystem -#define SYS_SELECT 84 // Wait for file handles - -#define NUM_SYSCALLS 85 -#define SYS_DEBUG 0x100 - -#ifndef __ASSEMBLER__ -static const char *cSYSCALL_NAMES[] = { - "SYS_EXIT", - "SYS_CLONE", - "SYS_KILL", - "SYS_SETFAULTHANDLER", - "SYS_YIELD", - "SYS_SLEEP", - "SYS_WAITEVENT", - "SYS_WAITTID", - "SYS_SETNAME", - "SYS_GETNAME", - "SYS_GETTID", - "SYS_GETPID", - "SYS_SETPRI", - "SYS_SENDMSG", - "SYS_GETMSG", - "SYS_GETTIME", - "SYS_SPAWN", - "SYS_EXECVE", - "SYS_LOADBIN", - "SYS_UNLOADBIN", - "SYS_LOADMOD", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "SYS_GETPHYS", - "SYS_MAP", - "SYS_ALLOCATE", - "SYS_UNMAP", - "SYS_PREALLOC", - "SYS_SETFLAGS", - "SYS_SHAREWITH", - "SYS_GETUID", - "SYS_GETGID", - "SYS_SETUID", - "SYS_SETGID", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "SYS_OPEN", - "SYS_REOPEN", - "SYS_CLOSE", - "SYS_READ", - "SYS_WRITE", - "SYS_IOCTL", - "SYS_SEEK", - "SYS_READDIR", - "SYS_OPENCHILD", - "SYS_GETACL", - "SYS_SETACL", - "SYS_FINFO", - "SYS_MKDIR", - "SYS_LINK", - "SYS_SYMLINK", - "SYS_UNLINK", - "SYS_TELL", - "SYS_CHDIR", - "SYS_GETCWD", - "SYS_MOUNT", - "SYS_SELECT", - - "" -}; -#endif - -#endif diff --git a/Kernel/include/syscalls.inc.asm b/Kernel/include/syscalls.inc.asm deleted file mode 100644 index 149fe373..00000000 --- a/Kernel/include/syscalls.inc.asm +++ /dev/null @@ -1,57 +0,0 @@ -; Acess2 -; System Calls List -; - -%define SYS_EXIT 0 ;Kill this thread -%define SYS_CLONE 1 ;Create a new thread -%define SYS_KILL 2 ;Send a signal -%define SYS_SETFAULTHANDLER 3 ;Set signal Handler -%define SYS_YIELD 4 ;Yield remainder of timestamp -%define SYS_SLEEP 5 ;Sleep until messaged or signaled -%define SYS_WAITEVENT 6 ;Wait for an event -%define SYS_WAITTID 7 ;Wait for a thread to do something -%define SYS_SETNAME 8 ;Sets the name of the current thread -%define SYS_GETNAME 9 ;Gets the name of a thread -%define SYS_GETTID 10 ;Get current thread ID -%define SYS_GETPID 11 ;Get current thread group ID -%define SYS_SETPRI 12 ;Set process priority -%define SYS_SENDMSG 13 ;Send an IPC message -%define SYS_GETMSG 14 ;Recieve an IPC message -%define SYS_GETTIME 15 ;Get the current timestamp -%define SYS_SPAWN 16 ;Spawn a new process -%define SYS_EXECVE 17 ;Replace the current process -%define SYS_LOADBIN 18 ;Load a binary into the current address space -%define SYS_UNLOADBIN 19 ;Unload a loaded binary -%define SYS_LOADMOD 20 ;Load a module into the kernel -%define SYS_GETPHYS 32 ;Get the physical address of a page -%define SYS_MAP 33 ;Map a physical address -%define SYS_ALLOCATE 34 ;Allocate a page -%define SYS_UNMAP 35 ;Unmap a page -%define SYS_PREALLOC 36 ;Preallocate a page -%define SYS_SETFLAGS 37 ;Set a page's flags -%define SYS_SHAREWITH 38 ;Share a page with another thread -%define SYS_GETUID 39 ;Get current User ID -%define SYS_GETGID 40 ;Get current Group ID -%define SYS_SETUID 41 ;Set current user ID -%define SYS_SETGID 42 ;Set current Group ID -%define SYS_OPEN 64 ;Open a file -%define SYS_REOPEN 65 ;Close a file and reuse its handle -%define SYS_CLOSE 66 ;Close a file -%define SYS_READ 67 ;Read from an open file -%define SYS_WRITE 68 ;Write to an open file -%define SYS_IOCTL 69 ;Perform an IOCtl Call -%define SYS_SEEK 70 ;Seek to a new position in the file -%define SYS_READDIR 71 ;Read from an open directory -%define SYS_OPENCHILD 72 ;Open a child entry in a directory -%define SYS_GETACL 73 ;Get an ACL Value -%define SYS_SETACL 74 ;Set an ACL Value -%define SYS_FINFO 75 ;Get file information -%define SYS_MKDIR 76 ;Create a new directory -%define SYS_LINK 77 ;Create a new link to a file -%define SYS_SYMLINK 78 ;Create a symbolic link -%define SYS_UNLINK 79 ;Delete a file -%define SYS_TELL 80 ;Return the current file position -%define SYS_CHDIR 81 ;Change current directory -%define SYS_GETCWD 82 ;Get current directory -%define SYS_MOUNT 83 ;Mount a filesystem -%define SYS_SELECT 84 ;Wait for file handles diff --git a/Kernel/include/threads.h b/Kernel/include/threads.h deleted file mode 100644 index 9ff7b629..00000000 --- a/Kernel/include/threads.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Acess2 Kernel - */ -#ifndef _THREADS_H_ -#define _THREADS_H_ - -#include -#include -//#include - -enum eFaultNumbers -{ - FAULT_MISC, - FAULT_PAGE, - FAULT_ACCESS, - FAULT_DIV0, - FAULT_OPCODE, - FAULT_FLOAT -}; - -#define GETMSG_IGNORE ((void*)-1) - -typedef struct sThread tThread; - -// === FUNCTIONS === -extern void Threads_SetFaultHandler(Uint Handler); - -extern int Threads_SetUID(tUID ID); -extern int Threads_SetGID(tUID ID); -extern tTID Threads_WaitTID(int TID, int *Status); - - -extern int *Threads_GetMaxFD(void); -extern char **Threads_GetCWD(void); -extern char **Threads_GetChroot(void); - -extern int Proc_SendMessage(Uint Dest, int Length, void *Data); -extern int Proc_GetMessage(Uint *Source, void *Buffer); - -#endif diff --git a/Kernel/include/threads_int.h b/Kernel/include/threads_int.h deleted file mode 100644 index 66bb8c16..00000000 --- a/Kernel/include/threads_int.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Internal Threading header - * - Only for use by stuff that needs access to the thread type. - */ -#ifndef _THREADS_INT_H_ -#define _THREADS_INT_H_ - -#include -#include - - -typedef struct sProcess tProcess; - -/** - * \brief IPC Message - */ -typedef struct sMessage -{ - struct sMessage *Next; //!< Next message in thread's inbox - tTID Source; //!< Source thread ID - Uint Length; //!< Length of message data in bytes - Uint8 Data[]; //!< Message data -} tMsg; - -/** - * \brief Process state - */ -struct sProcess -{ - tPID PID; - int nThreads; - - tUID UID; //!< User ID - tGID GID; //!< User and Group - tMemoryState MemState; - - int MaxFD; - char *CurrentWorkingDir; - char *RootDir; -}; - -/** - * \brief Core threading structure - * - */ -struct sThread -{ - // --- threads.c's - /** - * \brief Next thread in current list - * \note Required to be first for linked list hacks to work - */ - struct sThread *Next; - struct sThread *GlobalNext; //!< Next thread in global list - struct sThread *GlobalPrev; //!< Previous thread in global list - tShortSpinlock IsLocked; //!< Thread's spinlock - volatile int Status; //!< Thread Status - void *WaitPointer; //!< What (Mutex/Thread/other) is the thread waiting on - int RetStatus; //!< Return Status - - tTID TID; //!< Thread ID - struct sProcess *Process; //!< Thread Group / Process - struct sThread *Parent; //!< Parent Thread - char *ThreadName; //!< Name of thread - - // --- arch/proc.c's responsibility - //! Kernel Stack Base - tVAddr KernelStack; - - //! State on task switch - tTaskState SavedState; - - // --- threads.c's - int CurFaultNum; //!< Current fault number, 0: none - tVAddr FaultHandler; //!< Fault Handler - - tMsg * volatile Messages; //!< Message Queue - tMsg *LastMessage; //!< Last Message (speeds up insertion) - - int Quantum, Remaining; //!< Quantum Size and remaining timesteps - int Priority; //!< Priority - 0: Realtime, higher means less time - - int _errno; - - volatile int CurCPU; - - bool bInstrTrace; - - // --- event.c - Uint32 EventState; -}; - - -enum { - THREAD_STAT_NULL, // Invalid process - THREAD_STAT_ACTIVE, // Running and schedulable process - THREAD_STAT_SLEEPING, // Message Sleep - THREAD_STAT_MUTEXSLEEP, // Mutex Sleep - THREAD_STAT_SEMAPHORESLEEP, // Semaphore Sleep - THREAD_STAT_QUEUESLEEP, // Queue - THREAD_STAT_EVENTSLEEP, // Event sleep - THREAD_STAT_WAITING, // ??? (Waiting for a thread) - THREAD_STAT_PREINIT, // Being created - THREAD_STAT_ZOMBIE, // Died/Killed, but parent not informed - THREAD_STAT_DEAD, // Awaiting burial (free) - THREAD_STAT_BURIED // If it's still on the list here, something's wrong -}; -static const char * const casTHREAD_STAT[] = { - "THREAD_STAT_NULL", - "THREAD_STAT_ACTIVE", - "THREAD_STAT_SLEEPING", - "THREAD_STAT_MUTEXSLEEP", - "THREAD_STAT_SEMAPHORESLEEP", - "THREAD_STAT_QUEUESLEEP", - "THREAD_STAT_EVENTSLEEP", - "THREAD_STAT_WAITING", - "THREAD_STAT_PREINIT", - "THREAD_STAT_ZOMBIE", - "THREAD_STAT_DEAD", - "THREAD_STAT_BURIED" -}; - -// === GLOBALS === -extern BOOL gaThreads_NoTaskSwitch[MAX_CPUS]; -extern tShortSpinlock glThreadListLock; - -// === FUNCTIONS === -extern tThread *Proc_GetCurThread(void); - -extern tThread *Threads_GetThread(Uint TID); -extern void Threads_SetPriority(tThread *Thread, int Pri); -extern int Threads_Wake(tThread *Thread); -extern void Threads_Kill(tThread *Thread, int Status); -extern void Threads_AddActive(tThread *Thread); -extern tThread *Threads_RemActive(void); -extern void Threads_Delete(tThread *Thread); -extern tThread *Threads_GetNextToRun(int CPU, tThread *Last); - -extern tThread *Threads_CloneTCB(Uint Flags); -extern tThread *Threads_CloneThreadZero(void); - -#endif diff --git a/Kernel/include/timers.h b/Kernel/include/timers.h deleted file mode 100644 index 22dd5c6a..00000000 --- a/Kernel/include/timers.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * timers.h - * - Kernel timers - */ -#ifndef _KERNEL_TIMERS_H_ -#define _KERNEL_TIMERS_H_ -/** - * \file timers.h - * \brief Kernel timers - */ - -typedef struct sTimer tTimer; - -/** - * \brief Timer callback function - */ -typedef void (tTimerCallback)(void *); - -/** - * \brief Creates a one-shot timer - * \param Delta Period of the timer - * \param Callback Function to call each time - * \param Argument Argument to pass to the callback - */ -extern tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument); -/** - * \brief Removed an active timer - */ -extern void Time_RemoveTimer(tTimer *Timer); -/** - * \brief Wait for a period of milliseconds - */ -extern void Time_Delay(int Delay); - -#endif - diff --git a/Kernel/include/tpl_mm_phys_bitmap.h b/Kernel/include/tpl_mm_phys_bitmap.h deleted file mode 100644 index 39444d94..00000000 --- a/Kernel/include/tpl_mm_phys_bitmap.h +++ /dev/null @@ -1,435 +0,0 @@ -/* - * Acess2 Core - * - * include/tpl_mm_phys_bitmap.h - * Physical Memory Manager Template - */ -/* - * Bitmap Edition - * - * Uses 4.125+PtrSize bytes per page - */ - -#define MM_PAGE_REFCOUNTS MM_PMM_BASE -#define MM_PAGE_NODES (MM_PMM_BASE+(MM_MAXPHYSPAGE*sizeof(Uint32))) -#define MM_PAGE_BITMAP (MM_PAGE_NODES+(MM_MAXPHYSPAGE*sizeof(void*))) - -#define PAGE_BITMAP_FREE(__pg) (gaPageBitmaps[(__pg)/32] & (1LL << ((__pg)&31))) -#define PAGE_BITMAP_SETFREE(__pg) do{gaPageBitmaps[(__pg)/32] |= (1LL << ((__pg)&31));}while(0) -#define PAGE_BITMAP_SETUSED(__pg) do{gaPageBitmaps[(__pg)/32] &= ~(1LL << ((__pg)&31));}while(0) - -// === PROTOTYPES === -//void MM_InitPhys_Multiboot(tMBoot_Info *MBoot); -//tPAddr MM_AllocPhysRange(int Num, int Bits); -//tPAddr MM_AllocPhys(void); -//void MM_RefPhys(tPAddr PAddr); -//void MM_DerefPhys(tPAddr PAddr); - int MM_int_GetRangeID( tPAddr Addr ); - int MM_int_GetMapEntry( void *Data, int Index, tPAddr *Start, tPAddr *Length ); -void MM_Tpl_InitPhys(int MaxRAMPage, void *MemoryMap); - -// === GLOBALS === -tMutex glPhysicalPages; -void **gapPageNodes = (void*)MM_PAGE_NODES; //!< Associated VFS Node for each page -Uint32 *gaiPageReferences = (void*)MM_PAGE_REFCOUNTS; // Reference Counts -Uint32 *gaPageBitmaps = (void*)MM_PAGE_BITMAP; // Used bitmap (1 == avail) -Uint64 giMaxPhysPage = 0; // Maximum Physical page - int gbPMM_Init = 0; - int giPhysFirstFree; - int giPhysLastFree; - int giPhysNumFree; - -// === CODE === -/** - * \brief Initialise the physical memory manager with a passed memory map - */ -void MM_Tpl_InitPhys(int MaxRAMPage, void *MemoryMap) -{ - int mapIndex = 0; - tPAddr rangeStart, rangeLen; - - if( MM_PAGE_BITMAP + (MM_MAXPHYSPAGE/8) > MM_PMM_END ) { - Log_KernelPanic("PMM", "Config Error, PMM cannot fit data in allocated range"); - } - - giMaxPhysPage = MaxRAMPage; - -// for( i = 0; i < MM_RANGE_MAX; i ++ ) -// gaiPhysRangeFirstFree[i] = -1; - giPhysFirstFree = -1; - - while( MM_int_GetMapEntry(MemoryMap, mapIndex++, &rangeStart, &rangeLen) ) - { - tVAddr bitmap_page; - - LOG("Range %i, %P to %P", mapIndex-1, rangeStart, rangeLen); - rangeStart /= PAGE_SIZE; - rangeLen /= PAGE_SIZE; - - giPhysNumFree += rangeLen; - - LOG("rangeStart = 0x%x, rangeLen = 0x%x", rangeStart, rangeLen); - - if( giPhysFirstFree == -1 || giPhysFirstFree > rangeStart ) - giPhysFirstFree = rangeStart; - - if( giPhysLastFree < rangeStart + rangeLen ) - giPhysLastFree = rangeStart + rangeLen; - - LOG("giPhysFirstFree = 0x%x, giPhysLastFree = 0x%x", giPhysFirstFree, giPhysLastFree); - - bitmap_page = (tVAddr)&gaPageBitmaps[rangeStart/32]; - bitmap_page &= ~(PAGE_SIZE-1); - - // Only need to allocate bitmaps - if( !MM_GetPhysAddr( bitmap_page ) ) { - if( !MM_Allocate( bitmap_page ) ) { - Log_KernelPanic("PMM", "Out of memory during init, this is bad"); - return ; - } -// memset( (void*)bitmap_page, 0, (rangeStart/8) & ~(PAGE_SIZE-1) ); - memset( (void*)bitmap_page, 0, PAGE_SIZE ); - } - - // Align to 32 pages - for( ; (rangeStart & 31) && rangeLen > 0; rangeStart++, rangeLen-- ) { - gaPageBitmaps[rangeStart / 32] |= 1 << (rangeStart&31); - LOG("gaPageBitmaps[%i] = 0x%x", rangeStart/32, gaPageBitmaps[rangeStart/32]); - } - // Mark blocks of 32 as avail - for( ; rangeLen > 31; rangeStart += 32, rangeLen -= 32 ) { - gaPageBitmaps[rangeStart / 32] = -1; - } - // Mark the tail - for( ; rangeLen > 0; rangeStart ++, rangeLen -- ) { - gaPageBitmaps[rangeStart / 32] |= 1 << (rangeStart&31); - } - } - - gbPMM_Init = 1; - - LOG("giPhysFirstFree = 0x%x, giPhysLastFree = 0x%x", giPhysFirstFree, giPhysLastFree); - LEAVE('-'); -} - -/** - * \brief Allocate a contiguous range of physical pages with a maximum - * bit size of \a MaxBits - * \param Pages Number of pages to allocate - * \param MaxBits Maximum size of the physical address - * \note If \a MaxBits is <= 0, any sized address is used (with preference - * to higher addresses) - */ -tPAddr MM_AllocPhysRange(int Pages, int MaxBits) -{ - tPAddr addr, ret; - int nFree = 0, i; - - ENTER("iPages iBits", Pages, MaxBits); - - Mutex_Acquire(&glPhysicalPages); - - // Check if there is enough in the range - if(giPhysNumFree >= Pages) - { - LOG("{0x%x -> 0x%x}", giPhysFirstFree, giPhysLastFree); - // Do a cheap scan, scanning upwards from the first free page in - // the range - nFree = 0; - addr = giPhysFirstFree; - while( addr <= giPhysLastFree ) - { - #if USE_SUPER_BITMAP - // Check the super bitmap - if( gaSuperBitmap[addr / (32*32)] == 0 ) - { - LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr); - nFree = 0; - addr += (32*32); - addr &= ~(32*32-1); // (1LL << 6+6) - 1 - continue; - } - #endif - LOG("gaPageBitmaps[%i] = 0x%x", addr/32, gaPageBitmaps[addr/32]); - // Check page block (32 pages) - if( gaPageBitmaps[addr / 32] == 0) { - LOG("nFree = %i = 0 (block) (0x%x)", nFree, addr); - nFree = 0; - addr += 32; - addr &= ~31; - continue; - } - // Check individual page - if( !(gaPageBitmaps[addr / 32] & (1LL << (addr & 31))) ) - { - LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr); - nFree = 0; - addr ++; - continue; - } - nFree ++; - addr ++; - LOG("nFree(%i) == %i (1x%x)", nFree, Pages, addr); - if(nFree == Pages) - break; - } - LOG("nFree = %i", nFree); - // If we don't find a contiguous block, nFree will not be equal - // to Num, so we set it to zero and do the expensive lookup. - if(nFree != Pages) nFree = 0; - } - - if( !nFree ) - { -#if 0 - // Oops. ok, let's do an expensive check (scan down the list - // until a free range is found) - nFree = 1; - addr = gaiPhysRangeLastFree[ rangeID ]; - // TODO -#endif - Mutex_Release(&glPhysicalPages); - // TODO: Page out - // ATM. Just Warning - Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Pages); - Log_Warning("PMM", - "Out of memory (unable to fulfil request for %i pages)", - Pages - ); - LEAVE('i', 0); - return 0; - } - LOG("nFree = %i, addr = 0x%08x", nFree, (addr-Pages) << 12); - - // Mark pages as allocated - addr -= Pages; - for( i = 0; i < Pages; i++, addr++ ) - { - // Mark as used - PAGE_BITMAP_SETUSED(addr); - // Maintain first possible free - giPhysNumFree --; - if(addr == giPhysFirstFree) - giPhysFirstFree += 1; - - LOG("if( MM_GetPhysAddr( %p ) )", &gaiPageReferences[addr]); - // Mark as referenced if the reference count page is valid - if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[addr] ) ) { - gaiPageReferences[addr] = 1; - } - } - ret = addr - Pages; // Save the return address - LOG("ret = %x", ret); - - #if TRACE_ALLOCS - LogF("MM_AllocPhysRange: %P (%i pages)\n", ret, Pages); - if(Pages > 1) { - LogF(" also"); - for(i = 1; i < Pages; i++) - LogF(" %P", ret+i); - LogF("\n"); - } - #endif - - #if USE_SUPER_BITMAP - // Update super bitmap - Pages += addr & (32-1); - addr &= ~(32-1); - Pages = (Pages + (32-1)) & ~(32-1); - for( i = 0; i < Pages/32; i++ ) - { - if( gaPageBitmaps[ addr / 32 ] + 1 == 0 ) - gaSuperBitmap[addr / (32*32)] |= 1LL << ((addr / 32) & 31); - } - #endif - - Mutex_Release(&glPhysicalPages); - LEAVE('x', ret << 12); - return ret << 12; -} - -/** - * \brief Allocate a single physical page, with no preference as to address size. - */ -tPAddr MM_AllocPhys(void) -{ - int i; - - if( !gbPMM_Init ) - { - // Hack to allow allocation during setup - for(i = 0; i < NUM_STATIC_ALLOC; i++) { - if( gaiStaticAllocPages[i] ) { - tPAddr ret = gaiStaticAllocPages[i]; - gaiStaticAllocPages[i] = 0; - Log("MM_AllocPhys: Return %x, static alloc %i", ret, i); - return ret; - } - } - - tPAddr ret = 0; - for( ret = 0; ret < giMaxPhysPage; ret ++ ) - { - if( !MM_GetPhysAddr( (tVAddr)&gaPageBitmaps[ret/32] ) ) { - ret += PAGE_SIZE*8; - continue ; - } - if( gaPageBitmaps[ret/32] == 0 ) { - ret += 32-1; - continue ; - } - if( gaPageBitmaps[ret/32] & (1 << (ret&31)) ) { - gaPageBitmaps[ret/32] &= ~(1 << (ret&31)); - return ret * PAGE_SIZE; - } - } - Log_Error("PMM", "MM_AllocPhys failed duing init"); - return 0; - } - #if TRACE_ALLOCS - Log("AllocPhys by %p", __builtin_return_address(0)); - #endif - - return MM_AllocPhysRange(1, -1); -} - -/** - * \brief Reference a physical page - */ -void MM_RefPhys(tPAddr PAddr) -{ - tPAddr page = PAddr / PAGE_SIZE; - tVAddr refpage = (tVAddr)&gaiPageReferences[page] & ~(PAGE_SIZE-1); - - if( page >= giMaxPhysPage ) return ; - - if( PAGE_BITMAP_FREE(page) ) - { - // Allocate - PAGE_BITMAP_SETUSED(page); - #if USE_SUPER_BITMAP - if( gaPageBitmaps[page / 32] == 0 ) - gaSuperBitmap[page / (32*32)] &= ~(1LL << ((page / 32) & 31)); - #endif - if( MM_GetPhysAddr( refpage ) ) - gaiPageReferences[page] = 1; - } - else - { - // Reference again - if( !MM_GetPhysAddr( refpage ) ) - { - int pages_per_page, basepage, i; - if( MM_Allocate(refpage) == 0 ) { - // Out of memory, can this be resolved? - // TODO: Reclaim memory - Log_Error("PMM", "Out of memory (MM_RefPhys)"); - return ; - } - pages_per_page = PAGE_SIZE/sizeof(*gaiPageReferences); - basepage = page & ~(pages_per_page-1); - for( i = 0; i < pages_per_page; i ++ ) { - if( PAGE_BITMAP_FREE(basepage+i) ) - gaiPageReferences[basepage+i] = 0; - else - gaiPageReferences[basepage+i] = 1; - } - gaiPageReferences[page] = 2; - } - else - gaiPageReferences[ page ] ++; - } -} - -int MM_GetRefCount(tPAddr PAddr) -{ - PAddr >>= 12; - if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[PAddr] ) ) { - return gaiPageReferences[PAddr]; - } - - if( gaPageBitmaps[ PAddr / 32 ] & (1LL << (PAddr&31)) ) { - return 1; - } - - return 0; -} - -/** - * \brief Dereference a physical page - */ -void MM_DerefPhys(tPAddr PAddr) -{ - Uint64 page = PAddr >> 12; - - if( PAddr >> 12 > giMaxPhysPage ) return ; - - ENTER("PPAddr", PAddr); - - if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[page] ) ) - { - if( gaiPageReferences[page] > 0 ) - gaiPageReferences[ page ] --; - if( gaiPageReferences[ page ] == 0 ) { - gaPageBitmaps[ page / 32 ] |= 1 << (page&31); - // TODO: Catch when all pages in this range have been dereferenced - } - } - else - gaPageBitmaps[ page / 32 ] |= 1 << (page&31); - // Clear node if needed - if( MM_GetPhysAddr( (tVAddr)&gapPageNodes[page] ) ) { - gapPageNodes[page] = NULL; - // TODO: Catch when all pages in this range are not using nodes - } - - // Update the free counts if the page was freed - if( gaPageBitmaps[ page / 32 ] & (1LL << (page&31)) ) - { - giPhysNumFree ++; - if( giPhysFirstFree == -1 || giPhysFirstFree > page ) - giPhysFirstFree = page; - if( giPhysLastFree < page ) - giPhysLastFree = page; - } - - #if USE_SUPER_BITMAP - // If the bitmap entry is not zero, set the bit free in the super bitmap - if(gaPageBitmaps[ page / 32 ] != 0 ) { - gaSuperBitmap[page / (32*32)] |= 1LL << ((page / 32) & 31); - } - #endif - LEAVE('-'); -} - -int MM_SetPageNode(tPAddr PAddr, void *Node) -{ - tPAddr page = PAddr >> 12; - tVAddr node_page = ((tVAddr)&gapPageNodes[page]) & ~(PAGE_SIZE-1); - - if( !MM_GetRefCount(PAddr) ) return 1; - - if( !MM_GetPhysAddr(node_page) ) { - if( !MM_Allocate(node_page) ) - return -1; - memset( (void*)node_page, 0, PAGE_SIZE ); - } - - gapPageNodes[page] = Node; - return 0; -} - -int MM_GetPageNode(tPAddr PAddr, void **Node) -{ - if( !MM_GetRefCount(PAddr) ) return 1; - PAddr >>= 12; - - if( !MM_GetPhysAddr( (tVAddr)&gapPageNodes[PAddr] ) ) { - *Node = NULL; - return 0; - } - - *Node = gapPageNodes[PAddr]; - return 0; -} - diff --git a/Kernel/include/tpl_mm_phys_stack.h b/Kernel/include/tpl_mm_phys_stack.h deleted file mode 100644 index 46ceb97d..00000000 --- a/Kernel/include/tpl_mm_phys_stack.h +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Acess2 Core - * - * include/tpl_mm_phys.h - * Physical Memory Manager Template - */ -#define DEBUG 0 - -/** - * \file tpl_mm_phys_stack.h - * \brief Template physical memory manager - * - * An extensible physical memory manager - * - * Usage: Requires NUM_MM_PHYS_RANGES to be set to the number of address - * "classes" wanted. - * MAX_PHYS_PAGES - Used to calculate structure sizes - * PADDR_TYPE - */ - -// === PROTOTYPES === -//void MM_InitPhys_Multiboot(tMBoot_Info *MBoot); -//tPAddr MM_AllocPhysRange(int Num, int Bits); -//tPAddr MM_AllocPhys(void); -//void MM_RefPhys(tPAddr PAddr); -//void MM_DerefPhys(tPAddr PAddr); - int MM_int_GetRangeID( tPAddr Addr ); - int MM_int_IsPhysUnavail( tPageNum Page ); - -// === GLOBALS === -tMutex glPhysicalPages; -//Uint64 *gaPageStacks[NUM_MM_PHYS_RANGES]; // Page stacks - int giPageStackSizes[NUM_MM_PHYS_RANGES]; // Points to the first unused slot -Uint32 *gaiPageReferences = (void*)MM_PAGE_COUNTS; // Reference Counts -Uint64 giMaxPhysPage = 0; // Maximum Physical page - -// === CODE === -/** - * \brief Initialise the physical memory map using a Multiboot 1 map - */ -void MM_Tpl_InitPhys(int MaxRAMPage) -{ - const int PAGE_SIZE = 0x1000; - const int pagesPerPageOnStack = PAGE_SIZE / sizeof(gaPageStack[0]); - int i; - tPageNum page; - -// ENTER("pMBoot=%p", MBoot); - - giMaxPhysPage = MaxRAMPage; - - tPAddr page = 0; - for( i = 0; i < NUM_MM_PHYS_RANGES; i ++ ) - { - for( ; page < giPageRangeMax[i] && page < giMaxPhysPage; page ++ ) - { - int rangeSize; - - rangeSize = MM_int_IsPhysUnavail(page); - if( rangeSize > 0 ) { - page += rangeSize; - continue; - } - // Page is avaliable for use - - // Check if the page stack is allocated - tVAddr stack_page = &gaPageStacks[i][giPageStackSizes[i]&~pagesPerPageOnStack]; - if( !MM_GetPhysAddr( stack_page ) ) { - MM_Map( stack_page, page*PAGE_SIZE ); - } - else { - gaPageStacks[i][ giPageStackSizes[i] ] = page; - giPageStackSizes[i] ++; - } - } - } - - LEAVE('-'); -} - -/** - * \brief Allocate a contiguous range of physical pages with a maximum - * bit size of \a MaxBits - * \param Pages Number of pages to allocate - * \param MaxBits Maximum size of the physical address - * \note If \a MaxBits is <= 0, any sized address is used (with preference - * to higher addresses) - */ -tPAddr MM_AllocPhysRange(int Pages, int MaxBits) -{ - tPAddr addr, ret; - int rangeID; - int nFree = 0, i; - - ENTER("iPages iBits", Pages, MaxBits); - - if( MaxBits <= 0 || MaxBits >= 64 ) // Speedup for the common case - rangeID = MM_PHYS_MAX; - else - rangeID = MM_int_GetRangeID( (1LL << MaxBits) - 1 ); - - LOG("rangeID = %i", rangeID); - - Mutex_Acquire(&glPhysicalPages); - - // Check if the range actually has any free pages - while(giPageStackSizes[rangeID] == 0 && rangeID) - rangeID --; - - LOG("rangeID = %i", rangeID); - - // What the? Oh, man. No free pages - if(giPageStackSizes[rangeID] == 0) { - Mutex_Release(&glPhysicalPages); - // TODO: Page out / attack the cache - // ATM. Just Warning - Warning(" MM_AllocPhysRange: Out of free pages"); - Log_Warning("Arch", - "Out of memory (unable to fulfil request for %i pages), zero remaining", - Pages - ); - LEAVE('i', 0); - return 0; - } - - // Check if there is enough in the range - if(giPhysRangeFree[rangeID] >= Pages) - { - LOG("{%i,0x%x -> 0x%x}", - giPhysRangeFree[rangeID], - giPhysRangeFirst[rangeID], giPhysRangeLast[rangeID] - ); - // Do a cheap scan, scanning upwards from the first free page in - // the range - nFree = 0; - addr = giPhysRangeFirst[ rangeID ]; - while( addr <= giPhysRangeLast[ rangeID ] ) - { - //Log(" MM_AllocPhysRange: addr = 0x%x", addr); - // Check the super bitmap - if( gaSuperBitmap[addr >> (6+6)] + 1 == 0 ) { - LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr); - nFree = 0; - addr += 1LL << (6+6); - addr &= ~0xFFF; // (1LL << 6+6) - 1 - continue; - } - // Check page block (64 pages) - if( gaMainBitmap[addr >> 6] + 1 == 0) { - LOG("nFree = %i = 0 (main) (0x%x)", nFree, addr); - nFree = 0; - addr += 1LL << (6); - addr &= ~0x3F; - continue; - } - // Check individual page - if( gaMainBitmap[addr >> 6] & (1LL << (addr & 63)) ) { - LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr); - nFree = 0; - addr ++; - continue; - } - nFree ++; - addr ++; - LOG("nFree(%i) == %i (0x%x)", nFree, Pages, addr); - if(nFree == Pages) - break; - } - LOG("nFree = %i", nFree); - // If we don't find a contiguous block, nFree will not be equal - // to Num, so we set it to zero and do the expensive lookup. - if(nFree != Pages) nFree = 0; - } - - if( !nFree ) - { - // Oops. ok, let's do an expensive check (scan down the list - // until a free range is found) - nFree = 1; - addr = giPhysRangeLast[ rangeID ]; - // TODO - Mutex_Release(&glPhysicalPages); - // TODO: Page out - // ATM. Just Warning - Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Pages); - Log_Warning("Arch", - "Out of memory (unable to fulfil request for %i pages)", - Pages - ); - LEAVE('i', 0); - return 0; - } - LOG("nFree = %i, addr = 0x%08x", nFree, addr); - - // Mark pages as allocated - addr -= Pages; - for( i = 0; i < Pages; i++, addr++ ) - { - gaMainBitmap[addr >> 6] |= 1LL << (addr & 63); - rangeID = MM_int_GetRangeID(addr << 12); - giPhysRangeFree[ rangeID ] --; - LOG("%x == %x", addr, giPhysRangeFirst[ rangeID ]); - if(addr == giPhysRangeFirst[ rangeID ]) - giPhysRangeFirst[ rangeID ] += 1; - } - ret = addr; // Save the return address - - // Update super bitmap - Pages += addr & (64-1); - addr &= ~(64-1); - Pages = (Pages + (64-1)) & ~(64-1); - for( i = 0; i < Pages/64; i++ ) - { - if( gaMainBitmap[ addr >> 6 ] + 1 == 0 ) - gaSuperBitmap[addr>>12] |= 1LL << ((addr >> 6) & 63); - } - - Mutex_Release(&glPhysicalPages); - LEAVE('x', ret << 12); - return ret << 12; -} - -/** - * \brief Allocate a single physical page, with no preference as to address - * size. - */ -tPAddr MM_AllocPhys(void) -{ - int i; - - // Hack to allow allocation during setup - for(i = 0; i < NUM_STATIC_ALLOC; i++) { - if( gaiStaticAllocPages[i] ) { - tPAddr ret = gaiStaticAllocPages[i]; - gaiStaticAllocPages[i] = 0; - Log("MM_AllocPhys: Return %x, static alloc %i", ret, i); - return ret; - } - } - - return MM_AllocPhysRange(1, -1); -} - -/** - * \brief Reference a physical page - */ -void MM_RefPhys(tPAddr PAddr) -{ - Uint64 page = PAddr >> 12; - - if( PAddr >> 12 > giMaxPhysPage ) return ; - - if( gaMainBitmap[ page >> 6 ] & (1LL << (page&63)) ) - { - // Reference again - gaMultiBitmap[ page >> 6 ] |= 1LL << (page&63); - gaiPageReferences[ page ] ++; - } - else - { - // Allocate - gaMainBitmap[page >> 6] |= 1LL << (page&63); - if( gaMainBitmap[page >> 6 ] + 1 == 0 ) - gaSuperBitmap[page>> 12] |= 1LL << ((page >> 6) & 63); - } -} - -/** - * \brief Dereference a physical page - */ -void MM_DerefPhys(tPAddr PAddr) -{ - Uint64 page = PAddr >> 12; - - if( PAddr >> 12 > giMaxPhysPage ) return ; - - if( gaMultiBitmap[ page >> 6 ] & (1LL << (page&63)) ) { - gaiPageReferences[ page ] --; - if( gaiPageReferences[ page ] == 1 ) - gaMultiBitmap[ page >> 6 ] &= ~(1LL << (page&63)); - if( gaiPageReferences[ page ] == 0 ) - gaMainBitmap[ page >> 6 ] &= ~(1LL << (page&63)); - } - else - gaMainBitmap[ page >> 6 ] &= ~(1LL << (page&63)); - - // Update the free counts if the page was freed - if( !(gaMainBitmap[ page >> 6 ] & (1LL << (page&63))) ) - { - int rangeID; - rangeID = MM_int_GetRangeID( PAddr ); - giPhysRangeFree[ rangeID ] ++; - if( giPhysRangeFirst[rangeID] > page ) - giPhysRangeFirst[rangeID] = page; - if( giPhysRangeLast[rangeID] < page ) - giPhysRangeLast[rangeID] = page; - } - - // If the bitmap entry is not -1, unset the bit in the super bitmap - if(gaMainBitmap[ page >> 6 ] + 1 != 0 ) { - gaSuperBitmap[page >> 12] &= ~(1LL << ((page >> 6) & 63)); - } -} - -/** - * \brief Takes a physical address and returns the ID of its range - * \param Addr Physical address of page - * \return Range ID from eMMPhys_Ranges - */ -int MM_int_GetRangeID( tPAddr Addr ) -{ - if(Addr >> 32) - return MM_PHYS_MAX; - else if(Addr >> 24) - return MM_PHYS_32BIT; - else if(Addr >> 20) - return MM_PHYS_24BIT; - else if(Addr >> 16) - return MM_PHYS_20BIT; - else - return MM_PHYS_16BIT; -} diff --git a/Kernel/include/vfs.h b/Kernel/include/vfs.h deleted file mode 100644 index 1aa073b2..00000000 --- a/Kernel/include/vfs.h +++ /dev/null @@ -1,503 +0,0 @@ -/* - * Acess2 - * VFS Common Header - */ -/** - * \file vfs.h - * \brief Acess VFS Layer - * - * The Acess Virtual File System (VFS) provides abstraction of multiple - * physical filesystems, network storage and devices (both hardware and - * virtual) to the user. - * - * The core of the VFS is the concept of a \ref tVFS_Node "VFS Node". - * A VFS Node represents a "file" in the VFS tree, this can be any sort - * of file (an ordinary file, a directory, a symbolic link or a device) - * depending on the bits set in the \ref tVFS_Node.Flags Flags field. - * - For more information see "VFS Node Flags" - */ -#ifndef _VFS_H -#define _VFS_H - -#include - -/** - * \brief Thread list datatype for VFS_Select - */ -typedef struct sVFS_SelectList tVFS_SelectList; - -typedef struct sVFS_NodeType tVFS_NodeType; - -/** - * \name tVFS_Node Flags - * \brief Flag values for tVFS_Node.Flags - * \{ - */ -//! \todo Is this still needed -#define VFS_FFLAG_READONLY 0x01 //!< Readonly File -/** - * \brief Directory Flag - * - * This flag marks the tVFS_Node as describing a directory, and says - * that the tVFS_Node.FindDir, tVFS_Node.ReadDir, tVFS_Node.MkNod and - * tVFS_Node.Relink function pointers are valid. - * For a directory the tVFS_Node.Size field contains the number of files - * within the directory, or -1 for undetermined. - */ -#define VFS_FFLAG_DIRECTORY 0x02 -/** - * \brief Symbolic Link Flag - * - * Marks a file as a symbolic link - */ -#define VFS_FFLAG_SYMLINK 0x04 -/** - * \brief Set User ID Flag - * - * Allows an executable file to change it's executing user to the file's - * owner. - * In the case of a directory, it means that all immediate children will - * inherit the UID of the parent. - */ -#define VFS_FFLAG_SETUID 0x08 -/** - * \brief Set Group ID Flag - * - * Allows an executable file to change it's executing group to the file's - * owning group. - * In the case of a directory, it means that all immediate children will - * inherit the GID of the parent. - */ -#define VFS_FFLAG_SETGID 0x10 - -/** - * \brief "Don't do Write-Back" Flag - * - * Stops the VFS from calling tVFS_Node.Write on dirty pages when a region - * is unmapped. Nice for read-only files and memory-only files (or - * pseudo-readonly filesystems) - */ -#define VFS_FFLAG_NOWRITEBACK -/** - * \} - */ - -/** - * \brief Represents a node (file or directory) in the VFS tree - * - * This structure provides the VFS with the functions required to read/write - * the file (or directory) that it represents. - */ -typedef struct sVFS_Node -{ - /** - * \name Identifiers - * \brief Fields used by the driver to identify what data this node - * corresponds to. - * \{ - */ - Uint64 Inode; //!< Inode ID - Must identify the node uniquely if tVFS_Driver.GetNodeFromINode is non-NULL - Uint ImplInt; //!< Implementation Usable Integer - void *ImplPtr; //!< Implementation Usable Pointer - /** - * \} - */ - - /** - * \name Node State - * \brief Stores the misc information about the node - * \{ - */ - int ReferenceCount; //!< Number of times the node is used - - Uint64 Size; //!< File Size - - Uint32 Flags; //!< File Flags - - /** - * \brief Pointer to cached data (FS Specific) - * \note The Inode_* functions will free when the node is uncached - * this if needed - */ - void *Data; - - /** - * \brief Node mutex - * \note Provided for the Filesystem driver's use - */ - tMutex Lock; - - /** - * \} - */ - - /** - * \name Times - * \{ - */ - Sint64 ATime; //!< Last Accessed Time - Sint64 MTime; //!< Last Modified Time - Sint64 CTime; //!< Creation Time - /** - * \} - */ - - /** - * \name Access control - * \{ - */ - tUID UID; //!< ID of Owning User - tGID GID; //!< ID of Owning Group - - int NumACLs; //!< Number of ACL entries - tVFS_ACL *ACLs; //!< Access Controll List pointer - /** - * \} - */ - - /** - * \name VFS_Select() fields - * \note Used by the VFS internals, drivers should use VFS_Mark* - * \{ - */ - int DataAvaliable; - tVFS_SelectList *ReadThreads; //!< Threads waiting to read - int BufferFull; - tVFS_SelectList *WriteThreads; //!< Threads waiting to write - int ErrorOccurred; - tVFS_SelectList *ErrorThreads; //!< Threads waiting for an error - /** - * \} - */ - - /** - * \name VFS_MMap() fields - * \{ - */ - void *MMapInfo; - /** - * \} - */ - - /** - * \brief Functions associated with the node - */ - tVFS_NodeType *Type; -} tVFS_Node; - -/** - * \brief Functions for a specific node type - */ -struct sVFS_NodeType -{ - /** - * \brief Debug name for the type - */ - const char *TypeName; - - /** - * \name Common Functions - * \brief Functions that are used no matter the value of .Flags - * \{ - */ - /** - * \brief Reference the node - * \param Node Pointer to this node - */ - void (*Reference)(struct sVFS_Node *Node); - /** - * \brief Close (dereference) the node - * \param Node Pointer to this node - * - * Usually .Close is used to write any changes to the node back to - * the persistent storage. - */ - void (*Close)(struct sVFS_Node *Node); - - /** - * \brief Send an IO Control - * \param Node Pointer to this node - * \param Id IOCtl call number - * \param Data Pointer to data to pass to the driver - * \return Implementation defined - */ - int (*IOCtl)(struct sVFS_Node *Node, int Id, void *Data); - - /** - * \} - */ - - /** - * \name Buffer Functions - * \brief Functions for accessing a buffer-type file (normal file or - * symbolic link) - * \{ - */ - - /** - * \brief Read from the file - * \param Node Pointer to this node - * \param Offset Byte offset in the file - * \param Length Number of bytes to read - * \param Buffer Destination for read data - * \return Number of bytes read - */ - Uint64 (*Read)(struct sVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); - /** - * \brief Write to the file - * \param Node Pointer to this node - * \param Offset Byte offser in the file - * \param Length Number of bytes to write - * \param Buffer Source of written data - * \return Number of bytes read - */ - Uint64 (*Write)(struct sVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); - - /** - * \brief Map a region of a file into memory - * \param Node Pointer to this node - * \param Offset Start of the region (page aligned) - * \param Length Length of the region (page aligned) - * \param Dest Location to which to map - * \return Boolean Failure - * \note If NULL, the VFS implements it using .Read - */ - int (*MMap)(struct sVFS_Node *Node, Uint64 Offset, int Length, void *Dest); - - /** - * \} - */ - - /** - * \name Directory Functions - * \{ - */ - /** - * \brief Find an directory entry by name - * \param Node Pointer to this node - * \param Name Name of the file wanted - * \return Pointer to the requested node or NULL if it cannot be found - * \note The node returned must be accessable until ::tVFS_Node.Close - * is called and ReferenceCount reaches zero. - */ - struct sVFS_Node *(*FindDir)(struct sVFS_Node *Node, const char *Name); - - /** - * \brief Read from a directory - * \param Node Pointer to this node - * \param Pos Offset in the directory - * \return Pointer to the name of the item on the heap (will be freed - * by the caller). If the directory end has been reached, NULL - * will be returned. - * If an item is required to be skipped either &::NULLNode, - * ::VFS_SKIP or ::VFS_SKIPN(0...1023) will be returned. - */ - char *(*ReadDir)(struct sVFS_Node *Node, int Pos); - - /** - * \brief Create a node in a directory - * \param Node Pointer to this node - * \param Name Name of the new child - * \param Flags Flags to apply to the new child (directory or symlink) - * \return Zero on Success, non-zero on error (see errno.h) - */ - int (*MkNod)(struct sVFS_Node *Node, const char *Name, Uint Flags); - - /** - * \brief Relink (Rename/Remove) a file/directory - * \param Node Pointer to this node - * \param OldName Name of the item to move/delete - * \param NewName New name (or NULL if unlinking is wanted) - * \return Zero on Success, non-zero on error (see errno.h) - */ - int (*Relink)(struct sVFS_Node *Node, const char *OldName, const char *NewName); - - /** - * \brief Link a node to a name - * \param Node Pointer to this node (directory) - * \param Child Node to create a new link to - * \param NewName Name for the new link - * \retur Zeron on success, non-zero on error (see errno.h) - */ - int (*Link)(struct sVFS_Node *Node, struct sVFS_Node *Child, const char *NewName); - - /** - * \} - */ -}; - -/** - * \brief VFS Driver (Filesystem) Definition - */ -typedef struct sVFS_Driver -{ - /** - * \brief Unique Identifier for this filesystem type - */ - const char *Name; - /** - * \brief Flags applying to this driver - */ - Uint Flags; - - /** - * \brief Callback to mount a device - */ - tVFS_Node *(*InitDevice)(const char *Device, const char **Options); - /** - * \brief Callback to unmount a device - */ - void (*Unmount)(tVFS_Node *Node); - /** - * \brief Retrieve a VFS node from an inode - */ - tVFS_Node *(*GetNodeFromINode)(tVFS_Node *RootNode, Uint64 InodeValue); - /** - * \brief Used internally (next driver in the chain) - */ - struct sVFS_Driver *Next; -} tVFS_Driver; - -// === GLOBALS === -//! \brief Maximum number of elements that can be skipped in one return -#define VFS_MAXSKIP ((void*)1024) -//! \brief Skip a single entry in readdir -#define VFS_SKIP ((void*)1) -//! \brief Skip \a n entries in readdir -#define VFS_SKIPN(n) ((void*)(n)) - -extern tVFS_Node NULLNode; //!< NULL VFS Node (Ignored/Skipped) -/** - * \name Static ACLs - * \brief Simple ACLs to aid writing drivers - * \{ - */ -extern tVFS_ACL gVFS_ACL_EveryoneRWX; //!< Everyone Read/Write/Execute -extern tVFS_ACL gVFS_ACL_EveryoneRW; //!< Everyone Read/Write -extern tVFS_ACL gVFS_ACL_EveryoneRX; //!< Everyone Read/Execute -extern tVFS_ACL gVFS_ACL_EveryoneRO; //!< Everyone Read only -/** - * \} - */ - -// === FUNCTIONS === -/** - * \fn int VFS_AddDriver(tVFS_Driver *Info) - * \brief Registers the driver with the DevFS layer - * \param Info Driver information structure - */ -extern int VFS_AddDriver(tVFS_Driver *Info); -/** - * \brief Get the information structure of a driver given its name - * \param Name Name of filesystem driver to find - */ -extern tVFS_Driver *VFS_GetFSByName(const char *Name); - - -/** - * \brief Prepare a node for use - */ -extern void VFS_InitNode(tVFS_Node *Node); - -/** - * \brief Clean up a node, ready for deletion - * \note This should ALWAYS be called before a node is freed, as it - * cleans up VFS internal structures. - */ -extern void VFS_CleanupNode(tVFS_Node *Node); - -/** - * \fn tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group) - * \brief Transforms Unix Permssions into Acess ACLs - * \param Mode Unix RWXrwxRWX mask - * \param Owner UID of the file's owner - * \param Group GID of the file's owning group - * \return An array of 3 Acess ACLs - */ -extern tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group); - -/** - * \brief Flags fro \a TypeFlag of VFS_SelectNode - * \{ - */ -#define VFS_SELECT_READ 0x01 -#define VFS_SELECT_WRITE 0x02 -#define VFS_SELECT_ERROR 0x04 -/** - * \} - */ - -/** - * \brief Wait for an event on a node - * \param Node Node to wait on - * \param Type Type of wait - * \param Timeout Time to wait (NULL for infinite wait) - * \param Name Name to show in debug output - * \return Number of nodes that actioned (0 or 1) - */ -extern int VFS_SelectNode(tVFS_Node *Node, int Type, tTime *Timeout, const char *Name); - -/** - * \brief Change the full flag on a node - */ -extern int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull); -/** - * \brief Alter the space avaliable flag on a node - */ -extern int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable); -/** - * \brief Alter the error flags on a node - */ -extern int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState); - -// --- Node Cache -- -/** - * \name Node Cache - * \brief Functions to allow a node to be cached in memory by the VFS - * - * These functions store a node for the driver, to prevent it from having - * to re-generate the node on each call to FindDir. It also allows for - * fast cleanup when a filesystem is unmounted. - * \{ - */ -/** - * \fn int Inode_GetHandle(void) - * \brief Gets a unique handle to the Node Cache - * \return A unique handle for use for the rest of the Inode_* functions - */ -extern int Inode_GetHandle(void); -/** - * \fn tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode) - * \brief Gets an inode from the node cache - * \param Handle A handle returned by Inode_GetHandle() - * \param Inode Value of the Inode field of the ::tVFS_Node you want - * \return A pointer to the cached node - */ -extern tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode); -/** - * \fn tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node) - * \brief Caches a node in the Node Cache - * \param Handle A handle returned by Inode_GetHandle() - * \param Node A pointer to the node to be cached (a copy is taken) - * \return A pointer to the node in the node cache - */ -extern tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node); -/** - * \fn int Inode_UncacheNode(int Handle, Uint64 Inode) - * \brief Dereferences (and removes if needed) a node from the cache - * \param Handle A handle returned by Inode_GetHandle() - * \param Inode Value of the Inode field of the ::tVFS_Node you want to remove - */ -extern void Inode_UncacheNode(int Handle, Uint64 Inode); -/** - * \fn void Inode_ClearCache(int Handle) - * \brief Clears the cache for a handle - * \param Handle A handle returned by Inode_GetHandle() - */ -extern void Inode_ClearCache(int Handle); - -/** - * \} - */ - -#endif diff --git a/Kernel/include/vfs_ext.h b/Kernel/include/vfs_ext.h deleted file mode 100644 index b4d5e2ad..00000000 --- a/Kernel/include/vfs_ext.h +++ /dev/null @@ -1,390 +0,0 @@ -/** - * \file vfs_ext.h - * \brief Exported VFS Definitions - * \author John Hodge (thePowersGang) - */ -#ifndef _VFS_EXT_H -#define _VFS_EXT_H - -//! Inode number type -typedef Uint64 tInode; - -//! Mountpoint identifier type -typedef Uint32 tMount; - -// === CONSTANTS === -//! Maximum size of a Memory Path generated by VFS_GetMemPath -#define VFS_MEMPATH_SIZE (3 + (BITS/4)*2) -/** - * \name Flags for VFS_Open - * \{ - */ -//! Open for execution -#define VFS_OPENFLAG_EXEC 0x01 -//! Open for reading -#define VFS_OPENFLAG_READ 0x02 -//! Open for writing -#define VFS_OPENFLAG_WRITE 0x04 -//! Do not resolve the final symbolic link -#define VFS_OPENFLAG_NOLINK 0x40 -//! Create the file if it doesn't exist -#define VFS_OPENFLAG_CREATE 0x80 -//! Open as a user -#define VFS_OPENFLAG_USER 0x8000 -/** - * \} - */ -//! Marks a VFS handle as belonging to the kernel -#define VFS_KERNEL_FLAG 0x40000000 - -//! Architectual maximum number of file descriptors -#define MAX_FILE_DESCS 128 - -/** - * \brief VFS_Seek directions - */ -enum eVFS_SeekDirs -{ - SEEK_SET = 1, //!< Set the current file offset - SEEK_CUR = 0, //!< Seek relative to the current position - SEEK_END = -1 //!< Seek from the end of the file backwards -}; - -/** - * \name ACL Permissions - * \{ - */ -/** - * \brief Readable - */ -#define VFS_PERM_READ 0x00000001 -/** - * \brief Writeable - */ -#define VFS_PERM_WRITE 0x00000002 -/** - * \brief Append allowed - */ -#define VFS_PERM_APPEND 0x00000004 -/** - * \brief Executable - */ -#define VFS_PERM_EXECUTE 0x00000008 -/** - * \brief All permissions granted - */ -#define VFS_PERM_ALL 0x7FFFFFFF // Mask for permissions -/** - * \brief Denies instead of granting permissions - * \note Denials take precedence - */ -#define VFS_PERM_DENY 0x80000000 // Inverts permissions -/** - * \} - */ - -/** - * \brief MMap protection flags - * \{ - */ -#define MMAP_PROT_READ 0x001 //!< Readable memory -#define MMAP_PROT_WRITE 0x002 //!< Writable memory -#define MMAP_PROT_EXEC 0x004 //!< Executable memory -/** - * \} - */ - -/** - * \brief MMap mapping flags - * \{ - */ -#define MMAP_MAP_SHARED 0x001 //!< Shared with all other users of the FD -#define MMAP_MAP_PRIVATE 0x002 //!< Local (COW) copy -#define MMAP_MAP_FIXED 0x004 //!< Load to a fixed address -#define MMAP_MAP_ANONYMOUS 0x008 //!< Not associated with a FD -/** - * \} - */ - -// -- System Call Structures --- -/** - * \brief ACL Defintion Structure - */ -typedef struct sVFS_ACL -{ - struct { - unsigned Group: 1; //!< Group (as opposed to user) flag - unsigned ID: 31; //!< ID of Group/User (-1 for nobody/world) - }; - struct { - unsigned Inv: 1; //!< Invert Permissions - unsigned Perms: 31; //!< Permission Flags - }; -} tVFS_ACL; - -/** - * \brief SYS_FINFO structure - */ -typedef struct sFInfo -{ - tMount mount; //!< Mountpoint ID - tInode inode; //!< Inode - Uint32 uid; //!< Owning User ID - Uint32 gid; //!< Owning Group ID - Uint32 flags; //!< File flags - Uint64 size; //!< File Size - Sint64 atime; //!< Last Accessed time - Sint64 mtime; //!< Last modified time - Sint64 ctime; //!< Creation time - Sint32 numacls; //!< Total number of ACL entries - tVFS_ACL acls[]; //!< ACL buffer (size is passed in the \a MaxACLs argument to VFS_FInfo) -} PACKED tFInfo; - -/** - * \brief fd_set for select() - */ -typedef struct -{ - //! Bitmap of set file descriptors - Uint16 flags[MAX_FILE_DESCS/16]; -} fd_set; - -/** - * \brief Clear a descriptor flag in a fd_set - * \param fd File descriptor to clear - * \param fdsetp Set to modify - */ -#define FD_CLR(fd, fdsetp) ((fdsetp)->flags[(fd)/16]&=~(1<<((fd)%16))) -/** - * \brief Set a descriptor flag in a fd_set - * \param fd File descriptor to set - * \param fdsetp Set to modify - */ -#define FD_SET(fd, fdsetp) ((fdsetp)->flags[(fd)/16]|=~(1<<((fd)%16))) -/** - * \brief Test a descriptor flag in a fd_set - * \param fd File descriptor to test - * \param fdsetp Set to modify - */ -#define FD_ISSET(fd, fdsetp) ((fdsetp)->flags[(fd)/16]&(1<<((fd)%16))) - -// === FUNCTIONS === -/** - * \brief Initialise the VFS (called by system.c) - * \return Boolean Success - */ -extern int VFS_Init(void); - -/** - * \brief Open a file - * \param Path Absolute or relative path to the file - * \param Flags Flags defining how to open the file - * \return VFS Handle (an integer) or -1 if an error occured - * \note Calls VFS_OpenEx(Path, Flags, 0) - */ -extern int VFS_Open(const char *Path, Uint Flags); -/** - * \brief Open a file - * \param Path Absolute or relative path to the file - * \param Flags Flags defining how to open the file - * \param Mode Mode for newly created file (POSIX compatability) - * \return VFS Handle (an integer) or -1 if an error occured - */ -extern int VFS_OpenEx(const char *Path, Uint Flags, Uint Mode); -/** - * \brief Opens a file via an open directory - * \note The file to open must be a direct child of the parent - * \param FD Parent Directory - * \param Name Child name - * \param Mode Open mode - * \return File handle (same as returned from VFS_Open) - */ -extern int VFS_OpenChild(int FD, const char *Name, Uint Mode); -/** - * \brief Opens a file given a mount ID and an inode number - * \param Mount Mount ID returned by FInfo - * \param Inode Inode number from FInfo - * \param Mode Open mode (see VFS_Open) - * \return File handle (same as VFS_Open) - */ -extern int VFS_OpenInode(Uint32 Mount, Uint64 Inode, int Mode); - -/** - * \brief Close a currently open file - * \param FD Handle returned by ::VFS_Open - */ -extern void VFS_Close(int FD); - -/** - * \brief Get file information from an open file - * \param FD File handle returned by ::VFS_Open - * \param Dest Destination for the read information - * \param MaxACLs Number of ACL slots allocated in the \a Dest structure - * \return Boolean Success - * - * If the \a NumACLs is smaller than the number of ACLs the node has, only - * \a NumACLs will be copied into \a Dest, but the tFInfo.numacls field - * will be set to the true ammount of ACLs. It is up to the user to do with - * this information how they like. - */ -extern int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs); -/** - * \brief Gets the permissions appling to a user/group. - * \param FD File handle returned by ::VFS_Open - * \param Dest ACL information structure to edit - * \return Boolean success - * - * This function sets the tVFS_ACL.Inv and tVFS_ACL.Perms fields to what - * permissions the user/group specied in tVFS_ACL.ID has on the file. - */ -extern int VFS_GetACL(int FD, tVFS_ACL *Dest); -/** - * \brief Changes the user's current working directory - * \param Dest New working directory (either absolute or relative to the current) - * \return Boolean Success - */ -extern int VFS_ChDir(const char *Dest); -/** - * \brief Change the current virtual root for the user - * \param New New virtual root (same as ::VFS_ChDir but cannot go - * above the current virtual root) - * \return Boolean success - */ -extern int VFS_ChRoot(const char *New); - -/** - * \brief Change the location of the current file pointer - * \param FD File handle returned by ::VFS_Open - * \param Offset Offset within the file to go to - * \param Whence A direction from ::eVFS_SeekDirs - * \return Boolean success - */ -extern int VFS_Seek(int FD, Sint64 Offset, int Whence); -/** - * \brief Returns the current file pointer - * \param FD File handle returned by ::VFS_Open - * \return Current file position - */ -extern Uint64 VFS_Tell(int FD); - -/** - * \brief Reads data from a file - * \param FD File handle returned by ::VFS_Open - * \param Length Number of bytes to read from the file - * \param Buffer Destination of read data - * \return Number of read bytes - */ -extern Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer); -/** - * \brief Writes data to a file - * \param FD File handle returned by ::VFS_Open - * \param Length Number of bytes to write to the file - * \param Buffer Source of written data - * \return Number of bytes written - */ -extern Uint64 VFS_Write(int FD, Uint64 Length, const void *Buffer); - -/** - * \brief Reads from a specific offset in the file - * \param FD File handle returned by ::VFS_Open - * \param Offset Byte offset in the file - * \param Length Number of bytes to read from the file - * \param Buffer Source of read data - * \return Number of bytes read - */ -extern Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer); -/** - * \brief Writes to a specific offset in the file - * \param FD File handle returned by ::VFS_Open - * \param Offset Byte offset in the file - * \param Length Number of bytes to write to the file - * \param Buffer Source of written data - * \return Number of bytes written - */ -extern Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer); - -/** - * \brief Sends an IOCtl request to the driver - * \param FD File handle returned by ::VFS_Open - * \param ID IOCtl call ID (driver specific) - * \param Buffer Data pointer to send to the driver - * \return Driver specific response - */ -extern int VFS_IOCtl(int FD, int ID, void *Buffer); - -/** - * \brief Creates a VFS Memory path from a pointer and size - * \param Dest Destination for the created path - * \param Base Base of the memory file - * \param Length Length of the memory buffer - * \note A maximum of VFS_MEMPATH_SIZE bytes will be required in \a Dest - */ -extern void VFS_GetMemPath(char *Dest, void *Base, Uint Length); -/** - * \brief Gets the canoical (true) path of a file - * \param Path File path (may contain symlinks, relative elements etc.) - * \return Absolute path with no symlinks - */ -extern char *VFS_GetTruePath(const char *Path); - -/** - * \brief Mounts a filesystem - * \param Device Device to mount - * \param MountPoint Location to mount - * \param Filesystem Filesystem to use - * \param Options Options string to pass the driver - * \return 1 on succes, -1 on error - */ -extern int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options); -/** - * \brief Create a new directory - * \param Path Path to new directory (absolute or relative) - * \return Boolean success - * \note The parent of the directory must exist - */ -extern int VFS_MkDir(const char *Path); -/** - * \brief Create a symbolic link - * \param Name Name of the symbolic link - * \param Link File the symlink points to - * \return Boolean success (\a Link is not tested for existence) - */ -extern int VFS_Symlink(const char *Name, const char *Link); -/** - * \brief Read from a directory - * \param FD File handle returned by ::VFS_Open - * \param Dest Destination array for the file name (max 255 bytes) - * \return Boolean Success - */ -extern int VFS_ReadDir(int FD, char *Dest); -/** - * \brief Wait for an aciton on a file descriptor - * \param MaxHandle Maximum set handle in \a *Handles - * \param ReadHandles Handles to wait for data on - * \param WriteHandles Handles to wait to write to - * \param ErrHandles Handle to wait for errors on - * \param Timeout Timeout for select() (if null, there is no timeout), if zero select() is non blocking - * \param ExtraEvents Extra event set to wait on - * \param IsKernel Use kernel handles as opposed to user handles - * \return Number of handles that actioned - */ -extern int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel); - -/** - * \brief Map a file into memory - * \param DestHint Suggested place for read data - * \param Length Size of area to map - * \param Protection Protection type (see `man mmap`) - * \param Flags Mapping flags - * \param FD File descriptor to load from - * \param Offset Start of region - */ -extern void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset); - -/** - * \brief Unmap memory allocated by VFS_MMap - * \param Addr Address of data to unmap - * \param Length Length of data - */ -extern int VFS_MUnmap(void *Addr, size_t Length); -#endif diff --git a/Kernel/include/vfs_int.h b/Kernel/include/vfs_int.h deleted file mode 100644 index a8eadb6d..00000000 --- a/Kernel/include/vfs_int.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Acess Micro - VFS Server Ver 1 - */ -#ifndef _VFS_INT_H -#define _VFS_INT_H - -#include "vfs.h" - -// === TYPES === -typedef struct sVFS_Mount { - struct sVFS_Mount *Next; - char *MountPoint; - size_t MountPointLen; - Uint32 Identifier; - char *Device; - char *Options; - tVFS_Driver *Filesystem; - tVFS_Node *RootNode; - char StrData[]; -} tVFS_Mount; - -typedef struct sVFS_Handle { - tVFS_Node *Node; - tVFS_Mount *Mount; - Uint64 Position; - Uint Mode; -} tVFS_Handle; - -typedef struct sVFS_Proc { - struct sVFS_Proc *Next; - int ID; - int CwdLen; - Uint UID, GID; - char *Cwd; - int MaxHandles; - tVFS_Handle Handles[]; -} tVFS_Proc; - -typedef struct sVFS_MMapPage { - Uint64 FileOffset; - tPAddr PAddr; -} tVFS_MMapPage; - -// === GLOBALS === -extern tVFS_Mount *gVFS_Mounts; - -// === PROTOTYPES === -// --- open.c --- -extern char *VFS_GetAbsPath(const char *Path); -extern tVFS_Node *VFS_ParsePath(const char *Path, char **TruePath, tVFS_Mount **MountPoint); -extern tVFS_Handle *VFS_GetHandle(int FD); -// --- acls.c --- -extern int VFS_CheckACL(tVFS_Node *Node, Uint Permissions); -// --- mount.c --- -extern tVFS_Mount *VFS_GetMountByIdent(Uint32 MountID); -// --- dir.c --- -extern int VFS_MkNod(const char *Path, Uint Flags); - - -// --- VFS Helpers --- -static inline void _CloseNode(tVFS_Node *Node) -{ - if(Node && Node->Type && Node->Type->Close) - Node->Type->Close( Node ); -} - - -#endif diff --git a/Kernel/include/vfs_ramfs.h b/Kernel/include/vfs_ramfs.h deleted file mode 100644 index 8bbc8b58..00000000 --- a/Kernel/include/vfs_ramfs.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * AcessMicro VFS - * - RAM Filesystem Coommon Structures - */ -#ifndef _RAMFS_H -#define _RAMFS_H -#include - -typedef struct sRamFS_File { - struct sRamFS_File *Next; - struct sRamFS_File *Parent; - char Name[32]; - tVFS_Node Node; - union { - struct sRamFS_File *FirstChild; - char *Bytes; - } Data; -} tRamFS_File; - -#endif diff --git a/Kernel/include/vfs_threads.h b/Kernel/include/vfs_threads.h deleted file mode 100644 index 95ec2278..00000000 --- a/Kernel/include/vfs_threads.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * include/vfs_threads.h - * - Handle maintainance functions for the VFS used by threading code - */ -#ifndef _VFS_THREADS_H_ -#define _VFS_THREADS_H_ - -// === FUNCTIONS === -extern void VFS_ReferenceUserHandles(void); -extern void VFS_CloseAllUserHandles(void); - -extern void *VFS_SaveHandles(int NumFDs, int *FDs); -extern void VFS_RestoreHandles(int NumFDs, void *Handles); -extern void VFS_FreeSavedHandles(int NumFDs, void *Handles); - -#endif diff --git a/Kernel/include/workqueue.h b/Kernel/include/workqueue.h deleted file mode 100644 index ff0c7f4c..00000000 --- a/Kernel/include/workqueue.h +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * workqueue.h - * - FIFO Queue - */ -#ifndef _WORKQUEUE_H_ -#define _WORKQUEUE_H_ - -#include - -typedef struct sWorkqueue tWorkqueue; - -struct sWorkqueue -{ - tShortSpinlock Protector; - const char *Name; - int NextOffset; - - void *Head; - void *Tail; - struct sThread *Sleeper; -}; - -extern void Workqueue_Init(tWorkqueue *Queue, const char *Name, size_t NextOfset); -extern void *Workqueue_GetWork(tWorkqueue *Queue); -extern void Workqueue_AddWork(tWorkqueue *Queue, void *Ptr); - -#endif - diff --git a/Kernel/lib.c b/Kernel/lib.c deleted file mode 100644 index 60cbd4c7..00000000 --- a/Kernel/lib.c +++ /dev/null @@ -1,1084 +0,0 @@ -/* - * Acess2 - * Common Library Functions - */ -#include -#include - -// === CONSTANTS === -#define RANDOM_SEED 0xACE55052 -#define RANDOM_A 0x00731ADE -#define RANDOM_C 12345 -#define RANDOM_SPRUCE 0xf12b039 -// Jan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec -const short DAYS_BEFORE[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; -#define UNIX_TO_2K ((30*365*3600*24) + (7*3600*24)) //Normal years + leap years - -// === PROTOTYPES === -#if 0 - int atoi(const char *string); -void itoa(char *buf, Uint64 num, int base, int minLength, char pad); - int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args); - int sprintf(char *__s, const char *__format, ...); -#endif - int tolower(int c); -#if 0 - int strucmp(const char *Str1, const char *Str2); -char *strchr(const char *__s, int __c); - int strpos(const char *Str, char Ch); - Uint8 ByteSum(void *Ptr, int Size); -size_t strlen(const char *__s); -char *strcpy(char *__str1, const char *__str2); -char *strncpy(char *__str1, const char *__str2, size_t max); - int strcmp(const char *str1, const char *str2); - int strncmp(const char *str1, const char *str2, size_t num); -char *_strdup(const char *File, int Line, const char *Str); -char **str_split(const char *__str, char __ch); - int strpos8(const char *str, Uint32 Search); - int ReadUTF8(Uint8 *str, Uint32 *Val); - int WriteUTF8(Uint8 *str, Uint32 Val); - int DivUp(int num, int dem); -Sint64 timestamp(int sec, int mins, int hrs, int day, int month, int year); -void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms); - int rand(void); - - int CheckString(char *String); - int CheckMem(void *Mem, int NumBytes); - - int ModUtil_LookupString(char **Array, char *Needle); - int ModUtil_SetIdent(char *Dest, char *Value); - - int Hex(char *Dest, size_t Size, const Uint8 *SourceData); - int UnHex(Uint8 *Dest, size_t DestSize, const char *SourceString); -#endif - -// === EXPORTS === -EXPORT(atoi); -EXPORT(itoa); -EXPORT(vsnprintf); -EXPORT(sprintf); -EXPORT(tolower); -EXPORT(strucmp); -EXPORT(strchr); -EXPORT(strpos); -EXPORT(ByteSum); -EXPORT(strlen); -EXPORT(strcpy); -EXPORT(strncpy); -EXPORT(strcat); -EXPORT(strncat); -EXPORT(strcmp); -EXPORT(strncmp); -//EXPORT(strdup); -EXPORT(_strdup); // Takes File/Line too -EXPORT(str_split); -EXPORT(strpos8); -EXPORT(DivUp); -EXPORT(ReadUTF8); -EXPORT(WriteUTF8); -EXPORT(timestamp); -EXPORT(CheckString); -EXPORT(CheckMem); -EXPORT(ModUtil_LookupString); -EXPORT(ModUtil_SetIdent); -EXPORT(UnHex); -EXPORT(SwapEndian16); -EXPORT(SwapEndian32); -EXPORT(memmove); - -// === CODE === -/** - * \brief Convert a string into an integer - */ -int atoi(const char *string) -{ - int ret = 0; - ParseInt(string, &ret); - return ret; -} -int ParseInt(const char *string, int *Val) -{ - int ret = 0; - int bNeg = 0; - const char *orig_string = string; - - //Log("atoi: (string='%s')", string); - - // Clear non-numeric characters - while( !('0' <= *string && *string <= '9') && *string != '-' ) string++; - if( *string == '-' ) { - bNeg = 1; - while( !('0' <= *string && *string <= '9') ) string++; - } - - if(*string == '0') - { - string ++; - if(*string == 'x') - { - // Hex - string ++; - for( ;; string ++ ) - { - if('0' <= *string && *string <= '9') { - ret *= 16; - ret += *string - '0'; - } - else if('A' <= *string && *string <= 'F') { - ret *= 16; - ret += *string - 'A' + 10; - } - else if('a' <= *string && *string <= 'f') { - ret *= 16; - ret += *string - 'a' + 10; - } - else - break; - } - } - else // Octal - { - for( ; '0' <= *string && *string <= '7'; string ++ ) - { - ret *= 8; - ret += *string - '0'; - } - } - } - else // Decimal - { - for( ; '0' <= *string && *string <= '9'; string++) - { - ret *= 10; - ret += *string - '0'; - } - // Error check - if( ret == 0 ) return 0; - } - - if(bNeg) ret = -ret; - - //Log("atoi: RETURN %i", ret); - - if(Val) *Val = ret; - - return string - orig_string; -} - -static const char cUCDIGITS[] = "0123456789ABCDEF"; -/** - * \fn void itoa(char *buf, Uint64 num, int base, int minLength, char pad) - * \brief Convert an integer into a character string - */ -void itoa(char *buf, Uint64 num, int base, int minLength, char pad) -{ - char tmpBuf[64+1]; - int pos=0, i; - Uint64 rem; - - // Sanity check - if(!buf) return; - - // Sanity Check - if(base > 16 || base < 2) { - buf[0] = 0; - return; - } - - // Convert - while(num > base-1) { - num = DivMod64U(num, base, &rem); // Shift `num` and get remainder - tmpBuf[pos] = cUCDIGITS[ rem ]; - pos++; - } - tmpBuf[pos++] = cUCDIGITS[ num ]; // Last digit of `num` - - // Put in reverse - i = 0; - minLength -= pos; - while(minLength-- > 0) buf[i++] = pad; - while(pos-- > 0) buf[i++] = tmpBuf[pos]; // Reverse the order of characters - buf[i] = 0; -} - -/** - * \brief Append a character the the vsnprintf output - */ -#define PUTCH(c) _putch(c) -#define GETVAL() do {\ - if(isLongLong) val = va_arg(args, Uint64);\ - else val = va_arg(args, unsigned int);\ - }while(0) -/** - * \brief VArg String Number Print Formatted - */ -int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args) -{ - char c, pad = ' '; - int minSize = 0, precision = -1, len; - char tmpBuf[34]; // For Integers - const char *p = NULL; - int isLongLong = 0; - Uint64 val; - size_t pos = 0; - // Flags - int bPadLeft = 0; - - auto void _putch(char ch); - - void _putch(char ch) - { - if(pos < __maxlen) - { - if(__s) __s[pos] = ch; - pos ++; - } - } - - while((c = *__format++) != 0) - { - // Non control character - if(c != '%') { PUTCH(c); continue; } - - c = *__format++; - if(c == '\0') break; - - // Literal % - if(c == '%') { PUTCH('%'); continue; } - - // Pointer - Done first for debugging - if(c == 'p') { - Uint ptr = va_arg(args, Uint); - PUTCH('*'); PUTCH('0'); PUTCH('x'); - for( len = BITS/4; len --; ) - PUTCH( cUCDIGITS[ (ptr>>(len*4))&15 ] ); - continue ; - } - - // - Padding Side Flag - if(c == '-') { - bPadLeft = 1; - c = *__format++; - } - - // - Padding - if(c == '0') { - pad = '0'; - c = *__format++; - } - else - pad = ' '; - - // - Minimum length - if(c == '*') { // Dynamic length - minSize = va_arg(args, unsigned int); - c = *__format++; - } - else if('1' <= c && c <= '9') - { - minSize = 0; - while('0' <= c && c <= '9') - { - minSize *= 10; - minSize += c - '0'; - c = *__format++; - } - } - else - minSize = 0; - - // - Precision - precision = -1; - if( c == '.' ) { - c = *__format++; - - if(c == '*') { // Dynamic length - precision = va_arg(args, unsigned int); - c = *__format++; - } - else if('1' <= c && c <= '9') - { - precision = 0; - while('0' <= c && c <= '9') - { - precision *= 10; - precision += c - '0'; - c = *__format++; - } - } - } - - // - Default, Long or LongLong? - isLongLong = 0; - if(c == 'l') // Long is actually the default on x86 - { - c = *__format++; - if(c == 'l') { - c = *__format++; - isLongLong = 1; - } - } - - // - Now get the format code - p = tmpBuf; - switch(c) - { - case 'd': - case 'i': - GETVAL(); - if( isLongLong && val >> 63 ) { - PUTCH('-'); - val = -val; - } - else if( !isLongLong && val >> 31 ) { - PUTCH('-'); - val = -(Sint32)val; - } - itoa(tmpBuf, val, 10, minSize, pad); - goto printString; - case 'u': // Unsigned - GETVAL(); - itoa(tmpBuf, val, 10, minSize, pad); - goto printString; - case 'P': // Physical Address - PUTCH('0'); - PUTCH('x'); - if(sizeof(tPAddr) > 4) isLongLong = 1; - GETVAL(); - itoa(tmpBuf, val, 16, minSize, pad); - goto printString; - case 'X': // Hex - if(BITS == 64) - isLongLong = 1; // TODO: Handle non-x86 64-bit archs - GETVAL(); - itoa(tmpBuf, val, 16, minSize, pad); - goto printString; - - case 'x': // Lower case hex - GETVAL(); - itoa(tmpBuf, val, 16, minSize, pad); - goto printString; - case 'o': // Octal - GETVAL(); - itoa(tmpBuf, val, 8, minSize, pad); - goto printString; - case 'b': - GETVAL(); - itoa(tmpBuf, val, 2, minSize, pad); - goto printString; - - case 'B': //Boolean - val = va_arg(args, unsigned int); - if(val) p = "True"; - else p = "False"; - goto printString; - - // String - Null Terminated Array - case 's': - p = va_arg(args, char*); // Get Argument - if( !p || !CheckString(p) ) p = "(inval)"; // Avoid #PFs - printString: - if(!p) p = "(null)"; - len = strlen(p); - if( !bPadLeft ) while(len++ < minSize) PUTCH(pad); - while(*p && precision--) PUTCH(*p++); - if( bPadLeft ) while(len++ < minSize) PUTCH(pad); - break; - - case 'C': // Non-Null Terminated Character Array - p = va_arg(args, char*); - if( !CheckMem(p, minSize) ) continue; // No #PFs please - if(!p) goto printString; - while(minSize--) PUTCH(*p++); - break; - - // Single Character - case 'c': - default: - GETVAL(); - PUTCH( (Uint8)val ); - break; - } - } - - if(__s && pos != __maxlen) - __s[pos] = '\0'; - - return pos; -} -#undef PUTCH - -/** - */ -int sprintf(char *__s, const char *__format, ...) -{ - va_list args; - int ret; - - va_start(args, __format); - ret = vsnprintf(__s, -1, __format, args); - va_end(args); - - return ret; -} - -/** - * \fn int tolower(int c) - * \brief Converts a character to lower case - */ -int tolower(int c) -{ - if('A' <= c && c <= 'Z') - return c - 'A' + 'a'; - return c; -} - -/** - * \fn int strucmp(const char *Str1, const char *Str2) - * \brief Compare \a Str1 and \a Str2 case-insensitively - */ -int strucmp(const char *Str1, const char *Str2) -{ - while(*Str1 && tolower(*Str1) == tolower(*Str2)) - Str1++, Str2++; - return tolower(*Str1) - tolower(*Str2); -} - -/** - * \brief Locate a byte in a string - */ -char *strchr(const char *__s, int __c) -{ - for( ; *__s; __s ++ ) - { - if( *__s == __c ) return (char*)__s; - } - return NULL; -} - -/** - * \fn int strpos(const char *Str, char Ch) - * \brief Search a string for an ascii character - */ -int strpos(const char *Str, char Ch) -{ - int pos; - for(pos=0;Str[pos];pos++) - { - if(Str[pos] == Ch) return pos; - } - return -1; -} - -/** - * \fn Uint8 ByteSum(void *Ptr, int Size) - * \brief Adds the bytes in a memory region and returns the sum - */ -Uint8 ByteSum(const void *Ptr, int Size) -{ - Uint8 sum = 0; - const Uint8 *data = Ptr; - while(Size--) sum += *(data++); - return sum; -} - -/** - * \fn size_t strlen(const char *__str) - * \brief Get the length of string - */ -size_t strlen(const char *__str) -{ - size_t ret = 0; - while(*__str++) ret++; - return ret; -} - -/** - * \brief Copy a string to a new location - */ -char *strcpy(char *__str1, const char *__str2) -{ - while(*__str2) - *__str1++ = *__str2++; - *__str1 = '\0'; // Terminate String - return __str1; -} - -/** - * \brief Copy a string to a new location - * \note Copies at most `max` chars - */ -char *strncpy(char *__str1, const char *__str2, size_t __max) -{ - while(*__str2 && __max-- >= 1) - *__str1++ = *__str2++; - if(__max) - *__str1 = '\0'; // Terminate String - return __str1; -} - -/** - * \brief Append a string to another - */ -char *strcat(char *__dest, const char *__src) -{ - while(*__dest++); - __dest--; - while(*__src) - *__dest++ = *__src++; - *__dest = '\0'; - return __dest; -} - -/** - * \brief Append at most \a n chars to a string from another - * \note At most n+1 chars are written (the dest is always zero terminated) - */ -char *strncat(char *__dest, const char *__src, size_t n) -{ - while(*__dest++); - while(*__src && n-- >= 1) - *__dest++ = *__src++; - *__dest = '\0'; - return __dest; -} - -/** - * \fn int strcmp(const char *str1, const char *str2) - * \brief Compare two strings return the difference between - * the first non-matching characters. - */ -int strcmp(const char *str1, const char *str2) -{ - while(*str1 && *str1 == *str2) - str1++, str2++; - return *str1 - *str2; -} - -/** - * \fn int strncmp(const char *Str1, const char *Str2, size_t num) - * \brief Compare strings \a Str1 and \a Str2 to a maximum of \a num characters - */ -int strncmp(const char *Str1, const char *Str2, size_t num) -{ - if(num == 0) return 0; // TODO: Check what should officially happen here - while(--num && *Str1 && *Str1 == *Str2) - Str1++, Str2++; - return *Str1-*Str2; -} - -#if 0 -/** - * \fn char *strdup(const char *Str) - * \brief Duplicates a string - */ -char *strdup(const char *Str) -{ - char *ret; - ret = malloc(strlen(Str)+1); - if( !ret ) return NULL; - strcpy(ret, Str); - return ret; -} -#else - -/** - * \fn char *_strdup(const char *File, int Line, const char *Str) - * \brief Duplicates a string - */ -char *_strdup(const char *File, int Line, const char *Str) -{ - char *ret; - ret = Heap_Allocate(File, Line, strlen(Str)+1); - if( !ret ) return NULL; - strcpy(ret, Str); - return ret; -} -#endif - -/** - * \brief Split a string using the passed character - * \return NULL terminated array of strings on the heap - * \param __str String to split - * \param __ch Character to split by - */ -char **str_split(const char *__str, char __ch) -{ - int i, j; - int len = 1; - char **ret; - char *start; - - for( i = 0; __str[i]; i++ ) - { - if(__str[i] == __ch) - len ++; - } - - ret = malloc( sizeof(char*)*(len+1) + (i + 1) ); - if( !ret ) return NULL; - - j = 1; - start = (char *)&ret[len+1]; - ret[0] = start; - for( i = 0; __str[i]; i++ ) - { - if(__str[i] == __ch) { - *start++ = '\0'; - ret[j++] = start; - } - else { - *start++ = __str[i]; - } - } - *start = '\0'; - ret[j] = NULL; - - return ret; -} - -/** - * \fn int DivUp(int num, int dem) - * \brief Divide two numbers, rounding up - * \param num Numerator - * \param dem Denominator - */ -int DivUp(int num, int dem) -{ - return (num+dem-1)/dem; -} - -/** - * \fn int strpos8(const char *str, Uint32 search) - * \brief Search a string for a UTF-8 character - */ -int strpos8(const char *str, Uint32 Search) -{ - int pos; - Uint32 val = 0; - for(pos=0;str[pos];pos++) - { - // ASCII Range - if(Search < 128) { - if(str[pos] == Search) return pos; - continue; - } - if(*(Uint8*)(str+pos) < 128) continue; - - pos += ReadUTF8( (Uint8*)&str[pos], &val ); - if(val == Search) return pos; - } - return -1; -} - -/** - * \fn int ReadUTF8(Uint8 *str, Uint32 *Val) - * \brief Read a UTF-8 character from a string - */ -int ReadUTF8(const Uint8 *str, Uint32 *Val) -{ - *Val = 0xFFFD; // Assume invalid character - - // ASCII - if( !(*str & 0x80) ) { - *Val = *str; - return 1; - } - - // Middle of a sequence - if( (*str & 0xC0) == 0x80 ) { - return 1; - } - - // Two Byte - if( (*str & 0xE0) == 0xC0 ) { - *Val = (*str & 0x1F) << 6; // Upper 6 Bits - str ++; - if( (*str & 0xC0) != 0x80) return -1; // Validity check - *Val |= (*str & 0x3F); // Lower 6 Bits - return 2; - } - - // Three Byte - if( (*str & 0xF0) == 0xE0 ) { - *Val = (*str & 0x0F) << 12; // Upper 4 Bits - str ++; - if( (*str & 0xC0) != 0x80) return -1; // Validity check - *Val |= (*str & 0x3F) << 6; // Middle 6 Bits - str ++; - if( (*str & 0xC0) != 0x80) return -1; // Validity check - *Val |= (*str & 0x3F); // Lower 6 Bits - return 3; - } - - // Four Byte - if( (*str & 0xF1) == 0xF0 ) { - *Val = (*str & 0x07) << 18; // Upper 3 Bits - str ++; - if( (*str & 0xC0) != 0x80) return -1; // Validity check - *Val |= (*str & 0x3F) << 12; // Middle-upper 6 Bits - str ++; - if( (*str & 0xC0) != 0x80) return -1; // Validity check - *Val |= (*str & 0x3F) << 6; // Middle-lower 6 Bits - str ++; - if( (*str & 0xC0) != 0x80) return -1; // Validity check - *Val |= (*str & 0x3F); // Lower 6 Bits - return 4; - } - - // UTF-8 Doesn't support more than four bytes - return 4; -} - -/** - * \fn int WriteUTF8(Uint8 *str, Uint32 Val) - * \brief Write a UTF-8 character sequence to a string - */ -int WriteUTF8(Uint8 *str, Uint32 Val) -{ - // ASCII - if( Val < 128 ) { - if( str ) { - *str = Val; - } - return 1; - } - - // Two Byte - if( Val < 0x8000 ) { - if( str ) { - *str++ = 0xC0 | (Val >> 6); - *str++ = 0x80 | (Val & 0x3F); - } - return 2; - } - - // Three Byte - if( Val < 0x10000 ) { - if( str ) { - *str++ = 0xE0 | (Val >> 12); - *str++ = 0x80 | ((Val >> 6) & 0x3F); - *str++ = 0x80 | (Val & 0x3F); - } - return 3; - } - - // Four Byte - if( Val < 0x110000 ) { - if( str ) { - *str++ = 0xF0 | (Val >> 18); - *str++ = 0x80 | ((Val >> 12) & 0x3F); - *str++ = 0x80 | ((Val >> 6) & 0x3F); - *str++ = 0x80 | (Val & 0x3F); - } - return 4; - } - - // UTF-8 Doesn't support more than four bytes - return 0; -} - -/** - * \fn Uint64 timestamp(int sec, int mins, int hrs, int day, int month, int year) - * \brief Converts a date into an Acess Timestamp - */ -Sint64 timestamp(int sec, int min, int hrs, int day, int month, int year) -{ - int is_leap; - Sint64 stamp; - - if( !(0 <= sec && sec < 60) ) return 0; - if( !(0 <= min && min < 60) ) return 0; - if( !(0 <= hrs && hrs < 24) ) return 0; - if( !(0 <= day && day < 31) ) return 0; - if( !(0 <= month && month < 12) ) return 0; - - stamp = DAYS_BEFORE[month] + day; - - // Every 4 years - // - every 100 years - // + every 400 years - is_leap = (year % 4 == 0) - (year % 100 == 0) + (year % 400 == 0); - ASSERT(is_leap == 0 || is_leap == 1); - - if( is_leap && month > 1 ) // Leap year and after feb - stamp += 1; - - // Get seconds before the first of specified year - year -= 2000; // Base off Y2K - // base year days + total leap year days - stamp += year*365 + (year/400) - (year/100) + (year/4); - - stamp *= 3600*24; - stamp += UNIX_TO_2K; - stamp += sec; - stamp += min*60; - stamp += hrs*3600; - - return stamp * 1000; -} - -void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms) -{ - int is_leap = 0, i; - - auto Sint64 DivMod64(Sint64 N, Sint64 D, Sint64 *R); - - Sint64 DivMod64(Sint64 N, Sint64 D, Sint64 *R) - { - int sign = (N < 0) != (D < 0); - if(N < 0) N = -N; - if(D < 0) D = -D; - if(sign) - return -DivMod64U(N, D, (Uint64*)R); - else - return DivMod64U(N, D, (Uint64*)R); - } - - // Get time - // TODO: Leap-seconds? - { - Sint64 rem; - TS = DivMod64( TS, 1000, &rem ); - *ms = rem; - TS = DivMod64( TS, 60, &rem ); - *sec = rem; - TS = DivMod64( TS, 60, &rem ); - *mins = rem; - TS = DivMod64( TS, 24, &rem ); - *hrs = rem; - } - - // Adjust to Y2K - TS -= UNIX_TO_2K/(3600*24); - - // Year (400 yr blocks) - (400/4-3) leap years - *year = 400 * DivMod64( TS, 365*400 + (400/4-3), &TS ); - if( TS < 366 ) // First year in 400 is a leap - is_leap = 1; - else - { - // 100 yr blocks - 100/4-1 leap years - *year += 100 * DivMod64( TS, 365*100 + (100/4-1), &TS ); - if( TS < 366 ) // First year in 100 isn't a leap - is_leap = 0; - else - { - *year += 4 * DivMod64( TS, 365*4 + 1, &TS ); - if( TS < 366 ) // First year in 4 is a leap - is_leap = 1; - else - { - *year += DivMod64( TS, 356, &TS ); - } - } - } - *year += 2000; - - ASSERT(TS >= 0); - - *day = 0; - // Month (if after the first of march, which is 29 Feb in a leap year) - if( is_leap && TS > DAYS_BEFORE[2] ) { - TS -= 1; // Shifts 29 Feb to 28 Feb - *day = 1; - } - // Get what month it is - for( i = 0; i < 12; i ++ ) { - if( TS < DAYS_BEFORE[i] ) - break; - } - *month = i - 1; - // Get day - TS -= DAYS_BEFORE[i-1]; - *day += TS; // Plus offset from leap handling above -} - -/** - * \fn int rand() - * \brief Pseudo random number generator - */ -int rand(void) -{ - #if 0 - static Uint state = RANDOM_SEED; - Uint old = state; - // Get the next state value - giRandomState = (RANDOM_A*state + RANDOM_C); - // Check if it has changed, and if it hasn't, change it - if(state == old) state += RANDOM_SPRUCE; - return state; - #else - // http://en.wikipedia.org/wiki/Xorshift - // 2010-10-03 - static Uint32 x = 123456789; - static Uint32 y = 362436069; - static Uint32 z = 521288629; - static Uint32 w = 88675123; - Uint32 t; - - t = x ^ (x << 11); - x = y; y = z; z = w; - return w = w ^ (w >> 19) ^ t ^ (t >> 8); - #endif -} - -/* * - * \name Memory Validation - * \{ - */ -/** - * \brief Checks if a string resides fully in valid memory - */ -int CheckString(const char *String) -{ - tVAddr addr; - int bUser; - - addr = (tVAddr)String; - - if( !MM_GetPhysAddr( addr ) ) - return 0; - - // Check 1st page - bUser = MM_IsUser( addr ); - - while( *(char*)addr ) - { - if( (addr & (PAGE_SIZE-1)) == 0 ) - { - if(bUser && !MM_IsUser(addr) ) - return 0; - if(!bUser && !MM_GetPhysAddr(addr) ) - return 0; - } - addr ++; - } - return 1; -} - -/** - * \brief Check if a sized memory region is valid memory - * \return Boolean success - */ -int CheckMem(const void *Mem, int NumBytes) -{ - return MM_IsValidBuffer( (tVAddr)Mem, NumBytes ); -} -/* * - * \} - */ - -/** - * \brief Search a string array for \a Needle - * \note Helper function for eTplDrv_IOCtl::DRV_IOCTL_LOOKUP - */ -int ModUtil_LookupString(const char **Array, const char *Needle) -{ - int i; - if( !CheckString(Needle) ) return -1; - for( i = 0; Array[i]; i++ ) - { - if(strcmp(Array[i], Needle) == 0) return i; - } - return -1; -} - -int ModUtil_SetIdent(char *Dest, const char *Value) -{ - if( !CheckMem(Dest, 32) ) return -1; - strncpy(Dest, Value, 32); - return 1; -} - -int Hex(char *Dest, size_t Size, const Uint8 *SourceData) -{ - int i; - for( i = 0; i < Size; i ++ ) - { - sprintf(Dest + i*2, "%02x", SourceData[i]); - } - return i*2; -} - -/** - * \brief Convert a string of hexadecimal digits into a byte stream - */ -int UnHex(Uint8 *Dest, size_t DestSize, const char *SourceString) -{ - int i; - - for( i = 0; i < DestSize*2; i += 2 ) - { - Uint8 val = 0; - - if(SourceString[i] == '\0') break; - - if('0' <= SourceString[i] && SourceString[i] <= '9') - val |= (SourceString[i]-'0') << 4; - else if('A' <= SourceString[i] && SourceString[i] <= 'F') - val |= (SourceString[i]-'A'+10) << 4; - else if('a' <= SourceString[i] && SourceString[i] <= 'f') - val |= (SourceString[i]-'a'+10) << 4; - - if(SourceString[i+1] == '\0') break; - - if('0' <= SourceString[i+1] && SourceString[i+1] <= '9') - val |= (SourceString[i+1] - '0'); - else if('A' <= SourceString[i+1] && SourceString[i+1] <= 'F') - val |= (SourceString[i+1] - 'A' + 10); - else if('a' <= SourceString[i+1] && SourceString[i+1] <= 'f') - val |= (SourceString[i+1] - 'a' + 10); - - Dest[i/2] = val; - } - return i/2; -} - -Uint16 SwapEndian16(Uint16 Val) -{ - return ((Val&0xFF)<<8) | ((Val>>8)&0xFF); -} -Uint32 SwapEndian32(Uint32 Val) -{ - return ((Val&0xFF)<<24) | ((Val&0xFF00)<<8) | ((Val>>8)&0xFF00) | ((Val>>24)&0xFF); -} - -void *memmove(void *__dest, const void *__src, size_t len) -{ - size_t block_size; - char *dest = __dest; - const char *src = __src; - void *ret = __dest; - - if( len == 0 || dest == src ) - return dest; - - if( (tVAddr)dest > (tVAddr)src + len ) - return memcpy(dest, src, len); - if( (tVAddr)dest + len < (tVAddr)src ) - return memcpy(dest, src, len); - - // NOTE: Assumes memcpy works forward - if( (tVAddr)dest < (tVAddr)src ) - return memcpy(dest, src, len); - - if( (tVAddr)dest < (tVAddr)src ) - block_size = (tVAddr)src - (tVAddr)dest; - else - block_size = (tVAddr)dest - (tVAddr)src; - - block_size &= ~0xF; - - while(len >= block_size) - { - memcpy(dest, src, block_size); - len -= block_size; - dest += block_size; - src += block_size; - } - memcpy(dest, src, len); - return ret; - -} - diff --git a/Kernel/logging.c b/Kernel/logging.c deleted file mode 100644 index b419be3d..00000000 --- a/Kernel/logging.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Acess 2 Kernel - * - By John Hodge (thePowersGang) - * - * logging.c - Kernel Logging Service - */ -#include -#include - -#define CACHE_MESSAGES 0 -#define PRINT_ON_APPEND 1 -#define USE_RING_BUFFER 1 -#define RING_BUFFER_SIZE 4096 - -// === CONSTANTS === -enum eLogLevels -{ - LOG_LEVEL_KPANIC, - LOG_LEVEL_PANIC, - LOG_LEVEL_FATAL, - LOG_LEVEL_ERROR, - LOG_LEVEL_WARNING, - LOG_LEVEL_NOTICE, - LOG_LEVEL_LOG, - LOG_LEVEL_DEBUG, - NUM_LOG_LEVELS -}; -const char *csaLevelColours[] = { - "\x1B[35m", "\x1B[34m", "\x1B[36m", "\x1B[31m", - "\x1B[33m", "\x1B[32m", "\x1B[0m", "\x1B[0m" - }; -const char *csaLevelCodes[] = {"k","p","f","e","w","n","l","d"}; - -// === TYPES === -typedef struct sLogEntry -{ - struct sLogEntry *Next; - struct sLogEntry *LevelNext; - Sint64 Time; - int Level; - int Length; - char Ident[9]; - char Data[]; -} tLogEntry; -typedef struct sLogList -{ - tLogEntry *Head; - tLogEntry *Tail; -} tLogList; - -// === PROTOTYPES === -void Log_AddEvent(const char *Ident, int Level, const char *Format, va_list Args); -static void Log_Int_PrintMessage(tLogEntry *Entry); -//void Log_KernelPanic(const char *Ident, const char *Message, ...); -//void Log_Panic(const char *Ident, const char *Message, ...); -//void Log_Error(const char *Ident, const char *Message, ...); -//void Log_Warning(const char *Ident, const char *Message, ...); -//void Log_Notice(const char *Ident, const char *Message, ...); -//void Log_Log(const char *Ident, const char *Message, ...); -//void Log_Debug(const char *Ident, const char *Message, ...); - -// === EXPORTS === -EXPORT(Log_Panic); -EXPORT(Log_Error); -EXPORT(Log_Warning); -EXPORT(Log_Notice); -EXPORT(Log_Log); -EXPORT(Log_Debug); - -// === GLOBALS === -tShortSpinlock glLogOutput; -#if USE_RING_BUFFER -Uint8 gaLog_RingBufferData[sizeof(tRingBuffer)+RING_BUFFER_SIZE]; -tRingBuffer *gpLog_RingBuffer = (void*)gaLog_RingBufferData; -#else -tMutex glLog; -tLogList gLog; -tLogList gLog_Levels[NUM_LOG_LEVELS]; -#endif - -// === CODE === -/** - * \brief Adds an event to the log - */ -void Log_AddEvent(const char *Ident, int Level, const char *Format, va_list Args) -{ - int len; - tLogEntry *ent; - va_list args_tmp; - - if( Level >= NUM_LOG_LEVELS ) return; - - va_copy(args_tmp, Args); - len = vsnprintf(NULL, 256, Format, args_tmp); - - //Log("len = %i", len); - - #if USE_RING_BUFFER || !CACHE_MESSAGES - { - char buf[sizeof(tLogEntry)+len+1]; - ent = (void*)buf; - #else - ent = malloc(sizeof(tLogEntry)+len+1); - #endif - ent->Time = now(); - strncpy(ent->Ident, Ident, 8); - ent->Ident[8] = '\0'; - ent->Level = Level; - ent->Length = len; - vsnprintf( ent->Data, len+1, Format, Args ); - - #if CACHE_MESSAGES - # if USE_RING_BUFFER - { - #define LOG_HDR_LEN (14+1+2+8+2) - char newData[ LOG_HDR_LEN + len + 2 + 1 ]; - char _ident[9]; - strncpy(_ident, Ident, 9); - sprintf( newData, "%014lli%s [%-8s] ", - ent->Time, csaLevelCodes[Level], Ident); - strcpy( newData + LOG_HDR_LEN, ent->Data ); - strcpy( newData + LOG_HDR_LEN + len, "\r\n" ); - gpLog_RingBuffer->Space = RING_BUFFER_SIZE; // Needed to init the buffer - RingBuffer_Write( gpLog_RingBuffer, newData, LOG_HDR_LEN + len + 2 ); - } - # else - Mutex_Acquire( &glLog ); - - ent->Next = gLog.Tail; - if(gLog.Head) - gLog.Tail = ent; - else - gLog.Tail = gLog.Head = ent; - - ent->LevelNext = gLog_Levels[Level].Tail; - if(gLog_Levels[Level].Head) - gLog_Levels[Level].Tail = ent; - else - gLog_Levels[Level].Tail = gLog_Levels[Level].Head = ent; - - Mutex_Release( &glLog ); - # endif - #endif - - #if PRINT_ON_APPEND || !CACHE_MESSAGES - Log_Int_PrintMessage( ent ); - #endif - - #if USE_RING_BUFFER || !CACHE_MESSAGES - } - #endif -} - -/** - * \brief Prints a log message to the debug console - */ -void Log_Int_PrintMessage(tLogEntry *Entry) -{ - SHORTLOCK( &glLogOutput ); - LogF("%s%014lli%s [%-8s] %i - %s", - csaLevelColours[Entry->Level], - Entry->Time, - csaLevelCodes[Entry->Level], - Entry->Ident, - Threads_GetTID(), - Entry->Data - ); - LogF("\x1B[0m\r\n"); // Separate in case Entry->Data is too long - SHORTREL( &glLogOutput ); -} - -/** - * \brief KERNEL PANIC!!!! - */ -void Log_KernelPanic(const char *Ident, const char *Message, ...) -{ - va_list args; - va_start(args, Message); - Log_AddEvent(Ident, LOG_LEVEL_KPANIC, Message, args); - va_end(args); - Panic("Log_KernelPanic - %s", Ident); -} - -/** - * \brief Panic Message - Driver Unrecoverable error - */ -void Log_Panic(const char *Ident, const char *Message, ...) -{ - va_list args; - va_start(args, Message); - Log_AddEvent(Ident, LOG_LEVEL_PANIC, Message, args); - va_end(args); -} - -/** - * \brief Error Message - Recoverable Error - */ -void Log_Error(const char *Ident, const char *Message, ...) -{ - va_list args; - va_start(args, Message); - Log_AddEvent(Ident, LOG_LEVEL_ERROR, Message, args); - va_end(args); -} - -/** - * \brief Warning Message - Something the user should know - */ -void Log_Warning(const char *Ident, const char *Message, ...) -{ - va_list args; - - va_start(args, Message); - Log_AddEvent(Ident, LOG_LEVEL_WARNING, Message, args); - va_end(args); -} - -/** - * \brief Notice Message - Something the user might like to know - */ -void Log_Notice(const char *Ident, const char *Message, ...) -{ - va_list args; - va_start(args, Message); - Log_AddEvent(Ident, LOG_LEVEL_NOTICE, Message, args); - va_end(args); -} - -/** - * \brief Log Message - Possibly useful information - */ -void Log_Log(const char *Ident, const char *Message, ...) -{ - va_list args; - va_start(args, Message); - Log_AddEvent(Ident, LOG_LEVEL_LOG, Message, args); - va_end(args); -} - -/** - * \brief Debug Message - Only a developer would want this info - */ -void Log_Debug(const char *Ident, const char *Message, ...) -{ - va_list args; - va_start(args, Message); - Log_AddEvent(Ident, LOG_LEVEL_DEBUG, Message, args); - va_end(args); -} diff --git a/Kernel/messages.c b/Kernel/messages.c deleted file mode 100644 index afdcdbde..00000000 --- a/Kernel/messages.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * messages.c - * - IPC Messages - */ -#define DEBUG 0 -#include -#include -#include -#include -#include - -// === CODE === -/** - * \fn int Proc_SendMessage(Uint Dest, int Length, void *Data) - * \brief Send an IPC message - * \param Dest Destination Thread - * \param Length Length of the message - * \param Data Message data - */ -int Proc_SendMessage(Uint Dest, int Length, void *Data) -{ - tThread *thread; - tMsg *msg; - - ENTER("pErr iDest iLength pData", Err, Dest, Length, Data); - - if(Length <= 0 || !Data) { - errno = -EINVAL; - LEAVE_RET('i', -1); - } - - // TODO: Check message length against global/per-thread maximums - // TODO: Restrict queue length - - // Get thread - thread = Threads_GetThread( Dest ); - - // Error check - if(!thread) LEAVE_RET('i', -1); - - // Get Spinlock - SHORTLOCK( &thread->IsLocked ); - - // Check if thread is still alive - if(thread->Status == THREAD_STAT_DEAD) { - SHORTREL( &thread->IsLocked ); - LEAVE_RET('i', -1); - } - - // Create message - msg = malloc( sizeof(tMsg)+Length ); - msg->Next = NULL; - msg->Source = Proc_GetCurThread()->TID; - msg->Length = Length; - memcpy(msg->Data, Data, Length); - - // If there are already messages - if(thread->LastMessage) { - thread->LastMessage->Next = msg; - thread->LastMessage = msg; - } else { - thread->Messages = msg; - thread->LastMessage = msg; - } - - SHORTREL(&thread->IsLocked); - - // Wake the thread - LOG("Waking %p (%i %s)", thread, thread->TID, thread->ThreadName); - Threads_PostEvent( thread, THREAD_EVENT_IPCMSG ); - - LEAVE_RET('i', 0); -} - -/** - * \fn int Proc_GetMessage(Uint *Source, void *Buffer) - * \brief Gets a message - * \param Source Where to put the source TID - * \param Buffer Buffer to place the message data (set to NULL to just get message length) - * \return Message length - */ -int Proc_GetMessage(Uint *Source, void *Buffer) -{ - int ret; - void *tmp; - tThread *cur = Proc_GetCurThread(); - - ENTER("pSource pBuffer", Source, Buffer); - - // Check if queue has any items - if(!cur->Messages) { - LEAVE('i', 0); - return 0; - } - - SHORTLOCK( &cur->IsLocked ); - - if(Source) { - *Source = cur->Messages->Source; - LOG("*Source = %i", *Source); - } - - // Get message length - if( !Buffer ) { - ret = cur->Messages->Length; - SHORTREL( &cur->IsLocked ); - LEAVE('i', ret); - return ret; - } - - // Get message - if(Buffer != GETMSG_IGNORE) - { - if( !CheckMem( Buffer, cur->Messages->Length ) ) - { - LOG("Invalid buffer"); - errno = -EINVAL; - SHORTREL( &cur->IsLocked ); - LEAVE('i', -1); - return -1; - } - LOG("Copied to buffer"); - memcpy(Buffer, cur->Messages->Data, cur->Messages->Length); - } - ret = cur->Messages->Length; - - // Remove from list - tmp = cur->Messages; - cur->Messages = cur->Messages->Next; - if(cur->Messages == NULL) cur->LastMessage = NULL; - - SHORTREL( &cur->IsLocked ); - - free(tmp); // Free outside of lock - - LEAVE('i', ret); - return ret; -} diff --git a/Kernel/modules.c b/Kernel/modules.c deleted file mode 100644 index 98ca1a1e..00000000 --- a/Kernel/modules.c +++ /dev/null @@ -1,449 +0,0 @@ -/* - * Acess2 - * - Module Loader - */ -#define DEBUG 0 -#include -#include - -#define USE_EDI 0 -#define USE_UDI 0 - -// === PROTOTYPES === - int Module_int_Initialise(tModule *Module, const char *ArgString); -void Modules_int_GetBuiltinArray(void); -void Modules_LoadBuiltins(void); -void Modules_SetBuiltinParams(const char *Name, char *ArgString); - int Modules_InitialiseBuiltin(const char *Name); -// int Module_RegisterLoader(tModuleLoader *Loader); -// int Module_LoadMem(void *Buffer, Uint Length, char *ArgString); -// int Module_LoadFile(char *Path, char *ArgString); - int Module_int_ResolveDeps(tModule *Info); - int Module_IsLoaded(const char *Name); -// int Module_EnsureLoaded(const char *Name); - -// === EXPORTS === -EXPORT(Module_RegisterLoader); - -// === IMPORTS === -#if USE_UDI -extern int UDI_LoadDriver(void *Base); -#endif -extern void StartupPrint(const char *Str); -extern tModule gKernelModules; -extern tModule gKernelModulesEnd; - -// === GLOBALS === - int giNumBuiltinModules = 0; -tShortSpinlock glModuleSpinlock; -tModule *gLoadedModules = NULL; -tModuleLoader *gModule_Loaders = NULL; -tModule *gLoadingModules = NULL; -tModule **gapBuiltinModules = NULL; -char **gasBuiltinModuleArgs; - -// === CODE === -/** - * \brief Initialises a module - * \param Module Pointer to the module header - * \param ArgString Comma separated list of module arguments - * \return Zero on success, eModuleErrors or -1 on error - * \retval -1 Returned if a dependency fails, or a circular dependency - * exists. - * \retval 0 Returned on success - * \retval >0 Error code form the module's initialisation function - */ -int Module_int_Initialise(tModule *Module, const char *ArgString) -{ - int i, j; - int ret; - const char **deps; - char **args; - tModule *mod; - - ENTER("pModule", Module); - LOG("Module->Magic = 0x%x", Module->Magic); - if(Module->Magic != MODULE_MAGIC) { - Log_Warning( - "Module", - "Module %p is no a valid Acess2 module (0x%08x != 0x%08x)", - Module, Module->Magic, MODULE_MAGIC - ); - LEAVE('i', MODULE_ERR_BADMODULE); - return MODULE_ERR_BADMODULE; - } - LOG("Module->Name = %p \"%s\"", Module->Name, Module->Name); - - if(Module->Arch != MODULE_ARCH_ID) { - Log_Warning( - "Module", - "Module %p (%s) is for another architecture (%i)", - Module, Module->Name, Module->Arch - ); - } - - deps = Module->Dependencies; - - // Check if the module has been loaded - for( mod = gLoadedModules; mod; mod = mod->Next ) - { - if(mod == Module) LEAVE_RET('i', 0); - } - - // Add to the "loading" (prevents circular deps) - Module->Next = gLoadingModules; - gLoadingModules = Module; - - // Scan dependency list - for( j = 0; deps && deps[j]; j++ ) - { - // Check if the module is already loaded - for( mod = gLoadedModules; mod; mod = mod->Next ) - { - if(strcmp(deps[j], mod->Name) == 0) - break; - } - if( mod ) continue; // Dependency is loaded, check the rest - - // Ok, check if it's loading - for( mod = gLoadingModules->Next; mod; mod = mod->Next ) - { - if(strcmp(deps[j], mod->Name) == 0) - break; - } - if( mod ) { - Log_Warning("Module", "Circular dependency detected (%s and %s)", - mod->Name, Module->Name); - LEAVE_RET('i', -1); - } - - // So, if it's not loaded, we better load it then - for( i = 0; i < giNumBuiltinModules; i ++ ) - { - if( strcmp(deps[j], gapBuiltinModules[i]->Name) == 0 ) - break; - } - if( i == giNumBuiltinModules ) { - Log_Warning("Module", "Dependency '%s' for module '%s' failed", - deps[j], Module->Name); - return -1; - } - - // Dependency is not loaded, so load it - ret = Module_int_Initialise( - gapBuiltinModules[i], - gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL - ); - if( ret ) - { - // The only "ok" error is NOTNEEDED - if(ret != MODULE_ERR_NOTNEEDED) - LEAVE_RET('i', -1); - } - } - - // All Dependencies OK? Initialise - StartupPrint(Module->Name); - Log_Log("Module", "Starting %p '%s' v%i.%i", - Module, Module->Name, - Module->Version >> 8, Module->Version & 0xFF - ); - - if( ArgString ) - args = str_split( ArgString, ',' ); - else - args = NULL; - - ret = Module->Init(args); - - if(args) free(args); - - // Remove from loading list - gLoadingModules = gLoadingModules->Next; - - if( ret != MODULE_ERR_OK ) { - switch(ret) - { - case MODULE_ERR_MISC: - Log_Warning("Module", "Unable to load, reason: Miscelanious"); - break; - case MODULE_ERR_NOTNEEDED: - Log_Debug("Module", "Unable to load, reason: Module not needed"); - break; - case MODULE_ERR_MALLOC: - Log_Warning("Module", "Unable to load, reason: Error in malloc/realloc/calloc, probably not good"); - break; - default: - Log_Warning("Module", "Unable to load reason - Unknown code %i", ret); - break; - } - LEAVE_RET('i', ret); - return ret; - } - LOG("ret = %i", ret); - - // Add to loaded list - SHORTLOCK( &glModuleSpinlock ); - Module->Next = gLoadedModules; - gLoadedModules = Module; - SHORTREL( &glModuleSpinlock ); - - LEAVE_RET('i', 0); -} - -/** - * \brief Scans the builtin modules and creates an array of them - */ -void Modules_int_GetBuiltinArray(void) -{ - int i; - tModule *module; - - // Count - module = &gKernelModules; - i = 0; - while( (tVAddr)module < (tVAddr)&gKernelModulesEnd ) - { - if(module->Magic == MODULE_MAGIC) { - i ++; - module ++; - } - else - module = (void*)( (tVAddr)module + 4 ); - } - - // Create - giNumBuiltinModules = i; - gasBuiltinModuleArgs = calloc( giNumBuiltinModules, sizeof(char*) ); - gapBuiltinModules = malloc( giNumBuiltinModules * sizeof(tModule*) ); - - - // Fill - module = &gKernelModules; - i = 0; - while( (tVAddr)module < (tVAddr)&gKernelModulesEnd ) - { - if(module->Magic == MODULE_MAGIC) { - gapBuiltinModules[i] = module; - i ++; - module ++; - } - else - module = (void*)( (tVAddr)module + 4 ); - } -} - -/** - * \brief Initialises builtin modules - */ -void Modules_LoadBuiltins() -{ - int i; - - if( !gapBuiltinModules ) - Modules_int_GetBuiltinArray(); - - for( i = 0; i < giNumBuiltinModules; i++ ) - { - Module_int_Initialise( - gapBuiltinModules[i], - (gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL) - ); - } - - if( gasBuiltinModuleArgs != NULL ) - free(gasBuiltinModuleArgs); -} - -/** - * \brief Initialise a builtin module given it's name - * - * E.g. Used by VTerm to load an alternate video driver at runtime - */ -int Modules_InitialiseBuiltin(const char *Name) -{ - int i; - - // Check if it's loaded - if( Module_IsLoaded(Name) ) - return 0; - - if( !gapBuiltinModules ) - Modules_int_GetBuiltinArray(); - - for( i = 0; i < giNumBuiltinModules; i++ ) - { - if( strcmp(gapBuiltinModules[i]->Name, Name) == 0 ) { - return Module_int_Initialise(gapBuiltinModules[i], - (gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL) - ); - } - } - return -1; -} - -/** - * \brief Sets the parameters for a builtin module - */ -void Modules_SetBuiltinParams(const char *Name, char *ArgString) -{ - int i; - - if( gasBuiltinModuleArgs == NULL ) - { - Modules_int_GetBuiltinArray(); - } - - // I hate expensive scans - for( i = 0; i < giNumBuiltinModules; i++ ) - { - if(strcmp( gapBuiltinModules[i]->Name, Name ) == 0) { - gasBuiltinModuleArgs[i] = ArgString; - return ; - } - } - - Log_Warning("Modules", "Unknown builtin kernel module '%s'", Name); -} - -/** - * \brief Registers a tModuleLoader with the kernel - * \param Loader Pointer to loader structure (must be persistent) - */ -int Module_RegisterLoader(tModuleLoader *Loader) -{ - if(!Loader) return 1; - - Loader->Next = gModule_Loaders; - gModule_Loaders = Loader; - - return 0; -} - -/** - * \fn int Module_LoadMem(void *Buffer, Uint Length, char *ArgString) - * \brief Load a module from a memory location - */ -int Module_LoadMem(void *Buffer, Uint Length, const char *ArgString) -{ - char path[VFS_MEMPATH_SIZE]; - - VFS_GetMemPath(path, Buffer, Length); - - return Module_LoadFile( path, ArgString ); -} - -/** - * \fn int Module_LoadFile(const char *Path, const char *ArgString) - * \brief Load a module from a file - */ -int Module_LoadFile(const char *Path, const char *ArgString) -{ - void *base; - tModule *info; - - // Load Binary - base = Binary_LoadKernel(Path); - - // Error check - if(base == NULL) { - Log_Warning("Module", "Module_LoadFile - Unable to load '%s'", Path); - return 0; - } - - // Check for Acess Driver - if( Binary_FindSymbol(base, "DriverInfo", (Uint*)&info ) == 0 ) - { - tModuleLoader *tmp; - for( tmp = gModule_Loaders; tmp; tmp = tmp->Next) - { - if( tmp->Detector(base) == 0 ) continue; - - return tmp->Loader(base); - } - - #if USE_EDI - // Check for EDI Driver - if( Binary_FindSymbol(base, "driver_init", NULL ) != 0 ) - { - return Module_InitEDI( base ); // And intialise - } - #endif - - // Unknown module type?, return error - Binary_Unload(base); - #if USE_EDI - Log_Warning("Module", "Module '%s' has neither a Module Info struct, nor an EDI entrypoint", Path); - #else - Log_Warning("Module", "Module '%s' does not have a Module Info struct", Path); - #endif - return 0; - } - - // Initialise (and register) - if( Module_int_Initialise( info, ArgString ) ) - { - Binary_Unload(base); - return 0; - } - - return 1; -} - -/** - * \fn int Module_int_ResolveDeps(tModule *Info) - * \brief Resolves the dependencies - * \todo Implement - * \note Currently does not resolve the dependencies, just checks them - */ -int Module_int_ResolveDeps(tModule *Info) -{ - const char **names = Info->Dependencies; - - // Walk dependencies array - for( ; *names; names++ ) - { - // Check if the module is loaded - if( !Module_IsLoaded(*names) ) { - Log_Warning("Module", "Module `%s' requires `%s', which is not loaded\n", Info->Name, *names); - return 0; - } - } - return 1; -} - -/** - * \fn int Module_IsLoaded(const char *Name) - * \brief Checks if a module is loaded - * \param Name Name of module to find - */ -int Module_IsLoaded(const char *Name) -{ - tModule *mod = gLoadedModules; - - // Scan loaded list - for( ; mod; mod = mod->Next ) - { - // If found, return true - if(strcmp(mod->Name, Name) == 0) - return 1; - } - // not found - return false - return 0; -} - -/** - * \brief Load a module if needed - */ -int Module_EnsureLoaded(const char *Name) -{ - if( Module_IsLoaded(Name) ) - return 0; - - if( Modules_InitialiseBuiltin(Name) == 0 ) - return 0; - - // TODO: Load from a file? - - return -1; -} diff --git a/Kernel/mutex.c b/Kernel/mutex.c deleted file mode 100644 index 597242b6..00000000 --- a/Kernel/mutex.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * mutex.c - * - Mutexes - */ -#include -#include -#include - -// === PROTOTYPES === -#if 0 - int Mutex_Acquire(tMutex *Mutex); -void Mutex_Release(tMutex *Mutex); - int Mutex_IsLocked(tMutex *Mutex); -#endif - -// === CODE === -// -// Acquire mutex (see mutex.h for documentation) -// -int Mutex_Acquire(tMutex *Mutex) -{ - tThread *us = Proc_GetCurThread(); - - // Get protector - SHORTLOCK( &Mutex->Protector ); - -// Log("Mutex_Acquire: (%p)", Mutex); - - // Check if the lock is already held - if( Mutex->Owner ) { - SHORTLOCK( &glThreadListLock ); - // - Remove from active list - us = Threads_RemActive(); - us->Next = NULL; - // - Mark as sleeping - us->Status = THREAD_STAT_MUTEXSLEEP; - us->WaitPointer = Mutex; - - // - Add to waiting - if(Mutex->LastWaiting) { - Mutex->LastWaiting->Next = us; - Mutex->LastWaiting = us; - } - else { - Mutex->Waiting = us; - Mutex->LastWaiting = us; - } - - #if DEBUG_TRACE_STATE - Log("%p (%i %s) waiting on mutex %p", - us, us->TID, us->ThreadName, Mutex); - #endif - - #if 0 - { - int i = 0; - tThread *t; - for( t = Mutex->Waiting; t; t = t->Next, i++ ) - Log("[%i] (tMutex)%p->Waiting[%i] = %p (%i %s)", us->TID, Mutex, i, - t, t->TID, t->ThreadName); - } - #endif - - SHORTREL( &glThreadListLock ); - SHORTREL( &Mutex->Protector ); - while(us->Status == THREAD_STAT_MUTEXSLEEP) Threads_Yield(); - // We're only woken when we get the lock - us->WaitPointer = NULL; - } - // Ooh, let's take it! - else { - Mutex->Owner = us; - SHORTREL( &Mutex->Protector ); - } - - #if 0 - extern tMutex glPhysAlloc; - if( Mutex != &glPhysAlloc ) - LogF("Mutex %p taken by %i %p\n", Mutex, us->TID, __builtin_return_address(0)); - #endif - - return 0; -} - -// Release a mutex -void Mutex_Release(tMutex *Mutex) -{ - SHORTLOCK( &Mutex->Protector ); - //Log("Mutex_Release: (%p)", Mutex); - if( Mutex->Waiting ) { - Mutex->Owner = Mutex->Waiting; // Set owner - Mutex->Waiting = Mutex->Waiting->Next; // Next! - // Reset ->LastWaiting to NULL if we have just removed the last waiting thread - if( Mutex->LastWaiting == Mutex->Owner ) - Mutex->LastWaiting = NULL; - - // Wake new owner - if( Mutex->Owner->Status != THREAD_STAT_ACTIVE ) - Threads_AddActive(Mutex->Owner); - } - else { - Mutex->Owner = NULL; - } - SHORTREL( &Mutex->Protector ); - - #if 0 - extern tMutex glPhysAlloc; - if( Mutex != &glPhysAlloc ) - LogF("Mutex %p released by %i %p\n", Mutex, Threads_GetTID(), __builtin_return_address(0)); - #endif -} - -// Check if a mutex is locked -int Mutex_IsLocked(tMutex *Mutex) -{ - return Mutex->Owner != NULL; -} - -// === EXPORTS === -EXPORT(Mutex_Acquire); -EXPORT(Mutex_Release); -EXPORT(Mutex_IsLocked); diff --git a/Kernel/semaphore.c b/Kernel/semaphore.c deleted file mode 100644 index d4583e74..00000000 --- a/Kernel/semaphore.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * semaphore.c - * - Semaphores - */ -#include -#include -#include - -#define SEMAPHORE_DEBUG 0 // Debug semaphores - -// === CODE === -// -// Initialise a semaphore -// -void Semaphore_Init(tSemaphore *Sem, int Value, int MaxValue, const char *Module, const char *Name) -{ - memset(Sem, 0, sizeof(tSemaphore)); - Sem->Value = Value; - Sem->ModName = Module; - Sem->Name = Name; - Sem->MaxValue = MaxValue; -} -// -// Wait for items to be avaliable -// -int Semaphore_Wait(tSemaphore *Sem, int MaxToTake) -{ - tThread *us; - int taken; - if( MaxToTake < 0 ) { - Log_Warning("Threads", "Semaphore_Wait: User bug - MaxToTake(%i) < 0, Sem=%p(%s)", - MaxToTake, Sem, Sem->Name); - } - - SHORTLOCK( &Sem->Protector ); - - // Check if there's already items avaliable - if( Sem->Value > 0 ) - { - // Take what we need - if( MaxToTake && Sem->Value > MaxToTake ) - taken = MaxToTake; - else - taken = Sem->Value; - Sem->Value -= taken; - } - else - { - SHORTLOCK( &glThreadListLock ); - - // - Remove from active list - us = Threads_RemActive(); - us->Next = NULL; - // - Mark as sleeping - us->Status = THREAD_STAT_SEMAPHORESLEEP; - us->WaitPointer = Sem; - us->RetStatus = MaxToTake; // Use RetStatus as a temp variable - - // - Add to waiting - if(Sem->LastWaiting) { - Sem->LastWaiting->Next = us; - Sem->LastWaiting = us; - } - else { - Sem->Waiting = us; - Sem->LastWaiting = us; - } - - #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG - Log("%p (%i %s) waiting on semaphore %p %s:%s", - us, us->TID, us->ThreadName, - Sem, Sem->ModName, Sem->Name); - #endif - - SHORTREL( &Sem->Protector ); // Release first to make sure it is released - SHORTREL( &glThreadListLock ); - while( us->Status == THREAD_STAT_SEMAPHORESLEEP ) - { - Threads_Yield(); - if(us->Status == THREAD_STAT_SEMAPHORESLEEP) - Log_Warning("Threads", "Semaphore %p %s:%s re-schedulued while asleep", - Sem, Sem->ModName, Sem->Name); - } - #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG - Log("Semaphore %p %s:%s woken", Sem, Sem->ModName, Sem->Name); - #endif - // We're only woken when there's something avaliable (or a signal arrives) - us->WaitPointer = NULL; - - taken = us->RetStatus; - - // Get the lock again - SHORTLOCK( &Sem->Protector ); - } - - // While there is space, and there are thread waiting - // wake the first thread and give it what it wants (or what's left) - while( (Sem->MaxValue == 0 || Sem->Value < Sem->MaxValue) && Sem->Signaling ) - { - int given; - tThread *toWake = Sem->Signaling; - - Sem->Signaling = Sem->Signaling->Next; - // Reset ->LastWaiting to NULL if we have just removed the last waiting thread - if( Sem->Signaling == NULL ) - Sem->LastSignaling = NULL; - - // Figure out how much to give - if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue ) - given = toWake->RetStatus; - else - given = Sem->MaxValue - Sem->Value; - Sem->Value -= given; - - - #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG - Log("%p (%i %s) woken by wait on %p %s:%s", - toWake, toWake->TID, toWake->ThreadName, - Sem, Sem->ModName, Sem->Name); - #endif - - // Save the number we gave to the thread's status - toWake->RetStatus = given; - - // Wake the sleeper - SHORTLOCK( &glThreadListLock ); - if( toWake->Status != THREAD_STAT_ACTIVE ) - Threads_AddActive(toWake); - SHORTREL( &glThreadListLock ); - } - SHORTREL( &Sem->Protector ); - - #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG - Log("Semaphore %p %s:%s took %i by wait", - Sem, Sem->ModName, Sem->Name, taken); - #endif - - return taken; -} - -// -// Add items to a semaphore -// -int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd) -{ - int given; - int added; - - if( AmmountToAdd < 0 ) { - Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)", - AmmountToAdd, Sem, Sem->Name); - } - SHORTLOCK( &Sem->Protector ); - - // Check if we have to block - if( Sem->MaxValue && Sem->Value == Sem->MaxValue ) - { - tThread *us; - #if 0 - Log_Debug("Threads", "Semaphore_Signal: IDLE Sem = %s:%s", Sem->ModName, Sem->Name); - Log_Debug("Threads", "Semaphore_Signal: Sem->Value(%i) == Sem->MaxValue(%i)", Sem->Value, Sem->MaxValue); - #endif - - SHORTLOCK( &glThreadListLock ); - // - Remove from active list - us = Threads_RemActive(); - us->Next = NULL; - // - Mark as sleeping - us->Status = THREAD_STAT_SEMAPHORESLEEP; - us->WaitPointer = Sem; - us->RetStatus = AmmountToAdd; // Use RetStatus as a temp variable - - // - Add to waiting - if(Sem->LastSignaling) { - Sem->LastSignaling->Next = us; - Sem->LastSignaling = us; - } - else { - Sem->Signaling = us; - Sem->LastSignaling = us; - } - - #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG - Log("%p (%i %s) signaling semaphore %p %s:%s", - us, us->TID, us->ThreadName, - Sem, Sem->ModName, Sem->Name); - #endif - - SHORTREL( &glThreadListLock ); - SHORTREL( &Sem->Protector ); - while(us->Status == THREAD_STAT_SEMAPHORESLEEP) Threads_Yield(); - // We're only woken when there's something avaliable - us->WaitPointer = NULL; - - added = us->RetStatus; - - // Get the lock again - SHORTLOCK( &Sem->Protector ); - } - // Non blocking - else - { - // Figure out how much we need to take off - if( Sem->MaxValue && Sem->Value + AmmountToAdd > Sem->MaxValue) - added = Sem->MaxValue - Sem->Value; - else - added = AmmountToAdd; - Sem->Value += added; - } - - // While there are items avaliable, and there are thread waiting - // wake the first thread and give it what it wants (or what's left) - while( Sem->Value && Sem->Waiting ) - { - tThread *toWake = Sem->Waiting; - - // Remove thread from list (double ended, so clear LastWaiting if needed) - Sem->Waiting = Sem->Waiting->Next; - if( Sem->Waiting == NULL ) - Sem->LastWaiting = NULL; - - // Figure out how much to give to woken thread - // - Requested count is stored in ->RetStatus - if( toWake->RetStatus && Sem->Value > toWake->RetStatus ) - given = toWake->RetStatus; - else - given = Sem->Value; - Sem->Value -= given; - - // Save the number we gave to the thread's status - toWake->RetStatus = given; - - if(toWake->bInstrTrace) - Log("%s(%i) given %i from %p", toWake->ThreadName, toWake->TID, given, Sem); - #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG - Log("%p (%i %s) woken by signal on %p %s:%s", - toWake, toWake->TID, toWake->ThreadName, - Sem, Sem->ModName, Sem->Name); - #endif - - // Wake the sleeper - if( toWake->Status != THREAD_STAT_ACTIVE ) - Threads_AddActive(toWake); - else - Warning("Thread %p (%i %s) is already awake", toWake, toWake->TID, toWake->ThreadName); - } - SHORTREL( &Sem->Protector ); - - return added; -} - -// -// Get the current value of a semaphore -// -int Semaphore_GetValue(tSemaphore *Sem) -{ - return Sem->Value; -} - -// === EXPORTS === -EXPORT(Semaphore_Init); -EXPORT(Semaphore_Wait); -EXPORT(Semaphore_Signal); diff --git a/Kernel/syscalls.c b/Kernel/syscalls.c deleted file mode 100644 index 5580187d..00000000 --- a/Kernel/syscalls.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * AcessOS Microkernel Version - * syscalls.c - */ -#define DEBUG 0 - -#include -#include -#include -#include -#include -#include -#include - -#define CHECK_NUM_NULLOK(v,size) \ - if((v)&&!Syscall_Valid((size),(v))){ret=-1;err=-EINVAL;break;} -#define CHECK_STR_NULLOK(v) \ - if((v)&&!Syscall_ValidString((v))){ret=-1;err=-EINVAL;break;} -#define CHECK_NUM_NONULL(v,size) \ - if(!(v)||!Syscall_Valid((size),(v))){ret=-1;err=-EINVAL;break;} -#define CHECK_STR_NONULL(v) \ - if(!(v)||!Syscall_ValidString((v))){ret=-1;err=-EINVAL;break;} -#define CHECK_STR_ARRAY(arr) do {\ - int i;\ - char **tmp = (char**)arr; \ - CHECK_NUM_NONULL( tmp, sizeof(char**) ); \ - for(i=0;tmp[i];i++) { \ - CHECK_STR_NONULL( tmp[i] ); \ - CHECK_NUM_NONULL( &tmp[i+1], sizeof(char*) ); \ - }\ - if(tmp[i]) break;\ -} while(0) - -// === IMPORTS === -extern Uint Binary_Load(const char *file, Uint *entryPoint); - -// === PROTOTYPES === -void SyscallHandler(tSyscallRegs *Regs); - int Syscall_ValidString(const char *Addr); - int Syscall_Valid(int Size, const void *Addr); - -// === CODE === -// TODO: Do sanity checking on arguments, ATM the user can really fuck with the kernel -void SyscallHandler(tSyscallRegs *Regs) -{ - Uint64 ret = 0; - Uint err = -EOK; - int callNum = Regs->Num; - - #if DEBUG < 2 - if(callNum != SYS_READ && callNum != SYS_WRITE) { - #endif - ENTER("iThread iNum", Threads_GetTID(), callNum); - if(callNum < NUM_SYSCALLS) - LOG("Syscall %s", cSYSCALL_NAMES[callNum]); - LOG("Arg1: 0x%x, Arg2: 0x%x, Arg3: 0x%x, Arg4: 0x%x", Regs->Arg1, Regs->Arg2, Regs->Arg3, Regs->Arg4); - #if DEBUG < 2 - } - #endif - - switch(Regs->Num) - { - // -- Exit the current thread - case SYS_EXIT: Threads_Exit(0, Regs->Arg1); break; - - // -- Put the current thread to sleep - case SYS_SLEEP: Threads_Sleep(); break; - - // -- Yield current timeslice - case SYS_YIELD: Threads_Yield(); break; - - // -- Set Error Handler - case SYS_SETFAULTHANDLER: - Threads_SetFaultHandler(Regs->Arg1); - break; - - // -- Clone the current thread - case SYS_CLONE: - // Call clone system call - ret = Proc_Clone(Regs->Arg1); - break; - - // -- Send a signal - case SYS_KILL: - err = -ENOSYS; - ret = -1; - break; - - // -- Wait fr an event - case SYS_WAITEVENT: - // Message mask - ret = Threads_WaitEvents(Regs->Arg1); - break; - - // -- Wait for a thread - case SYS_WAITTID: - // Sanity Check (Status can be NULL) - CHECK_NUM_NULLOK( (int*)Regs->Arg2, sizeof(int) ); - // TID, *Status - ret = Threads_WaitTID(Regs->Arg1, (int*)Regs->Arg2); - break; - - // -- Get the physical address of a page - case SYS_GETPHYS: - ret = MM_GetPhysAddr(Regs->Arg1); - break; - - // -- Map an address - case SYS_MAP: MM_Map(Regs->Arg1, Regs->Arg2); break; - - // -- Allocate an address - case SYS_ALLOCATE: ret = MM_Allocate(Regs->Arg1); break; - - // -- Unmap an address - case SYS_UNMAP: MM_Deallocate(Regs->Arg1); break; - - // -- Get Thread/Process IDs - case SYS_GETTID: ret = Threads_GetTID(); break; - case SYS_GETPID: ret = Threads_GetPID(); break; - - // -- Get User/Group IDs - case SYS_GETUID: ret = Threads_GetUID(); break; - case SYS_GETGID: ret = Threads_GetGID(); break; - - // -- Set User/Group IDs - case SYS_SETUID: ret = Threads_SetUID(Regs->Arg1); break; - case SYS_SETGID: ret = Threads_SetGID(Regs->Arg1); break; - - // -- Send Message - case SYS_SENDMSG: - CHECK_NUM_NONULL( (void*)Regs->Arg3, Regs->Arg2 ); - // Destination, Size, *Data - ret = Proc_SendMessage(Regs->Arg1, Regs->Arg2, (void*)Regs->Arg3); - break; - // -- Check for messages - case SYS_GETMSG: - CHECK_NUM_NULLOK( (Uint*)Regs->Arg1, sizeof(Uint) ); - // NOTE: Can't do range checking as we don't know the size - // - Should be done by Proc_GetMessage - if( Regs->Arg2 && Regs->Arg2 != -1 && !MM_IsUser(Regs->Arg2) ) { - err = -EINVAL; ret = -1; break; - } - // *Source, *Data - ret = Proc_GetMessage((Uint*)Regs->Arg1, (void*)Regs->Arg2); - break; - - // -- Get the current timestamp - case SYS_GETTIME: - ret = now(); - break; - - // -- Set the thread's name - case SYS_SETNAME: - CHECK_STR_NONULL( (char*) Regs->Arg1); - Threads_SetName( (char*)Regs->Arg1 ); - break; - - // --- - // Binary Control - // --- - // -- Create a new process - case SYS_SPAWN: - CHECK_STR_NONULL((const char*)Regs->Arg1); - CHECK_STR_ARRAY((const char**)Regs->Arg2); - CHECK_STR_ARRAY((const char**)Regs->Arg3); - CHECK_NUM_NONULL((void*)Regs->Arg5, Regs->Arg4*sizeof(int)); - ret = Proc_SysSpawn( - (const char*)Regs->Arg1, (const char**)Regs->Arg2, (const char**)Regs->Arg3, - Regs->Arg4, (int*)Regs->Arg5 - ); - break; - // -- Replace the current process with another - case SYS_EXECVE: - CHECK_STR_NONULL((char*)Regs->Arg1); - CHECK_STR_ARRAY( (char**)Regs->Arg2 ); - if( Regs->Arg3 ) - CHECK_STR_ARRAY( (char**)Regs->Arg3 ); - LEAVE('s', "Assuming 0"); - // Path, **Argv, **Envp, DataSize (=0 to tell it to create a copy) - ret = Proc_Execve( - (const char*)Regs->Arg1, (const char**)Regs->Arg2, (const char**)Regs->Arg3, - 0 - ); - break; - // -- Load a binary into the current process - case SYS_LOADBIN: - CHECK_STR_NONULL( (char*)Regs->Arg1 ); - CHECK_NUM_NONULL( (Uint*)Regs->Arg2, sizeof(Uint) ); - // Path, *Entrypoint - ret = Binary_Load((char*)Regs->Arg1, (Uint*)Regs->Arg2); - break; - - // --- - // Virtual Filesystem - // --- - case SYS_OPEN: - CHECK_STR_NONULL( (char*)Regs->Arg1 ); - LOG("VFS_Open(\"%s\", 0x%x)", (char*)Regs->Arg1, Regs->Arg2 | VFS_OPENFLAG_USER); - ret = VFS_Open((char*)Regs->Arg1, Regs->Arg2 | VFS_OPENFLAG_USER); - break; - - case SYS_CLOSE: - LOG("VFS_Close(%i)", Regs->Arg1); - VFS_Close( Regs->Arg1 ); - break; - - case SYS_SEEK: - #if BITS == 64 - ret = VFS_Seek( Regs->Arg1, Regs->Arg2, Regs->Arg3 ); - #else - ret = VFS_Seek( Regs->Arg1, Regs->Arg2|(((Uint64)Regs->Arg3)<<32), Regs->Arg4 ); - #endif - break; - - case SYS_TELL: - ret = VFS_Tell( Regs->Arg1 ); - break; - - case SYS_WRITE: - CHECK_NUM_NONULL( (void*)Regs->Arg2, Regs->Arg3 ); - ret = VFS_Write( Regs->Arg1, Regs->Arg3, (void*)Regs->Arg2 ); - break; - - case SYS_READ: - CHECK_NUM_NONULL( (void*)Regs->Arg2, Regs->Arg3 ); - ret = VFS_Read( Regs->Arg1, Regs->Arg3, (void*)Regs->Arg2 ); - break; - - case SYS_FINFO: - CHECK_NUM_NONULL( (void*)Regs->Arg2, sizeof(tFInfo) + Regs->Arg3*sizeof(tVFS_ACL) ); - // FP, Dest, MaxACLs - ret = VFS_FInfo( Regs->Arg1, (void*)Regs->Arg2, Regs->Arg3 ); - break; - - // Get ACL Value - case SYS_GETACL: - CHECK_NUM_NONULL( (void*)Regs->Arg2, sizeof(tVFS_ACL) ); - ret = VFS_GetACL( Regs->Arg1, (void*)Regs->Arg2 ); - break; - - // Read Directory - case SYS_READDIR: - // TODO: What if the filename is longer? - // Maybe force it to be a 256 byte buffer - CHECK_NUM_NONULL( (void*)Regs->Arg2, 256 ); - ret = VFS_ReadDir( Regs->Arg1, (void*)Regs->Arg2 ); - break; - - // Open a file that is a entry in an open directory - case SYS_OPENCHILD: - CHECK_STR_NONULL( (char*)Regs->Arg2 ); - ret = VFS_OpenChild( Regs->Arg1, (char*)Regs->Arg2, Regs->Arg3 | VFS_OPENFLAG_USER); - break; - - // Change Directory - case SYS_CHDIR: - CHECK_STR_NONULL( (const char*)Regs->Arg1 ); - ret = VFS_ChDir( (const char*)Regs->Arg1 ); - break; - - // IO Control - case SYS_IOCTL: - // All sanity checking should be done by the driver - if( Regs->Arg3 && !MM_IsUser(Regs->Arg3) ) { - err = -EINVAL; ret = -1; break; - } - ret = VFS_IOCtl( Regs->Arg1, Regs->Arg2, (void*)Regs->Arg3 ); - break; - - // Mount a filesystem - case SYS_MOUNT: - // Only root can mount filesystems - if(Threads_GetUID() != 0) { - err = -EACCES; - ret = -1; - break; - } - // Sanity check the paths - if(!Syscall_ValidString((char*)Regs->Arg1) - || !Syscall_ValidString((char*)Regs->Arg2) - || !Syscall_ValidString((char*)Regs->Arg3) - || !Syscall_ValidString((char*)Regs->Arg4) ) { - err = -EINVAL; - ret = -1; - break; - } - ret = VFS_Mount( - (char*)Regs->Arg1, // Device - (char*)Regs->Arg2, // Mount point - (char*)Regs->Arg3, // Filesystem - (char*)Regs->Arg4 // Options - ); - break; - - // Wait on a set of handles - case SYS_SELECT: - // Sanity checks - if( (Regs->Arg2 && !Syscall_Valid(sizeof(fd_set), (void*)Regs->Arg2)) - || (Regs->Arg3 && !Syscall_Valid(sizeof(fd_set), (void*)Regs->Arg3)) - || (Regs->Arg4 && !Syscall_Valid(sizeof(fd_set), (void*)Regs->Arg4)) - || (Regs->Arg5 && !Syscall_Valid(sizeof(tTime), (void*)Regs->Arg5)) ) - { - err = -EINVAL; - ret = -1; - break; - } - // Perform the call - ret = VFS_Select( - Regs->Arg1, // Max handle - (fd_set *)Regs->Arg2, // Read - (fd_set *)Regs->Arg3, // Write - (fd_set *)Regs->Arg4, // Errors - (tTime *)Regs->Arg5, // Timeout - (Uint32)Regs->Arg6, // Extra wakeup events - 0 // User handles - ); - break; - - // -- Debug - //#if DEBUG_BUILD - case SYS_DEBUG: - CHECK_STR_NONULL( (char*)Regs->Arg1 ); - LogF("Log: %08lli [%i] ", now(), Threads_GetTID()); - LogF((const char*)Regs->Arg1, - Regs->Arg2, Regs->Arg3, Regs->Arg4, Regs->Arg5, Regs->Arg6); - LogF("\r\n"); - break; - //#endif - - // -- Default (Return Error) - default: - Log_Warning("Syscalls", "Unknown System Call %i", Regs->Num); - if(Regs->Num < NUM_SYSCALLS) - Log_Warning("Syscall", " named '%s'", cSYSCALL_NAMES[Regs->Num]); - err = -ENOSYS; - ret = -1; - break; - } - - if(err == 0) err = errno; - - if(err != 0) { - LOG("ID: %i, Return errno = %i", Regs->Num, err); - } - - #if BITS < 64 - Regs->Return = ret&0xFFFFFFFF; - Regs->RetHi = ret >> 32; - #else - Regs->Return = ret; - #endif - Regs->Error = err; - #if DEBUG - # if DEBUG < 2 - if( callNum != SYS_READ && callNum != SYS_WRITE ) { - # endif - LOG("err = %i", err); - if( callNum == SYS_EXECVE ) - LOG("Actual %i", ret); - else - LEAVE('x', ret); - # if DEBUG < 2 - } - # endif - #endif -} - -/** - * \fn int Syscall_ValidString(const char *Addr) - * \brief Checks if a memory address contains a valid string - */ -int Syscall_ValidString(const char *Addr) -{ - // Check if the memory is user memory - if(!MM_IsUser( (tVAddr) Addr)) return 0; - - return CheckString( Addr ); -} - -/** - * \fn int Syscall_Valid(int Size, const void *Addr) - * \brief Checks if a memory address is valid - */ -int Syscall_Valid(int Size, const void *Addr) -{ - if(!MM_IsUser( (tVAddr)Addr )) return 0; - - return CheckMem( Addr, Size ); -} diff --git a/Kernel/syscalls.lst b/Kernel/syscalls.lst deleted file mode 100644 index 283caf9c..00000000 --- a/Kernel/syscalls.lst +++ /dev/null @@ -1,64 +0,0 @@ - -0 -SYS_EXIT Kill this thread -SYS_CLONE Create a new thread -SYS_KILL Send a signal -SYS_SETFAULTHANDLER Set signal Handler -SYS_YIELD Yield remainder of timestamp -SYS_SLEEP Sleep until messaged or signaled -SYS_WAITEVENT Wait for an event -SYS_WAITTID Wait for a thread to do something - -SYS_SETNAME Sets the name of the current thread -SYS_GETNAME Gets the name of a thread -SYS_GETTID Get current thread ID -SYS_GETPID Get current thread group ID -SYS_SETPRI Set process priority - -SYS_SENDMSG Send an IPC message -SYS_GETMSG Recieve an IPC message - -SYS_GETTIME Get the current timestamp - -SYS_SPAWN Spawn a new process -SYS_EXECVE Replace the current process -SYS_LOADBIN Load a binary into the current address space -SYS_UNLOADBIN Unload a loaded binary -SYS_LOADMOD Load a module into the kernel - -32 -SYS_GETPHYS Get the physical address of a page -SYS_MAP Map a physical address -SYS_ALLOCATE Allocate a page -SYS_UNMAP Unmap a page -SYS_PREALLOC Preallocate a page -SYS_SETFLAGS Set a page's flags -SYS_SHAREWITH Share a page with another thread - -SYS_GETUID Get current User ID -SYS_GETGID Get current Group ID -SYS_SETUID Set current user ID -SYS_SETGID Set current Group ID - -64 -SYS_OPEN Open a file -SYS_REOPEN Close a file and reuse its handle -SYS_CLOSE Close a file -SYS_READ Read from an open file -SYS_WRITE Write to an open file -SYS_IOCTL Perform an IOCtl Call -SYS_SEEK Seek to a new position in the file -SYS_READDIR Read from an open directory -SYS_OPENCHILD Open a child entry in a directory -SYS_GETACL Get an ACL Value -SYS_SETACL Set an ACL Value -SYS_FINFO Get file information -SYS_MKDIR Create a new directory -SYS_LINK Create a new link to a file -SYS_SYMLINK Create a symbolic link -SYS_UNLINK Delete a file -SYS_TELL Return the current file position -SYS_CHDIR Change current directory -SYS_GETCWD Get current directory -SYS_MOUNT Mount a filesystem -SYS_SELECT Wait for file handles diff --git a/Kernel/system.c b/Kernel/system.c deleted file mode 100644 index 8fc2a1cc..00000000 --- a/Kernel/system.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Acess 2 Kernel - * - By John Hodge (thePowersGang) - * system.c - * - Architecture Independent System Init - */ -#define DEBUG 0 -#include - -// === IMPORTS === -extern void Arch_LoadBootModules(void); -extern int Modules_LoadBuiltins(void); -extern void Modules_SetBuiltinParams(char *Name, char *ArgString); -extern void Debug_SetKTerminal(const char *File); - -// === PROTOTYPES === -void System_Init(char *Commandline); -void System_ParseCommandLine(char *ArgString); -void System_ExecuteCommandLine(void); -void System_ParseVFS(char *Arg); -void System_ParseModuleArgs(char *Arg); -void System_ParseSetting(char *Arg); - -// === GLOBALS === -const char *gsInitBinary = "/Acess/SBin/init"; -char *argv[32]; - int argc; - -// === CODE === -void System_Init(char *CommandLine) -{ - // Parse Kernel's Command Line - System_ParseCommandLine(CommandLine); - - // Initialise modules - Log_Log("Config", "Initialising builtin modules..."); - Modules_LoadBuiltins(); - Arch_LoadBootModules(); - - System_ExecuteCommandLine(); - - // - Execute the Config Script - Log_Log("Config", "Spawning init '%s'", gsInitBinary); - Proc_Spawn(gsInitBinary); - - // Set the debug to be echoed to the terminal - Log_Log("Config", "Kernel now echoes to VT7 (Ctrl-Alt-F8)"); - Debug_SetKTerminal("/Devices/VTerm/7"); -} - -/** - * \fn void System_ParseCommandLine(char *ArgString) - * \brief Parses the kernel's command line and sets the environment - */ -void System_ParseCommandLine(char *ArgString) -{ - int i; - char *str; - - Log_Log("Config", "Kernel Invocation (%p) \"%s\"", ArgString, ArgString); - - // --- Get Arguments --- - str = ArgString; - for( argc = 0; argc < 32; argc++ ) - { - // Eat Whitespace - while(*str == ' ') str++; - // Check for the end of the string - if(*str == '\0') { argc--; break;} - argv[argc] = str; - if(*str == '"') { - while(*str && !(*str == '"' && str[-1] != '\\')) - str ++; - } - else { - while(*str && *str != ' ') - str++; - } - if(*str == '\0') break; // Check for EOS - *str = '\0'; // Cap off argument string - str ++; // and increment the string pointer - } - if(argc < 32) - argc ++; // Count last argument - - // --- Parse Arguments (Pass 1) --- - for( i = 1; i < argc; i++ ) - { - switch(argv[i][0]) - { - // --- VFS --- - // Ignored on this pass - case '/': - break; - - // --- Module Paramaters --- - // -VTerm:Width=640,Height=480,Scrollback=2 - case '-': - System_ParseModuleArgs( argv[i] ); - break; - // --- Config Options --- - // SCRIPT=/Acess/Conf/BootConf.cfg - default: - System_ParseSetting( argv[i] ); - break; - } - } -} - -void System_ExecuteCommandLine(void) -{ - int i; - if(argc > 0) - LOG("Invocation '%s'", argv[0]); - for( i = 1; i < argc; i++ ) - { - LOG("argv[%i] = '%s'", i, argv[i]); - switch(argv[i][0]) - { - // --- VFS --- - // Mount /System=ext2:/Devices/ATA/A1 - // Symlink /Acess=/System/Acess2 - case '/': - System_ParseVFS( argv[i] ); - break; - } - } -} - -/** - * \fn void System_ParseVFS(char *Arg) - */ -void System_ParseVFS(char *Arg) -{ - char *value; - int fd; - - value = Arg; - // Search for the '=' token - while( *value && *value != '=' ) - value++; - - // Check if the equals was found - if( *value == '\0' ) { - Log_Warning("Config", "Expected '=' in the string '%s'", Arg); - return ; - } - - // Edit string - *value = '\0'; value ++; - - // Check assignment type - // - Symbolic Link = - if(value[0] == '/') - { - Log_Log("Config", "Symbolic link '%s' pointing to '%s'", Arg, value); - VFS_Symlink(Arg, value); - } - // - Mount =: - else - { - char *dev = value; - // Find colon - while(*dev && *dev != ':') dev++; - if(*dev) { - *dev = '\0'; - dev++; // Eat ':' - } - // Create Mountpoint - if( (fd = VFS_Open(Arg, 0)) == -1 ) { - Log_Log("Config", "Creating directory '%s'", Arg, value); - VFS_MkDir( Arg ); - } else { - VFS_Close(fd); - } - // Mount - Log_Log("Config", "Mounting '%s' to '%s' ('%s')", dev, Arg, value); - VFS_Mount(dev, Arg, value, ""); - } -} - -/** - * \brief Parse a module argument string - * \param Arg Argument string - */ -void System_ParseModuleArgs(char *Arg) -{ - char *name, *args; - int i; - - // Remove '-' - name = Arg + 1; - - // Find the start of the args - i = strpos(name, ':'); - if( i == -1 ) { - Log_Warning("Config", "Module spec with no arguments"); - #if 1 - return ; - #else - i = strlen(name); - args = name + i; - #endif - } - else { - name[i] = '\0'; - args = name + i + 1; - } - - Log_Log("Config", "Setting boot parameters for '%s' to '%s'", name, args); - Modules_SetBuiltinParams(name, args); -} - -/** - * \fn void System_ParseSetting(char *Arg) - */ -void System_ParseSetting(char *Arg) -{ - char *value; - value = Arg; - - // Search for the '=' token - while( *value && *value != '=' ) - value++; - - // Check for boolean/flag (no '=') - if(*value == '\0') - { - //if(strcmp(Arg, "") == 0) { - //} else { - Log_Warning("Config", "Kernel flag '%s' is not recognised", Arg); - //} - } - else - { - *value = '\0'; // Remove '=' - value ++; // and eat it's position - - if(strcmp(Arg, "INIT") == 0) { - Log_Log("Config", "Init binary: '%s'", value); - if(strlen(value) == 0) - gsInitBinary = NULL; - else - gsInitBinary = value; - } - else { - Log_Warning("Config", "Kernel config setting '%s' is not recognised", Arg); - } - - } -} - diff --git a/Kernel/threads.c b/Kernel/threads.c deleted file mode 100644 index 956eede8..00000000 --- a/Kernel/threads.c +++ /dev/null @@ -1,1355 +0,0 @@ -/* - * Acess2 - * threads.c - * - Common Thread Control - */ -#include -#include -#include -#include -#include -#include -#include // VFS Handle maintainence - -// Configuration -#define DEBUG_TRACE_TICKETS 0 // Trace ticket counts -#define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake) - -// --- Schedulers --- -#define SCHED_UNDEF 0 -#define SCHED_LOTTERY 1 // Lottery scheduler -#define SCHED_RR_SIM 2 // Single Queue Round Robin -#define SCHED_RR_PRI 3 // Multi Queue Round Robin -// Set scheduler type -#define SCHEDULER_TYPE SCHED_RR_PRI - -// === CONSTANTS === -#define DEFAULT_QUANTUM 5 -#define DEFAULT_PRIORITY 5 -#define MIN_PRIORITY 10 - -// === IMPORTS === - -// === TYPE === -typedef struct -{ - tThread *Head; - tThread *Tail; -} tThreadList; - -// === PROTOTYPES === -void Threads_Init(void); -#if 0 -void Threads_Delete(tThread *Thread); - int Threads_SetName(const char *NewName); -#endif -char *Threads_GetName(tTID ID); -#if 0 -void Threads_SetPriority(tThread *Thread, int Pri); -tThread *Threads_CloneTCB(Uint *Err, Uint Flags); - int Threads_WaitTID(int TID, int *status); -tThread *Threads_GetThread(Uint TID); -#endif -tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread); -void Threads_int_AddToList(tThreadList *List, tThread *Thread); -#if 0 -void Threads_Exit(int TID, int Status); -void Threads_Kill(tThread *Thread, int Status); -void Threads_Yield(void); -void Threads_Sleep(void); - int Threads_Wake(tThread *Thread); -void Threads_AddActive(tThread *Thread); -tThread *Threads_RemActive(void); -#endif -void Threads_ToggleTrace(int TID); -void Threads_Fault(int Num); -void Threads_SegFault(tVAddr Addr); -#if 0 - int Threads_GetPID(void); - int Threads_GetTID(void); -tUID Threads_GetUID(void); -tGID Threads_GetGID(void); - int Threads_SetUID(Uint *Errno, tUID ID); - int Threads_SetGID(Uint *Errno, tUID ID); -#endif -void Threads_Dump(void); -void Threads_DumpActive(void); - -// === GLOBALS === -// -- Core Thread -- -struct sProcess gProcessZero = { - }; -// Only used for the core kernel -tThread gThreadZero = { - .Status = THREAD_STAT_ACTIVE, // Status - .ThreadName = (char*)"ThreadZero", // Name - .Quantum = DEFAULT_QUANTUM, // Default Quantum - .Remaining = DEFAULT_QUANTUM, // Current Quantum - .Priority = DEFAULT_PRIORITY // Number of tickets - }; -// -- Processes -- -// --- Locks --- -tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked -// --- Current State --- -volatile int giNumActiveThreads = 0; // Number of threads on the active queue -volatile Uint giNextTID = 1; // Next TID to allocate -// --- Thread Lists --- -tThread *gAllThreads = NULL; // All allocated threads -tThreadList gSleepingThreads; // Sleeping Threads - int giNumCPUs = 1; // Number of CPUs -BOOL gaThreads_NoTaskSwitch[MAX_CPUS]; // Disables task switches for each core (Pseudo-IF) -// --- Scheduler Types --- -#if SCHEDULER_TYPE == SCHED_LOTTERY -const int caiTICKET_COUNTS[MIN_PRIORITY+1] = {100,81,64,49,36,25,16,9,4,1,0}; -volatile int giFreeTickets = 0; // Number of tickets held by non-scheduled threads -tThreadList gActiveThreads; // Currently Running Threads -#elif SCHEDULER_TYPE == SCHED_RR_SIM -tThreadList gActiveThreads; // Currently Running Threads -#elif SCHEDULER_TYPE == SCHED_RR_PRI -tThreadList gaActiveThreads[MIN_PRIORITY+1]; // Active threads for each priority level -#else -# error "Unkown scheduler type" -#endif - -// === CODE === -/** - * \fn void Threads_Init(void) - * \brief Initialse the thread list - */ -void Threads_Init(void) -{ - ArchThreads_Init(); - - Log_Debug("Threads", "Offsets of tThread"); - Log_Debug("Threads", ".Priority = %i", offsetof(tThread, Priority)); - Log_Debug("Threads", ".KernelStack = %i", offsetof(tThread, KernelStack)); - - // Create Initial Task -// #if SCHEDULER_TYPE == SCHED_RR_PRI -// gaActiveThreads[gThreadZero.Priority].Head = &gThreadZero; -// gaActiveThreads[gThreadZero.Priority].Tail = &gThreadZero; -// #else -// gActiveThreads.Head = &gThreadZero; -// gActiveThreads.Tail = &gThreadZero; -// #endif - - gAllThreads = &gThreadZero; - giNumActiveThreads = 1; - gThreadZero.Process = &gProcessZero; - - Proc_Start(); -} - -void Threads_Delete(tThread *Thread) -{ - // Set to dead - Thread->Status = THREAD_STAT_BURIED; - - // Clear out process state - Proc_ClearThread(Thread); - - Thread->Process->nThreads --; - if( Thread->Process->nThreads == 0 ) - { - tProcess *proc = Thread->Process; - // VFS Cleanup - VFS_CloseAllUserHandles(); - // Architecture cleanup - Proc_ClearProcess( proc ); - // VFS Configuration strings - if( proc->CurrentWorkingDir) - free( proc->CurrentWorkingDir ); - if( proc->RootDir ) - free( proc->RootDir ); - // Process descriptor - free( proc ); - } - - // Free name - if( IsHeap(Thread->ThreadName) ) - free(Thread->ThreadName); - - // Remove from global list - // TODO: Lock this too - if( Thread == gAllThreads ) - gAllThreads = Thread->GlobalNext; - else - Thread->GlobalPrev->GlobalNext = Thread->GlobalNext; - - free(Thread); -} - -/** - * \fn void Threads_SetName(const char *NewName) - * \brief Sets the current thread's name - * \param NewName New name for the thread - * \return Boolean Failure - */ -int Threads_SetName(const char *NewName) -{ - tThread *cur = Proc_GetCurThread(); - char *oldname = cur->ThreadName; - - // NOTE: There is a possibility of non-thread safety here - // A thread could read the current name pointer before it is zeroed - - cur->ThreadName = NULL; - - if( IsHeap(oldname) ) free( oldname ); - cur->ThreadName = strdup(NewName); - - Log_Debug("Threads", "Thread renamed to '%s'", NewName); - - return 0; -} - -/** - * \fn char *Threads_GetName(int ID) - * \brief Gets a thread's name - * \param ID Thread ID (-1 indicates current thread) - * \return Pointer to name - * \retval NULL Failure - */ -char *Threads_GetName(tTID ID) -{ - if(ID == -1) { - return Proc_GetCurThread()->ThreadName; - } - return Threads_GetThread(ID)->ThreadName; -} - -/** - * \fn void Threads_SetPriority(tThread *Thread, int Pri) - * \brief Sets the priority of a task - * \param Thread Thread to update ticket count (NULL means current thread) - * \param Pri New priority - */ -void Threads_SetPriority(tThread *Thread, int Pri) -{ - // Get current thread - if(Thread == NULL) Thread = Proc_GetCurThread(); - // Bounds checking - // - If < 0, set to lowest priority - // - Minumum priority is actualy a high number, 0 is highest - if(Pri < 0) Pri = MIN_PRIORITY; - if(Pri > MIN_PRIORITY) Pri = MIN_PRIORITY; - - // Do we actually have to do anything? - if( Pri == Thread->Priority ) return; - - #if SCHEDULER_TYPE == SCHED_RR_PRI - if( Thread != Proc_GetCurThread() ) - { - SHORTLOCK( &glThreadListLock ); - // Remove from old priority - Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread ); - // And add to new - Threads_int_AddToList( &gaActiveThreads[Pri], Thread ); - Thread->Priority = Pri; - SHORTREL( &glThreadListLock ); - } - else - Thread->Priority = Pri; - #else - // If this isn't the current thread, we need to lock - if( Thread != Proc_GetCurThread() ) - { - SHORTLOCK( &glThreadListLock ); - - #if SCHEDULER_TYPE == SCHED_LOTTERY - giFreeTickets -= caiTICKET_COUNTS[Thread->Priority] - caiTICKET_COUNTS[Pri]; - # if DEBUG_TRACE_TICKETS - Log("Threads_SetTickets: new giFreeTickets = %i [-%i+%i]", - giFreeTickets, - caiTICKET_COUNTS[Thread->Priority], caiTICKET_COUNTS[Pri]); - # endif - #endif - Thread->Priority = Pri; - SHORTREL( &glThreadListLock ); - } - else - Thread->Priority = Pri; - #endif - - #if DEBUG_TRACE_STATE - Log("Threads_SetPriority: %p(%i %s) pri set %i", - Thread, Thread->TID, Thread->ThreadName, - Pri); - #endif -} - -/** - * \brief Clone the TCB of the current thread - * \param Flags Flags for something... (What is this for?) - */ -tThread *Threads_CloneTCB(Uint Flags) -{ - tThread *cur, *new; - cur = Proc_GetCurThread(); - - // Allocate and duplicate - new = malloc(sizeof(tThread)); - if(new == NULL) { errno = -ENOMEM; return NULL; } - memcpy(new, cur, sizeof(tThread)); - - new->CurCPU = -1; - new->Next = NULL; - memset( &new->IsLocked, 0, sizeof(new->IsLocked)); - new->Status = THREAD_STAT_PREINIT; - new->RetStatus = 0; - - // Get Thread ID - new->TID = giNextTID++; - new->Parent = cur; - new->bInstrTrace = 0; - - // Clone Name - new->ThreadName = strdup(cur->ThreadName); - - // Set Thread Group ID (PID) - if(Flags & CLONE_VM) { - tProcess *newproc, *oldproc; - oldproc = cur->Process; - new->Process = malloc( sizeof(struct sProcess) ); - newproc = new->Process; - newproc->PID = new->TID; - newproc->UID = oldproc->UID; - newproc->GID = oldproc->GID; - newproc->MaxFD = oldproc->MaxFD; - if( oldproc->CurrentWorkingDir ) - newproc->CurrentWorkingDir = strdup( oldproc->CurrentWorkingDir ); - else - newproc->CurrentWorkingDir = NULL; - if( oldproc->RootDir ) - newproc->RootDir = strdup( oldproc->RootDir ); - else - newproc->RootDir = NULL; - newproc->nThreads = 1; - // Reference all handles in the VFS - VFS_ReferenceUserHandles(); - } - else { - new->Process->nThreads ++; - } - - // Messages are not inherited - new->Messages = NULL; - new->LastMessage = NULL; - - // Set State - new->Remaining = new->Quantum = cur->Quantum; - new->Priority = cur->Priority; - new->_errno = 0; - - // Set Signal Handlers - new->CurFaultNum = 0; - new->FaultHandler = cur->FaultHandler; - - // Maintain a global list of threads - SHORTLOCK( &glThreadListLock ); - new->GlobalPrev = NULL; // Protect against bugs - new->GlobalNext = gAllThreads; - gAllThreads->GlobalPrev = new; - gAllThreads = new; - SHORTREL( &glThreadListLock ); - - return new; -} - -/** - * \brief Clone the TCB of the kernel thread - */ -tThread *Threads_CloneThreadZero(void) -{ - tThread *new; - - // Allocate and duplicate - new = malloc(sizeof(tThread)); - if(new == NULL) { - return NULL; - } - memcpy(new, &gThreadZero, sizeof(tThread)); - - new->Process->nThreads ++; - - new->CurCPU = -1; - new->Next = NULL; - memset( &new->IsLocked, 0, sizeof(new->IsLocked)); - new->Status = THREAD_STAT_PREINIT; - new->RetStatus = 0; - - // Get Thread ID - new->TID = giNextTID++; - new->Parent = 0; - - // Clone Name - new->ThreadName = NULL; - - // Messages are not inherited - new->Messages = NULL; - new->LastMessage = NULL; - - // Set State - new->Remaining = new->Quantum = DEFAULT_QUANTUM; - new->Priority = DEFAULT_PRIORITY; - new->bInstrTrace = 0; - - // Set Signal Handlers - new->CurFaultNum = 0; - new->FaultHandler = 0; - - // Maintain a global list of threads - SHORTLOCK( &glThreadListLock ); - new->GlobalPrev = NULL; // Protect against bugs - new->GlobalNext = gAllThreads; - gAllThreads->GlobalPrev = new; - gAllThreads = new; - SHORTREL( &glThreadListLock ); - - return new; -} - -/** - * \brief Wait for a task to change state - * \param TID Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID) - * \param Status Thread return status - * \return TID of child that changed state - */ -tTID Threads_WaitTID(int TID, int *Status) -{ - // Any Child - if(TID == -1) { - Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child"); - return -1; - } - - // Any peer/child thread - if(TID == 0) { - Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling"); - return -1; - } - - // TGID = abs(TID) - if(TID < -1) { - Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID"); - return -1; - } - - // Specific Thread - if(TID > 0) { - tThread *t = Threads_GetThread(TID); - tTID ret; - - // Wait for the thread to die! - // TODO: Handle child also being suspended if wanted - while(t->Status != THREAD_STAT_ZOMBIE) { - Threads_Sleep(); - Log_Debug("Threads", "%i waiting for %i, t->Status = %i", - Threads_GetTID(), t->TID, t->Status); - } - - // Set return status - ret = t->TID; - switch(t->Status) - { - case THREAD_STAT_ZOMBIE: - // Kill the thread - t->Status = THREAD_STAT_DEAD; - // TODO: Child return value? - if(Status) *Status = t->RetStatus; - // add to delete queue - Threads_Delete( t ); - break; - default: - if(Status) *Status = -1; - break; - } - return ret; - } - - return -1; -} - -/** - * \brief Gets a thread given its TID - * \param TID Thread ID - * \return Thread pointer - */ -tThread *Threads_GetThread(Uint TID) -{ - tThread *thread; - - // Search global list - for(thread = gAllThreads; - thread; - thread = thread->GlobalNext) - { - if(thread->TID == TID) - return thread; - } - - Log("Unable to find TID %i on main list\n", TID); - - return NULL; -} - -/** - * \brief Deletes an entry from a list - * \param List Pointer to the list head - * \param Thread Thread to find - * \return \a Thread - */ -tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread) -{ - tThread *ret, *prev = NULL; - - for(ret = List->Head; - ret && ret != Thread; - prev = ret, ret = ret->Next - ); - - // Is the thread on the list - if(!ret) { - //LogF("%p(%s) is not on list %p\n", Thread, Thread->ThreadName, List); - return NULL; - } - - if( !prev ) { - List->Head = Thread->Next; - //LogF("%p(%s) removed from head of %p\n", Thread, Thread->ThreadName, List); - } - else { - prev->Next = Thread->Next; - //LogF("%p(%s) removed from %p (prev=%p)\n", Thread, Thread->ThreadName, List, prev); - } - if( Thread->Next == NULL ) - List->Tail = prev; - - return Thread; -} - -void Threads_int_AddToList(tThreadList *List, tThread *Thread) -{ - if( List->Head ) - List->Tail->Next = Thread; - else - List->Head = Thread; - List->Tail = Thread; - Thread->Next = NULL; -} - -/** - * \brief Exit the current process (or another?) - * \param TID Thread ID to kill - * \param Status Exit status - */ -void Threads_Exit(int TID, int Status) -{ - if( TID == 0 ) - Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF ); - else - Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF ); - - // Halt forever, just in case - for(;;) HALT(); -} - -/** - * \fn void Threads_Kill(tThread *Thread, int Status) - * \brief Kill a thread - * \param Thread Thread to kill - * \param Status Status code to return to the parent - */ -void Threads_Kill(tThread *Thread, int Status) -{ - tMsg *msg; - int isCurThread = Thread == Proc_GetCurThread(); - - // TODO: Disown all children? - #if 1 - { - tThread *child; - // TODO: I should keep a .Children list - for(child = gAllThreads; - child; - child = child->GlobalNext) - { - if(child->Parent == Thread) - child->Parent = &gThreadZero; - } - } - #endif - - ///\note Double lock is needed due to overlap of lock areas - - // Lock thread (stop us recieving messages) - SHORTLOCK( &Thread->IsLocked ); - - // Clear Message Queue - while( Thread->Messages ) - { - msg = Thread->Messages->Next; - free( Thread->Messages ); - Thread->Messages = msg; - } - - // Lock thread list - SHORTLOCK( &glThreadListLock ); - - switch(Thread->Status) - { - case THREAD_STAT_PREINIT: // Only on main list - break; - - // Currently active thread - case THREAD_STAT_ACTIVE: - if( Thread != Proc_GetCurThread() ) - { - #if SCHEDULER_TYPE == SCHED_RR_PRI - tThreadList *list = &gaActiveThreads[Thread->Priority]; - #else - tThreadList *list = &gActiveThreads; - #endif - if( Threads_int_DelFromQueue( list, Thread ) ) - { - } - else - { - Log_Warning("Threads", - "Threads_Kill - Thread %p(%i,%s) marked as active, but not on list", - Thread, Thread->TID, Thread->ThreadName - ); - } - #if SCHEDULER_TYPE == SCHED_LOTTERY - giFreeTickets -= caiTICKET_COUNTS[ Thread->Priority ]; - #endif - } - // Ensure that we are not rescheduled - Thread->Remaining = 0; // Clear Remaining Quantum - Thread->Quantum = 0; // Clear Quantum to indicate dead thread - - // Update bookkeeping - giNumActiveThreads --; - break; - // Kill it while it sleeps! - case THREAD_STAT_SLEEPING: - if( !Threads_int_DelFromQueue( &gSleepingThreads, Thread ) ) - { - Log_Warning("Threads", - "Threads_Kill - Thread %p(%i,%s) marked as sleeping, but not on list", - Thread, Thread->TID, Thread->ThreadName - ); - } - break; - - // Brains!... You cannot kill something that is already dead - case THREAD_STAT_ZOMBIE: - Log_Warning("Threads", "Threads_Kill - Thread %p(%i,%s) is undead, you cannot kill it", - Thread, Thread->TID, Thread->ThreadName); - SHORTREL( &glThreadListLock ); - SHORTREL( &Thread->IsLocked ); - return ; - - default: - Log_Warning("Threads", "Threads_Kill - BUG Un-checked status (%i)", - Thread->Status); - break; - } - - // Save exit status - Thread->RetStatus = Status; - - SHORTREL( &Thread->IsLocked ); - - Thread->Status = THREAD_STAT_ZOMBIE; - SHORTREL( &glThreadListLock ); - // TODO: Send something like SIGCHLD - Threads_Wake( Thread->Parent ); - - Log("Thread %i went *hurk* (%i)", Thread->TID, Status); - - // And, reschedule - if(isCurThread) - { - for( ;; ) - Proc_Reschedule(); - } -} - -/** - * \brief Yield remainder of the current thread's timeslice - */ -void Threads_Yield(void) -{ -// Log("Threads_Yield: by %p", __builtin_return_address(0)); - Proc_Reschedule(); -} - -/** - * \fn void Threads_Sleep(void) - * \brief Take the current process off the run queue - */ -void Threads_Sleep(void) -{ - tThread *cur = Proc_GetCurThread(); - - // Acquire Spinlock - SHORTLOCK( &glThreadListLock ); - - // Don't sleep if there is a message waiting - if( cur->Messages ) { - SHORTREL( &glThreadListLock ); - return; - } - - // Remove us from running queue - Threads_RemActive(); - // Mark thread as sleeping - cur->Status = THREAD_STAT_SLEEPING; - - // Add to Sleeping List (at the top) - Threads_int_AddToList( &gSleepingThreads, cur ); - - #if DEBUG_TRACE_STATE - Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName); - #endif - - // Release Spinlock - SHORTREL( &glThreadListLock ); - - while(cur->Status != THREAD_STAT_ACTIVE) { - Proc_Reschedule(); - if( cur->Status != THREAD_STAT_ACTIVE ) - Log("%i - Huh? why am I up? zzzz...", cur->TID); - } -} - - -/** - * \brief Wakes a sleeping/waiting thread up - * \param Thread Thread to wake - * \return Boolean Failure (Returns ERRNO) - * \warning This should ONLY be called with task switches disabled - */ -int Threads_Wake(tThread *Thread) -{ - if(!Thread) - return -EINVAL; - - switch(Thread->Status) - { - case THREAD_STAT_ACTIVE: - Log("Threads_Wake - Waking awake thread (%i)", Thread->TID); - return -EALREADY; - - case THREAD_STAT_SLEEPING: - SHORTLOCK( &glThreadListLock ); - // Remove from sleeping queue - Threads_int_DelFromQueue(&gSleepingThreads, Thread); - - SHORTREL( &glThreadListLock ); - Threads_AddActive( Thread ); - - #if DEBUG_TRACE_STATE - Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName); - #endif - return -EOK; - - case THREAD_STAT_SEMAPHORESLEEP: { - tSemaphore *sem; - tThread *th, *prev=NULL; - - sem = Thread->WaitPointer; - - SHORTLOCK( &sem->Protector ); - - // Remove from sleeping queue - for( th = sem->Waiting; th; prev = th, th = th->Next ) - if( th == Thread ) break; - if( th ) - { - if(prev) - prev->Next = Thread->Next; - else - sem->Waiting = Thread->Next; - if(sem->LastWaiting == Thread) - sem->LastWaiting = prev; - } - else - { - prev = NULL; - for( th = sem->Signaling; th; prev = th, th = th->Next ) - if( th == Thread ) break; - if( !th ) { - Log_Warning("Threads", "Thread %p(%i %s) is not on semaphore %p(%s:%s)", - Thread, Thread->TID, Thread->ThreadName, - sem, sem->ModName, sem->Name); - return -EINTERNAL; - } - - if(prev) - prev->Next = Thread->Next; - else - sem->Signaling = Thread->Next; - if(sem->LastSignaling == Thread) - sem->LastSignaling = prev; - } - - Thread->RetStatus = 0; // It didn't get anything - Threads_AddActive( Thread ); - - #if DEBUG_TRACE_STATE - Log("Threads_Sleep: %p(%i %s) woken from semaphore", Thread, Thread->TID, Thread->ThreadName); - #endif - SHORTREL( &sem->Protector ); - } return -EOK; - - case THREAD_STAT_WAITING: - Warning("Threads_Wake - Waiting threads are not currently supported"); - return -ENOTIMPL; - - case THREAD_STAT_DEAD: - Warning("Threads_Wake - Attempt to wake dead thread (%i)", Thread->TID); - return -ENOTIMPL; - - default: - Warning("Threads_Wake - Unknown process status (%i)\n", Thread->Status); - return -EINTERNAL; - } -} - -/** - * \brief Wake a thread given the TID - * \param TID Thread ID to wake - * \return Boolean Faulure (errno) - */ -int Threads_WakeTID(tTID TID) -{ - tThread *thread = Threads_GetThread(TID); - int ret; - if(!thread) - return -ENOENT; - ret = Threads_Wake( thread ); - //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread); - return ret; -} - -void Threads_ToggleTrace(int TID) -{ - tThread *thread = Threads_GetThread(TID); - if(!thread) return ; - thread->bInstrTrace = !thread->bInstrTrace; -} - -/** - * \brief Adds a thread to the active queue - */ -void Threads_AddActive(tThread *Thread) -{ - SHORTLOCK( &glThreadListLock ); - - if( Thread->Status == THREAD_STAT_ACTIVE ) { - tThread *cur = Proc_GetCurThread(); - Log_Warning("Threads", "WTF, %p CPU%i %p (%i %s) is adding %p (%i %s) when it is active", - __builtin_return_address(0), - GetCPUNum(), cur, cur->TID, cur->ThreadName, Thread, Thread->TID, Thread->ThreadName); - SHORTREL( &glThreadListLock ); - return ; - } - - // Set state - Thread->Status = THREAD_STAT_ACTIVE; -// Thread->CurCPU = -1; - // Add to active list - { - #if SCHEDULER_TYPE == SCHED_RR_PRI - tThreadList *list = &gaActiveThreads[Thread->Priority]; - #else - tThreadList *list = &gActiveThreads; - #endif - Threads_int_AddToList( list, Thread ); - } - - // Update bookkeeping - giNumActiveThreads ++; - - #if SCHEDULER_TYPE == SCHED_LOTTERY - { - int delta; - // Only change the ticket count if the thread is un-scheduled - if(Thread->CurCPU != -1) - delta = 0; - else - delta = caiTICKET_COUNTS[ Thread->Priority ]; - - giFreeTickets += delta; - # if DEBUG_TRACE_TICKETS - Log("CPU%i %p (%i %s) added, new giFreeTickets = %i [+%i]", - GetCPUNum(), Thread, Thread->TID, Thread->ThreadName, - giFreeTickets, delta - ); - # endif - } - #endif - - SHORTREL( &glThreadListLock ); -} - -/** - * \brief Removes the current thread from the active queue - * \warning This should ONLY be called with the lock held - * \return Current thread pointer - */ -tThread *Threads_RemActive(void) -{ - #if 0 - tThread *ret = Proc_GetCurThread(); - - if( !IS_LOCKED(&glThreadListLock) ) { - Log_KernelPanic("Threads", "Threads_RemActive called without lock held"); - return NULL; - } - - // Delete from active queue - #if SCHEDULER_TYPE == SCHED_RR_PRI - if( !Threads_int_DelFromQueue(&gaActiveThreads[ret->Priority], ret) ) - #else - if( !Threads_int_DelFromQueue(&gActiveThreads, ret) ) - #endif - { - Log_Warning("Threads", "Current thread %p(%i %s) is not on active queue", - ret, ret->TID, ret->ThreadName - ); - return NULL; - } - - ret->Next = NULL; - ret->Remaining = 0; - - giNumActiveThreads --; - // no need to decrement tickets, scheduler did it for us - - #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS - Log("CPU%i %p (%i %s) removed, giFreeTickets = %i [nc]", - GetCPUNum(), ret, ret->TID, ret->ThreadName, giFreeTickets); - #endif - - return ret; - #else - return Proc_GetCurThread(); - #endif -} - -/** - * \fn void Threads_SetFaultHandler(Uint Handler) - * \brief Sets the signal handler for a signal - */ -void Threads_SetFaultHandler(Uint Handler) -{ - //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler); - Proc_GetCurThread()->FaultHandler = Handler; -} - -/** - * \fn void Threads_Fault(int Num) - * \brief Calls a fault handler - */ -void Threads_Fault(int Num) -{ - tThread *thread = Proc_GetCurThread(); - - if(!thread) return ; - - Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler); - - switch(thread->FaultHandler) - { - case 0: // Panic? - Threads_Kill(thread, -1); - HALT(); - return ; - case 1: // Dump Core? - Threads_Kill(thread, -1); - HALT(); - return ; - } - - // Double Fault? Oh, F**k - if(thread->CurFaultNum != 0) { - Log_Warning("Threads", "Threads_Fault: Double fault on %i", thread->TID); - Threads_Kill(thread, -1); // For now, just kill - HALT(); - } - - thread->CurFaultNum = Num; - - Proc_CallFaultHandler(thread); -} - -/** - * \fn void Threads_SegFault(tVAddr Addr) - * \brief Called when a Segment Fault occurs - */ -void Threads_SegFault(tVAddr Addr) -{ - tThread *cur = Proc_GetCurThread(); - cur->bInstrTrace = 0; - Log_Warning("Threads", "Thread #%i committed a segfault at address %p", cur->TID, Addr); - MM_DumpTables(0, USER_MAX); - Threads_Fault( 1 ); - //Threads_Exit( 0, -1 ); -} - -// --- Process Structure Access Functions --- -tPID Threads_GetPID(void) -{ - return Proc_GetCurThread()->Process->PID; -} -tTID Threads_GetTID(void) -{ - return Proc_GetCurThread()->TID; -} -tUID Threads_GetUID(void) -{ - return Proc_GetCurThread()->Process->UID; -} -tGID Threads_GetGID(void) -{ - return Proc_GetCurThread()->Process->GID; -} - -int Threads_SetUID(tUID ID) -{ - tThread *t = Proc_GetCurThread(); - if( t->Process->UID != 0 ) { - errno = -EACCES; - return -1; - } - Log_Debug("Threads", "PID %i's UID set to %i", t->Process->PID, ID); - t->Process->UID = ID; - return 0; -} - -int Threads_SetGID(tGID ID) -{ - tThread *t = Proc_GetCurThread(); - if( t->Process->UID != 0 ) { - errno = -EACCES; - return -1; - } - Log_Debug("Threads", "PID %i's GID set to %i", t->Process->PID, ID); - t->Process->GID = ID; - return 0; -} - -// --- Per-thread storage --- -int *Threads_GetErrno(void) -{ - return &Proc_GetCurThread()->_errno; -} - -// --- Configuration --- -int *Threads_GetMaxFD(void) -{ - return &Proc_GetCurThread()->Process->MaxFD; -} -char **Threads_GetChroot(void) -{ - return &Proc_GetCurThread()->Process->RootDir; -} -char **Threads_GetCWD(void) -{ - return &Proc_GetCurThread()->Process->CurrentWorkingDir; -} -// --- - -/** - * \fn void Threads_Dump(void) - */ -void Threads_DumpActive(void) -{ - tThread *thread; - tThreadList *list; - #if SCHEDULER_TYPE == SCHED_RR_PRI - int i; - #endif - - Log("Active Threads: (%i reported)", giNumActiveThreads); - - #if SCHEDULER_TYPE == SCHED_RR_PRI - for( i = 0; i < MIN_PRIORITY+1; i++ ) - { - list = &gaActiveThreads[i]; - #else - list = &gActiveThreads; - #endif - for(thread=list->Head;thread;thread=thread->Next) - { - Log(" %p %i (%i) - %s (CPU %i)", - thread, thread->TID, thread->Process->PID, thread->ThreadName, thread->CurCPU); - if(thread->Status != THREAD_STAT_ACTIVE) - Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)", - thread->Status, THREAD_STAT_ACTIVE); - Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum); - Log(" KStack 0x%x", thread->KernelStack); - if( thread->bInstrTrace ) - Log(" Tracing Enabled"); - Proc_DumpThreadCPUState(thread); - } - - #if SCHEDULER_TYPE == SCHED_RR_PRI - } - #endif -} - -/** - * \fn void Threads_Dump(void) - * \brief Dumps a list of currently running threads - */ -void Threads_Dump(void) -{ - tThread *thread; - - Log("--- Thread Dump ---"); - Threads_DumpActive(); - - Log("All Threads:"); - for(thread=gAllThreads;thread;thread=thread->GlobalNext) - { - Log(" %p %i (%i) - %s (CPU %i)", - thread, thread->TID, thread->Process->PID, thread->ThreadName, thread->CurCPU); - Log(" State %i (%s)", thread->Status, casTHREAD_STAT[thread->Status]); - switch(thread->Status) - { - case THREAD_STAT_MUTEXSLEEP: - Log(" Mutex Pointer: %p", thread->WaitPointer); - break; - case THREAD_STAT_SEMAPHORESLEEP: - Log(" Semaphore Pointer: %p", thread->WaitPointer); - Log(" Semaphore Name: %s:%s", - ((tSemaphore*)thread->WaitPointer)->ModName, - ((tSemaphore*)thread->WaitPointer)->Name - ); - break; - case THREAD_STAT_ZOMBIE: - Log(" Return Status: %i", thread->RetStatus); - break; - default: break; - } - Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum); - Log(" KStack 0x%x", thread->KernelStack); - if( thread->bInstrTrace ) - Log(" Tracing Enabled"); - Proc_DumpThreadCPUState(thread); - } -} - -/** - * \brief Gets the next thread to run - * \param CPU Current CPU - * \param Last The thread the CPU was running - */ -tThread *Threads_GetNextToRun(int CPU, tThread *Last) -{ - tThread *thread; - - // If this CPU has the lock, we must let it complete - if( CPU_HAS_LOCK( &glThreadListLock ) ) - return Last; - - // Don't change threads if the current CPU has switches disabled - if( gaThreads_NoTaskSwitch[CPU] ) - return Last; - - // Lock thread list - SHORTLOCK( &glThreadListLock ); - - // Make sure the current (well, old) thread is marked as de-scheduled - if(Last) Last->CurCPU = -1; - - // No active threads, just take a nap - if(giNumActiveThreads == 0) { - SHORTREL( &glThreadListLock ); - #if DEBUG_TRACE_TICKETS - Log("No active threads"); - #endif - return NULL; - } - - #if 0 - #if SCHEDULER_TYPE != SCHED_RR_PRI - // Special case: 1 thread - if(giNumActiveThreads == 1) { - if( gActiveThreads.Head->CurCPU == -1 ) - gActiveThreads.Head->CurCPU = CPU; - - SHORTREL( &glThreadListLock ); - - if( gActiveThreads.Head->CurCPU == CPU ) - return gActiveThreads.Head; - - return NULL; // CPU has nothing to do - } - #endif - #endif - - // Allow the old thread to be scheduled again - if( Last ) { - if( Last->Status == THREAD_STAT_ACTIVE ) { - tThreadList *list; - #if SCHEDULER_TYPE == SCHED_LOTTERY - giFreeTickets += caiTICKET_COUNTS[ Last->Priority ]; - # if DEBUG_TRACE_TICKETS - LogF("Log: CPU%i released %p (%i %s) into the pool (%i [+%i] tickets in pool)\n", - CPU, Last, Last->TID, Last->ThreadName, giFreeTickets, - caiTICKET_COUNTS[ Last->Priority ]); - # endif - #endif - - #if SCHEDULER_TYPE == SCHED_RR_PRI - list = &gaActiveThreads[ Last->Priority ]; - #else - list = &gActiveThreads; - #endif - // Add to end of list - Threads_int_AddToList( list, Last ); - } - #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS - else - LogF("Log: CPU%i released %p (%i %s)->Status = %i (Released,not in pool)\n", - CPU, Last, Last->TID, Last->ThreadName, Last->Status); - #endif - Last->CurCPU = -1; - } - - // --- - // Lottery Scheduler - // --- - #if SCHEDULER_TYPE == SCHED_LOTTERY - { - int ticket, number; - # if 1 - number = 0; - for(thread = gActiveThreads.Head; thread; thread = thread->Next) - { - if(thread->Status != THREAD_STAT_ACTIVE) - Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i", - thread, thread->TID, thread->ThreadName, thread->Status); - if(thread->Next == thread) { - Panic("Bookkeeping fail - %p %i(%s) loops back on itself", - thread, thread->TID, thread->ThreadName, thread->Status); - } - number += caiTICKET_COUNTS[ thread->Priority ]; - } - if(number != giFreeTickets) { - Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i", - giFreeTickets, number, CPU); - } - # endif - - // No free tickets (all tasks delegated to cores) - if( giFreeTickets == 0 ) { - SHORTREL(&glThreadListLock); - return NULL; - } - - // Get the ticket number - ticket = number = rand() % giFreeTickets; - - // Find the next thread - for(thread = gActiveThreads.Head; thread; prev = thread, thread = thread->Next ) - { - if( caiTICKET_COUNTS[ thread->Priority ] > number) break; - number -= caiTICKET_COUNTS[ thread->Priority ]; - } - - // If we didn't find a thread, something went wrong - if(thread == NULL) - { - number = 0; - for(thread=gActiveThreads;thread;thread=thread->Next) { - if(thread->CurCPU >= 0) continue; - number += caiTICKET_COUNTS[ thread->Priority ]; - } - Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)", - giFreeTickets, number); - } - - // Remove - if(prev) - prev->Next = thread->Next; - else - gActiveThreads.Head = thread->Next; - if(!thread->Next) - gActiveThreads.Tail = prev; - - giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ]; - # if DEBUG_TRACE_TICKETS - LogF("Log: CPU%i allocated %p (%i %s), (%i [-%i] tickets in pool), \n", - CPU, thread, thread->TID, thread->ThreadName, - giFreeTickets, caiTICKET_COUNTS[ thread->Priority ]); - # endif - } - - // --- - // Priority based round robin scheduler - // --- - #elif SCHEDULER_TYPE == SCHED_RR_PRI - { - int i; - thread = NULL; - for( i = 0; i < MIN_PRIORITY + 1; i ++ ) - { - if( !gaActiveThreads[i].Head ) - continue ; - - thread = gaActiveThreads[i].Head; - - // Remove from head - gaActiveThreads[i].Head = thread->Next; - if(!thread->Next) - gaActiveThreads[i].Tail = NULL; - thread->Next = NULL; - break; - } - - // Anything to do? - if( !thread ) { - SHORTREL(&glThreadListLock); - return NULL; - } - if( thread->Status != THREAD_STAT_ACTIVE ) { - LogF("Oops, Thread %i (%s) is not active\n", thread->TID, thread->ThreadName); - } - } - #elif SCHEDULER_TYPE == SCHED_RR_SIM - { - // Get the next thread off the list - thread = gActiveThreads.Head; - gActiveThreads.Head = thread->Next; - if(!thread->Next) - gaActiveThreads.Tail = NULL; - thread->Next = NULL; - - // Anything to do? - if( !thread ) { - SHORTREL(&glThreadListLock); - return NULL; - } - } - #else - # error "Unimplemented scheduling algorithm" - #endif - - // Make the new thread non-schedulable - thread->CurCPU = CPU; - thread->Remaining = thread->Quantum; - - SHORTREL( &glThreadListLock ); - - return thread; -} - -// === EXPORTS === -EXPORT(Threads_GetUID); -EXPORT(Threads_GetGID); diff --git a/Kernel/time.c b/Kernel/time.c deleted file mode 100644 index 2c83fa32..00000000 --- a/Kernel/time.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Acess 2 - * - By John Hodge (thePowersGang) - * - * Timer Code - */ -#include -#include -#include -#include // Proc_GetCurThread - -// === CONSTANTS === -#define NUM_TIMERS 8 - -// === TYPEDEFS === -struct sTimer { - tTimer *Next; - Sint64 FiresAfter; - void (*Callback)(void*); - void *Argument; -}; - -// === PROTOTYPES === -void Timer_CallTimers(void); - -// === GLOBALS === -volatile Uint64 giTicks = 0; -volatile Sint64 giTimestamp = 0; -volatile Uint64 giPartMiliseconds = 0; -tTimer *gTimers; // TODO: Replace by a ring-list timer - -// === CODE === -/** - * \fn void Timer_CallTimers() - */ -void Timer_CallTimers() -{ - while( gTimers && gTimers->FiresAfter < now() ) - { - tTimer *next; - - if( gTimers->Callback ) - gTimers->Callback(gTimers->Argument); - else - Threads_PostEvent(gTimers->Argument, THREAD_EVENT_TIMER); - - next = gTimers->Next; - free(gTimers); - gTimers = next; - } -} - -/** - * \brief Schedule an action - */ -tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument) -{ - tTimer *ret; - tTimer *t, *p; - - if(Callback == NULL) - Argument = Proc_GetCurThread(); - - // TODO: Use a pool instead? - ret = malloc(sizeof(tTimer)); - - ret->Callback = Callback; - ret->FiresAfter = now() + Delta; - ret->Argument = Argument; - - // Add into list (sorted) - for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next ) - { - if( t->FiresAfter > ret->FiresAfter ) break; - } - ret->Next = t; - p->Next = ret; - - return ret; -} - -/** - * \brief Delete a timer - */ -void Time_RemoveTimer(tTimer *Timer) -{ - tTimer *t, *p; - for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next ) - { - if( t == Timer ) - { - p->Next = t->Next; - free(Timer); - return ; - } - } -} - -/** - * \fn void Time_Delay(int Delay) - * \brief Delay for a small ammount of time - */ -void Time_Delay(int Delay) -{ -// tTime dest = now() + Delay; -// while(dest > now()) Threads_Yield(); - Time_CreateTimer(Delay, NULL, NULL); - Threads_WaitEvents(THREAD_EVENT_TIMER); -} - -// === EXPORTS === -EXPORT(Time_CreateTimer); -EXPORT(Time_RemoveTimer); -EXPORT(Time_Delay); diff --git a/Kernel/vfs/acls.c b/Kernel/vfs/acls.c deleted file mode 100644 index ab88b985..00000000 --- a/Kernel/vfs/acls.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Acess Micro VFS - */ -#include -#include "vfs.h" -#include "vfs_int.h" - -// === GLOBALS === -tVFS_ACL gVFS_ACL_EveryoneRWX = { {1,-1}, {0,VFS_PERM_ALL} }; -tVFS_ACL gVFS_ACL_EveryoneRW = { {1,-1}, {0,VFS_PERM_ALL^VFS_PERM_EXECUTE} }; -tVFS_ACL gVFS_ACL_EveryoneRX = { {1,-1}, {0,VFS_PERM_READ|VFS_PERM_EXECUTE} }; -tVFS_ACL gVFS_ACL_EveryoneRO = { {1,-1}, {0,VFS_PERM_READ} }; - -// === CODE === -/** - * \fn int VFS_CheckACL(tVFS_Node *Node, Uint Permissions) - * \brief Checks the permissions on a file - */ -int VFS_CheckACL(tVFS_Node *Node, Uint Permissions) -{ - int i; - int uid = Threads_GetUID(); - int gid = Threads_GetGID(); - - // Root can do anything - if(uid == 0) return 1; - - // Root only file?, fast return - if( Node->NumACLs == 0 ) { - Log("VFS_CheckACL - %p inaccesable, NumACLs = 0, uid=%i", Node, uid); - return 0; - } - - // Check Deny Permissions - for(i=0;iNumACLs;i++) - { - if(!Node->ACLs[i].Inv) continue; // Ignore ALLOWs - if(Node->ACLs[i].ID != 0x7FFFFFFF) - { - if(!Node->ACLs[i].Group && Node->ACLs[i].ID != uid) continue; - if(Node->ACLs[i].Group && Node->ACLs[i].ID != gid) continue; - } - - //Log("Deny %x", Node->ACLs[i].Perms); - - if(Node->ACLs[i].Perms & Permissions) { - Log("VFS_CheckACL - %p inaccesable, %x denied", - Node, Node->ACLs[i].Perms & Permissions); - return 0; - } - } - - // Check for allow permissions - for(i=0;iNumACLs;i++) - { - if(Node->ACLs[i].Inv) continue; // Ignore DENYs - if(Node->ACLs[i].ID != 0x7FFFFFFF) - { - if(!Node->ACLs[i].Group && Node->ACLs[i].ID != uid) continue; - if(Node->ACLs[i].Group && Node->ACLs[i].ID != gid) continue; - } - - //Log("Allow %x", Node->ACLs[i].Perms); - - if((Node->ACLs[i].Perms & Permissions) == Permissions) return 1; - } - - Log("VFS_CheckACL - %p inaccesable, %x not allowed", Node, Permissions); - return 0; -} -/** - * \fn int VFS_GetACL(int FD, tVFS_ACL *Dest) - */ -int VFS_GetACL(int FD, tVFS_ACL *Dest) -{ - int i; - tVFS_Handle *h = VFS_GetHandle(FD); - - // Error check - if(!h) { - return -1; - } - - // Root can do anything - if(Dest->Group == 0 && Dest->ID == 0) { - Dest->Inv = 0; - Dest->Perms = -1; - return 1; - } - - // Root only file?, fast return - if( h->Node->NumACLs == 0 ) { - Dest->Inv = 0; - Dest->Perms = 0; - return 0; - } - - // Check Deny Permissions - for(i=0;iNode->NumACLs;i++) - { - if(h->Node->ACLs[i].Group != Dest->Group) continue; - if(h->Node->ACLs[i].ID != Dest->ID) continue; - - Dest->Inv = h->Node->ACLs[i].Inv; - Dest->Perms = h->Node->ACLs[i].Perms; - return 1; - } - - - Dest->Inv = 0; - Dest->Perms = 0; - return 0; -} - -/** - * \fn tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group) - * \brief Converts UNIX permissions to three Acess ACL entries - */ -tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group) -{ - tVFS_ACL *ret = malloc(sizeof(tVFS_ACL)*3); - - // Error Check - if(!ret) return NULL; - - // Owner - ret[0].Group = 0; ret[0].ID = Owner; - ret[0].Inv = 0; ret[0].Perms = 0; - if(Mode & 0400) ret[0].Perms |= VFS_PERM_READ; - if(Mode & 0200) ret[0].Perms |= VFS_PERM_WRITE; - if(Mode & 0100) ret[0].Perms |= VFS_PERM_EXECUTE; - - // Group - ret[1].Group = 1; ret[1].ID = Group; - ret[1].Inv = 0; ret[1].Perms = 0; - if(Mode & 0040) ret[1].Perms |= VFS_PERM_READ; - if(Mode & 0020) ret[1].Perms |= VFS_PERM_WRITE; - if(Mode & 0010) ret[1].Perms |= VFS_PERM_EXECUTE; - - // Global - ret[2].Group = 1; ret[2].ID = -1; - ret[2].Inv = 0; ret[2].Perms = 0; - if(Mode & 0004) ret[2].Perms |= VFS_PERM_READ; - if(Mode & 0002) ret[2].Perms |= VFS_PERM_WRITE; - if(Mode & 0001) ret[2].Perms |= VFS_PERM_EXECUTE; - - // Return buffer - return ret; -} - -// === EXPORTS === -// --- Variables --- -EXPORTV(gVFS_ACL_EveryoneRWX); -EXPORTV(gVFS_ACL_EveryoneRW); -EXPORTV(gVFS_ACL_EveryoneRX); -// --- Functions --- -EXPORT(VFS_UnixToAcessACL); diff --git a/Kernel/vfs/dir.c b/Kernel/vfs/dir.c deleted file mode 100644 index d4c15309..00000000 --- a/Kernel/vfs/dir.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Acess2 VFS - * - Directory Management Functions - */ -#define DEBUG 0 -#include -#include -#include - -// === IMPORTS === -extern tVFS_Mount *gRootMount; - -// === PROTOTYPES === -#if 0 - int VFS_MkDir(const char *Path); - int VFS_MkNod(const char *Path, Uint Flags); - int VFS_Symlink(const char *Name, const char *Link); -#endif - -// === CODE === -/** - * \fn int VFS_MkDir(char *Path) - * \brief Create a new node - * \param Path Path of directory to create - */ -int VFS_MkDir(const char *Path) -{ - return VFS_MkNod(Path, VFS_FFLAG_DIRECTORY); -} - -/** - * \fn int VFS_MkNod(char *Path, Uint Flags) - * \brief Create a new node in a directory - * \param Path Path of new node - * \param Flags Flags to apply to the node - */ -int VFS_MkNod(const char *Path, Uint Flags) -{ - char *absPath, *name; - int pos = 0, oldpos = 0; - int next = 0; - tVFS_Node *parent; - int ret; - - ENTER("sPath xFlags", Path, Flags); - - absPath = VFS_GetAbsPath(Path); - LOG("absPath = '%s'", absPath); - - while( (next = strpos(&absPath[pos+1], '/')) != -1 ) { - LOG("next = %i", next); - pos += next+1; - LOG("pos = %i", pos); - oldpos = pos; - } - absPath[oldpos] = '\0'; // Mutilate path - name = &absPath[oldpos+1]; - - LOG("absPath = '%s', name = '%s'", absPath, name); - - // Check for root - if(absPath[0] == '\0') - parent = VFS_ParsePath("/", NULL, NULL); - else - parent = VFS_ParsePath(absPath, NULL, NULL); - - LOG("parent = %p", parent); - - if(!parent) { - LEAVE('i', -1); - return -1; // Error Check - } - - // Permissions Check - if( !VFS_CheckACL(parent, VFS_PERM_EXECUTE|VFS_PERM_WRITE) ) { - _CloseNode(parent); - free(absPath); - LEAVE('i', -1); - return -1; - } - - LOG("parent = %p", parent); - - if(!parent->Type || !parent->Type->MkNod) { - Warning("VFS_MkNod - Directory has no MkNod method"); - LEAVE('i', -1); - return -1; - } - - // Create node - ret = parent->Type->MkNod(parent, name, Flags); - - // Free allocated string - free(absPath); - - // Free Parent - _CloseNode(parent); - - // Error Check - if(ret == 0) { - LEAVE('i', -1); - return -1; - } - - LEAVE('i', 0); - return 0; -} - -/** - * \fn int VFS_Symlink(const char *Name, const char *Link) - * \brief Creates a symlink called \a Name to \a Link - * \param Name Name of symbolic link - * \param Link Destination of symbolic link - */ -int VFS_Symlink(const char *Name, const char *Link) -{ - char *realLink; - int fp; - - ENTER("sName sLink", Name, Link); - - // Get absolue path name - realLink = VFS_GetAbsPath( Link ); - if(!realLink) { - Log_Warning("VFS", "Path '%s' is badly formed", Link); - LEAVE('i', -1); - return -1; - } - - LOG("realLink = '%s'", realLink); - - // Make node - if( VFS_MkNod(Name, VFS_FFLAG_SYMLINK) != 0 ) { - Log_Warning("VFS", "Unable to create link node '%s'", Name); - free(realLink); - LEAVE('i', -2); - return -2; // Make link node - } - - // Write link address - fp = VFS_Open(Name, VFS_OPENFLAG_WRITE|VFS_OPENFLAG_NOLINK); - VFS_Write(fp, strlen(realLink), realLink); - VFS_Close(fp); - - free(realLink); - - LEAVE('i', 1); - return 1; -} - -/** - * \fn int VFS_ReadDir(int FD, char *Dest) - * \brief Read from a directory - */ -int VFS_ReadDir(int FD, char *Dest) -{ - tVFS_Handle *h = VFS_GetHandle(FD); - char *tmp; - - //ENTER("ph pDest", h, Dest); - - if(!h || !h->Node->Type || !h->Node->Type->ReadDir) { - //LEAVE('i', 0); - return 0; - } - - if(h->Node->Size != -1 && h->Position >= h->Node->Size) { - //LEAVE('i', 0); - return 0; - } - - do { - tmp = h->Node->Type->ReadDir(h->Node, h->Position); - if((Uint)tmp < (Uint)VFS_MAXSKIP) - h->Position += (Uint)tmp; - else - h->Position ++; - } while(tmp != NULL && (Uint)tmp < (Uint)VFS_MAXSKIP); - - //LOG("tmp = '%s'", tmp); - - if(!tmp) { - //LEAVE('i', 0); - return 0; - } - - strcpy(Dest, tmp); - free(tmp); - - //LEAVE('i', 1); - return 1; -} diff --git a/Kernel/vfs/fs/devfs.c b/Kernel/vfs/fs/devfs.c deleted file mode 100644 index b3f4a579..00000000 --- a/Kernel/vfs/fs/devfs.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Acess 2 - * Device Filesystem (DevFS) - * - vfs/fs/devfs.c - */ -#include -#include -#include - -// === PROTOTYPES === -#if 0 - int DevFS_AddDevice(tDevFS_Driver *Device); -void DevFS_DelDevice(tDevFS_Driver *Device); -#endif -tVFS_Node *DevFS_InitDevice(const char *Device, const char **Options); -char *DevFS_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name); - -// === GLOBALS === -tVFS_Driver gDevFS_Info = { - "devfs", 0, DevFS_InitDevice, NULL, NULL - }; -tVFS_NodeType gDevFS_DirType = { - .TypeName = "DevFS-Dir", - .ReadDir = DevFS_ReadDir, - .FindDir = DevFS_FindDir - }; -tVFS_Node gDevFS_RootNode = { - .Size = 0, - .Flags = VFS_FFLAG_DIRECTORY, - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Type = &gDevFS_DirType - }; -tDevFS_Driver *gDevFS_Drivers = NULL; - int giDevFS_NextID = 1; -tShortSpinlock glDevFS_ListLock; - -// === CODE === -/** - * \fn int DevFS_AddDevice(tDevFS_Driver *Device) - */ -int DevFS_AddDevice(tDevFS_Driver *Device) -{ - int ret = 0; - tDevFS_Driver *dev; - - SHORTLOCK( &glDevFS_ListLock ); - - // Check if the device is already registered or the name is taken - for( dev = gDevFS_Drivers; dev; dev = dev->Next ) - { - if(dev == Device) break; - if(strcmp(dev->Name, Device->Name) == 0) break; - } - - if(dev) { - if(dev == Device) - Log_Warning("DevFS", "Device %p '%s' attempted to register itself twice", - dev, dev->Name); - else - Log_Warning("DevFS", "Device %p attempted to register '%s' which was owned by %p", - Device, dev->Name, dev); - ret = 0; // Error - } - else { - Device->Next = gDevFS_Drivers; - gDevFS_Drivers = Device; - gDevFS_RootNode.Size ++; - ret = giDevFS_NextID ++; - } - SHORTREL( &glDevFS_ListLock ); - - return ret; -} - -/** - * \brief Delete a device from the DevFS folder - */ -void DevFS_DelDevice(tDevFS_Driver *Device) -{ - tDevFS_Driver *prev = NULL, *dev; - - SHORTLOCK( &glDevFS_ListLock ); - // Search list for device - for(dev = gDevFS_Drivers; - dev && dev != Device; - prev = dev, dev = dev->Next - ); - - // Check if it was found - if(dev) - { - if(prev) - prev->Next = Device->Next; - else - gDevFS_Drivers = Device->Next; - } - else - Log_Warning("DevFS", "Attempted to unregister device %p '%s' which was not registered", - Device, Device->Name); - - SHORTREL( &glDevFS_ListLock ); -} - -/** - * \brief Initialise the DevFS and detect double-mounting, or just do nothing - * \note STUB - */ -tVFS_Node *DevFS_InitDevice(const char *Device, const char **Options) -{ - return &gDevFS_RootNode; -} - -/** - * \fn char *DevFS_ReadDir(tVFS_Node *Node, int Pos) - */ -char *DevFS_ReadDir(tVFS_Node *Node, int Pos) -{ - tDevFS_Driver *dev; - - if(Pos < 0) return NULL; - - for(dev = gDevFS_Drivers; - dev && Pos--; - dev = dev->Next - ); - - if(dev) - return strdup(dev->Name); - else - return NULL; -} - -/** - * \fn tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name) - * \brief Get an entry from the devices directory - */ -tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name) -{ - tDevFS_Driver *dev; - - //ENTER("pNode sName", Node, Name); - - for(dev = gDevFS_Drivers; - dev; - dev = dev->Next - ) - { - //LOG("dev = %p", dev); - //LOG("dev->Name = '%s'", dev->Name); - if(strcmp(dev->Name, Name) == 0) { - //LEAVE('p', &dev->RootNode); - return &dev->RootNode; - } - } - - //LEAVE('n'); - return NULL; -} - -// --- EXPORTS --- -EXPORT(DevFS_AddDevice); -EXPORT(DevFS_DelDevice); diff --git a/Kernel/vfs/fs/root.c b/Kernel/vfs/fs/root.c deleted file mode 100644 index 9fa1732b..00000000 --- a/Kernel/vfs/fs/root.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * AcessMicro VFS - * - Root Filesystem Driver - */ -#define DEBUG 0 -#include -#include -#include - -// === CONSTANTS === -#define MAX_FILES 64 -#define MAX_FILE_SIZE 1024 - -// === PROTOTYPES === -tVFS_Node *Root_InitDevice(const char *Device, const char **Options); - int Root_MkNod(tVFS_Node *Node, const char *Name, Uint Flags); -tVFS_Node *Root_FindDir(tVFS_Node *Node, const char *Name); -char *Root_ReadDir(tVFS_Node *Node, int Pos); -Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); -tRamFS_File *Root_int_AllocFile(void); - -// === GLOBALS === -tVFS_Driver gRootFS_Info = { - "rootfs", 0, Root_InitDevice, NULL, NULL - }; -tRamFS_File RootFS_Files[MAX_FILES]; -tVFS_ACL RootFS_DirACLs[3] = { - {{0,0}, {0,VFS_PERM_ALL}}, // Owner (Root) - {{1,0}, {0,VFS_PERM_ALL}}, // Group (Root) - {{0,-1}, {0,VFS_PERM_ALL^VFS_PERM_WRITE}} // World (Nobody) -}; -tVFS_ACL RootFS_FileACLs[3] = { - {{0,0}, {0,VFS_PERM_ALL^VFS_PERM_EXECUTE}}, // Owner (Root) - {{1,0}, {0,VFS_PERM_ALL^VFS_PERM_EXECUTE}}, // Group (Root) - {{0,-1}, {0,VFS_PERM_READ}} // World (Nobody) -}; -tVFS_NodeType gRootFS_DirType = { - .TypeName = "RootFS-Dir", - .ReadDir = Root_ReadDir, - .FindDir = Root_FindDir, - .MkNod = Root_MkNod -}; -tVFS_NodeType gRootFS_FileType = { - .TypeName = "RootFS-File", - .Read = Root_Read, - .Write = Root_Write, -}; - -// === CODE === -/** - * \brief Initialise the root filesystem - */ -tVFS_Node *Root_InitDevice(const char *Device, const char **Options) -{ - tRamFS_File *root; - if(strcmp(Device, "root") != 0) { - return NULL; - } - - // Create Root Node - root = &RootFS_Files[0]; - - root->Node.ImplPtr = root; - - root->Node.CTime - = root->Node.MTime - = root->Node.ATime = now(); - root->Node.NumACLs = 3; - root->Node.ACLs = RootFS_DirACLs; - - root->Node.Type = &gRootFS_DirType; - - return &root->Node; -} - -/** - * \fn int Root_MkNod(tVFS_Node *Node, const char *Name, Uint Flags) - * \brief Create an entry in the root directory - */ -int Root_MkNod(tVFS_Node *Node, const char *Name, Uint Flags) -{ - tRamFS_File *parent = Node->ImplPtr; - tRamFS_File *child; - tRamFS_File *prev = (tRamFS_File *) &parent->Data.FirstChild; - - ENTER("pNode sName xFlags", Node, Name, Flags); - - LOG("%i > %i", strlen(Name)+1, sizeof(child->Name)); - if(strlen(Name) + 1 > sizeof(child->Name)) - LEAVE_RET('i', 0); - - // Find last child, while we're at it, check for duplication - for( child = parent->Data.FirstChild; child; prev = child, child = child->Next ) - { - if(strcmp(child->Name, Name) == 0) { - LEAVE('i', 0); - return 0; - } - } - - child = Root_int_AllocFile(); - memset(child, 0, sizeof(tRamFS_File)); - - strcpy(child->Name, Name); - - child->Parent = parent; - child->Next = NULL; - child->Data.FirstChild = NULL; - - child->Node.ImplPtr = child; - child->Node.Flags = Flags; - child->Node.NumACLs = 3; - child->Node.Size = 0; - - if(Flags & VFS_FFLAG_DIRECTORY) - { - child->Node.ACLs = RootFS_DirACLs; - child->Node.Type = &gRootFS_DirType; - } else { - if(Flags & VFS_FFLAG_SYMLINK) - child->Node.ACLs = RootFS_DirACLs; - else - child->Node.ACLs = RootFS_FileACLs; - child->Node.Type = &gRootFS_FileType; - } - - prev->Next = child; - - parent->Node.Size ++; - - LEAVE('i', 1); - return 1; -} - -/** - * \fn tVFS_Node *Root_FindDir(tVFS_Node *Node, const char *Name) - * \brief Find an entry in the filesystem - */ -tVFS_Node *Root_FindDir(tVFS_Node *Node, const char *Name) -{ - tRamFS_File *parent = Node->ImplPtr; - tRamFS_File *child = parent->Data.FirstChild; - - //Log("Root_FindDir: (Node=%p, Name='%s')", Node, Name); - - for(;child;child = child->Next) - { - //Log(" Root_FindDir: strcmp('%s', '%s')", child->Node.Name, Name); - if(strcmp(child->Name, Name) == 0) return &child->Node; - } - - return NULL; -} - -/** - * \fn char *Root_ReadDir(tVFS_Node *Node, int Pos) - * \brief Get an entry from the filesystem - */ -char *Root_ReadDir(tVFS_Node *Node, int Pos) -{ - tRamFS_File *parent = Node->ImplPtr; - tRamFS_File *child = parent->Data.FirstChild; - - for( ; child && Pos--; child = child->Next ) ; - - if(child) return strdup(child->Name); - - return NULL; -} - -/** - * \fn Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Read from a file in the root directory - */ -Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - tRamFS_File *file = Node->ImplPtr; - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - - if(Offset > Node->Size) { - LEAVE('i', 0); - return 0; - } - if(Length > Node->Size) Length = Node->Size; - - if(Offset+Length > Node->Size) - Length = Node->Size - Offset; - - memcpy(Buffer, file->Data.Bytes+Offset, Length); - LOG("Buffer = '%.*s'", (int)Length, Buffer); - - LEAVE('i', Length); - return Length; -} - -/** - * \fn Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Write to a file in the root directory - */ -Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - tRamFS_File *file = Node->ImplPtr; - - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - - if(Offset > Node->Size) { - LEAVE('i', -1); - return -1; - } - - if(Offset + Length > MAX_FILE_SIZE) - { - Length = MAX_FILE_SIZE - Offset; - } - - LOG("Buffer = '%.*s'", (int)Length, Buffer); - - // Check if buffer needs to be expanded - if(Offset + Length > Node->Size) - { - void *tmp = realloc( file->Data.Bytes, Offset + Length ); - if(tmp == NULL) { - Warning("Root_Write - Increasing buffer size failed"); - LEAVE('i', -1); - return -1; - } - file->Data.Bytes = tmp; - Node->Size = Offset + Length; - LOG("Expanded buffer to %i bytes", (int)Node->Size); - } - - memcpy(file->Data.Bytes+Offset, Buffer, Length); - LOG("File - '%.*s'", Node->Size, file->Data.Bytes); - - LEAVE('i', Length); - return Length; -} - -/** - * \fn tRamFS_File *Root_int_AllocFile(void) - * \brief Allocates a file from the pool - */ -tRamFS_File *Root_int_AllocFile(void) -{ - int i; - for( i = 0; i < MAX_FILES; i ++ ) - { - if( RootFS_Files[i].Name[0] == '\0' ) - { - return &RootFS_Files[i]; - } - } - return NULL; -} diff --git a/Kernel/vfs/handle.c b/Kernel/vfs/handle.c deleted file mode 100644 index d22f9656..00000000 --- a/Kernel/vfs/handle.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Acess2 VFS - * - AllocHandle, GetHandle - */ -#define DEBUG 0 -#include -#include -#include "vfs.h" -#include "vfs_int.h" -#include "vfs_ext.h" -#include // GetMaxFD -#include // Handle maintainance - -// === CONSTANTS === -#define MAX_KERNEL_FILES 128 - -// === PROTOTYPES === -#if 0 -tVFS_Handle *VFS_GetHandle(int FD); -#endif - int VFS_AllocHandle(int FD, tVFS_Node *Node, int Mode); - -// === GLOBALS === -tVFS_Handle *gaUserHandles = (void*)MM_PPD_HANDLES; -tVFS_Handle *gaKernelHandles = (void*)MM_KERNEL_VFS; - -// === CODE === -/** - * \fn tVFS_Handle *VFS_GetHandle(int FD) - * \brief Gets a pointer to the handle information structure - */ -tVFS_Handle *VFS_GetHandle(int FD) -{ - tVFS_Handle *h; - - //Log_Debug("VFS", "VFS_GetHandle: (FD=0x%x)", FD); - - if(FD < 0) return NULL; - - if(FD & VFS_KERNEL_FLAG) { - FD &= (VFS_KERNEL_FLAG - 1); - if(FD >= MAX_KERNEL_FILES) return NULL; - h = &gaKernelHandles[ FD ]; - } else { - if(FD >= *Threads_GetMaxFD()) return NULL; - h = &gaUserHandles[ FD ]; - } - - if(h->Node == NULL) return NULL; - //Log_Debug("VFS", "VFS_GetHandle: RETURN %p", h); - return h; -} - -int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode) -{ - int i; - - // Check for a user open - if(bIsUser) - { - int max_handles = *Threads_GetMaxFD(); - // Allocate Buffer - if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 ) - { - Uint addr, size; - size = max_handles * sizeof(tVFS_Handle); - for(addr = 0; addr < size; addr += 0x1000) - { - if( !MM_Allocate( (tVAddr)gaUserHandles + addr ) ) - { - Warning("OOM - VFS_AllocHandle"); - Threads_Exit(0, 0xFF); // Terminate user - } - } - memset( gaUserHandles, 0, size ); - } - // Get a handle - for( i = 0; i < max_handles; i ++ ) - { - if(gaUserHandles[i].Node) continue; - gaUserHandles[i].Node = Node; - gaUserHandles[i].Position = 0; - gaUserHandles[i].Mode = Mode; - return i; - } - } - else - { - // Allocate space if not already - if( MM_GetPhysAddr( (tVAddr)gaKernelHandles ) == 0 ) - { - Uint addr, size; - size = MAX_KERNEL_FILES * sizeof(tVFS_Handle); - for(addr = 0; addr < size; addr += 0x1000) - { - if( !MM_Allocate( (tVAddr)gaKernelHandles + addr ) ) - { - Panic("OOM - VFS_AllocHandle"); - Threads_Exit(0, 0xFF); // Terminate application (get some space back) - } - } - memset( gaKernelHandles, 0, size ); - } - // Get a handle - for(i=0;iNode ) - continue ; - if( h->Node->Type && h->Node->Type->Reference ) - h->Node->Type->Reference( h->Node ); - } -} - -void VFS_CloseAllUserHandles(void) -{ - int i; - int max_handles = *Threads_GetMaxFD(); - - // Check if this process has any handles - if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 ) - return ; - - for( i = 0; i < max_handles; i ++ ) - { - tVFS_Handle *h; - h = &gaUserHandles[i]; - if( !h->Node ) - continue ; - if( h->Node->Type && h->Node->Type->Close ) - h->Node->Type->Close( h->Node ); - } -} - -/** - * \brief Take a backup of a set of file descriptors - */ -void *VFS_SaveHandles(int NumFDs, int *FDs) -{ - tVFS_Handle *ret; - int i; - int max_handles = *Threads_GetMaxFD(); - - // Check if this process has any handles - if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 ) - return NULL; - - // Allocate - ret = malloc( NumFDs * sizeof(tVFS_Handle) ); - if( !ret ) - return NULL; - - if( NumFDs > max_handles ) - NumFDs = max_handles; - - // Take copies of the handles - for( i = 0; i < NumFDs; i ++ ) - { - tVFS_Handle *h; - if( FDs == NULL ) - h = &gaUserHandles[i]; - else { - h = VFS_GetHandle(FDs[i] & (VFS_KERNEL_FLAG - 1)); - if(!h) { - Log_Warning("VFS", "VFS_SaveHandles - Invalid FD %i", - FDs[i] & (VFS_KERNEL_FLAG - 1) ); - free(ret); - return NULL; - } - } - memcpy( &ret[i], h, sizeof(tVFS_Handle) ); - - // Reference node - if( !h->Node ) - continue ; - if( h->Node->Type && h->Node->Type->Reference ) - h->Node->Type->Reference( h->Node ); - } - - return ret; -} - -void VFS_RestoreHandles(int NumFDs, void *Handles) -{ - tVFS_Handle *handles = Handles; - int i; - - // NULL = nothing to do - if( !Handles ) - return ; - - // Check if there is already a set of handles - if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) != 0 ) - return ; - - - // Allocate user handle area - { - Uint addr, size; - int max_handles = *Threads_GetMaxFD(); - size = max_handles * sizeof(tVFS_Handle); - for(addr = 0; addr < size; addr += 0x1000) - { - if( !MM_Allocate( (tVAddr)gaUserHandles + addr ) ) - { - Warning("OOM - VFS_AllocHandle"); - Threads_Exit(0, 0xFF); // Terminate user - } - } - memset( gaUserHandles, 0, size ); - } - - // Restore handles - memcpy( gaUserHandles, handles, NumFDs * sizeof(tVFS_Handle) ); - // Reference when copied - for( i = 0; i < NumFDs; i ++ ) - { - tVFS_Handle *h = &handles[i]; - - if( !h->Node ) - continue ; - if( h->Node->Type && h->Node->Type->Reference ) - h->Node->Type->Reference( h->Node ); - } -} - -void VFS_FreeSavedHandles(int NumFDs, void *Handles) -{ - tVFS_Handle *handles = Handles; - int i; - - // NULL = nothing to do - if( !Handles ) - return ; - - // Dereference all saved nodes - for( i = 0; i < NumFDs; i ++ ) - { - tVFS_Handle *h = &handles[i]; - - if( !h->Node ) - continue ; - if( h->Node->Type && h->Node->Type->Close ) - h->Node->Type->Close( h->Node ); - } - free( Handles ); -} diff --git a/Kernel/vfs/io.c b/Kernel/vfs/io.c deleted file mode 100644 index e7a2a068..00000000 --- a/Kernel/vfs/io.c +++ /dev/null @@ -1,216 +0,0 @@ -/* - * AcessMicro VFS - * - File IO Passthru's - */ -#define DEBUG 0 -#include -#include "vfs.h" -#include "vfs_int.h" - -// === CODE === -/** - * \fn Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer) - * \brief Read data from a node (file) - */ -Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer) -{ - tVFS_Handle *h; - Uint64 ret; - - ENTER("iFD XLength pBuffer", FD, Length, Buffer); - - h = VFS_GetHandle(FD); - if(!h) LEAVE_RET('i', -1); - - if( !(h->Mode & VFS_OPENFLAG_READ) || h->Node->Flags & VFS_FFLAG_DIRECTORY ) - LEAVE_RET('i', -1); - - if(!h->Node->Type || !h->Node->Type->Read) LEAVE_RET('i', 0); - - ret = h->Node->Type->Read(h->Node, h->Position, Length, Buffer); - if(ret == -1) LEAVE_RET('i', -1); - - h->Position += ret; - LEAVE('X', ret); - return ret; -} - -/** - * \fn Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Read data from a given offset (atomic) - */ -Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer) -{ - tVFS_Handle *h; - Uint64 ret; - - h = VFS_GetHandle(FD); - if(!h) return -1; - - if( !(h->Mode & VFS_OPENFLAG_READ) ) return -1; - if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1; - - if( !h->Node->Type || !h->Node->Type->Read) { - Warning("VFS_ReadAt - Node %p, does not have a read method", h->Node); - return 0; - } - ret = h->Node->Type->Read(h->Node, Offset, Length, Buffer); - if(ret == -1) return -1; - return ret; -} - -/** - * \fn Uint64 VFS_Write(int FD, Uint64 Length, const void *Buffer) - * \brief Read data from a node (file) - */ -Uint64 VFS_Write(int FD, Uint64 Length, const void *Buffer) -{ - tVFS_Handle *h; - Uint64 ret; - - h = VFS_GetHandle(FD); - if(!h) return -1; - - if( !(h->Mode & VFS_OPENFLAG_WRITE) ) return -1; - if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1; - - if( !h->Node->Type || !h->Node->Type->Write ) return 0; - - ret = h->Node->Type->Write(h->Node, h->Position, Length, Buffer); - if(ret == -1) return -1; - - h->Position += ret; - return ret; -} - -/** - * \fn Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer) - * \brief Write data to a file at a given offset - */ -Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - tVFS_Handle *h; - Uint64 ret; - - h = VFS_GetHandle(FD); - if(!h) return -1; - - if( !(h->Mode & VFS_OPENFLAG_WRITE) ) return -1; - if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1; - - if(!h->Node->Type || !h->Node->Type->Write) return 0; - ret = h->Node->Type->Write(h->Node, Offset, Length, Buffer); - - if(ret == -1) return -1; - return ret; -} - -/** - * \fn Uint64 VFS_Tell(int FD) - * \brief Returns the current file position - */ -Uint64 VFS_Tell(int FD) -{ - tVFS_Handle *h; - - h = VFS_GetHandle(FD); - if(!h) return -1; - - return h->Position; -} - -/** - * \fn int VFS_Seek(int FD, Sint64 Offset, int Whence) - * \brief Seek to a new location - * \param FD File descriptor - * \param Offset Where to go - * \param Whence From where - */ -int VFS_Seek(int FD, Sint64 Offset, int Whence) -{ - tVFS_Handle *h; - - h = VFS_GetHandle(FD); - if(!h) return -1; - - //Log_Debug("VFS", "VFS_Seek: (fd=0x%x, Offset=0x%llx, Whence=%i)", - // FD, Offset, Whence); - - // Set relative to current position - if(Whence == 0) { - h->Position += Offset; - return 0; - } - - // Set relative to end of file - if(Whence < 0) { - if( h->Node->Size == -1 ) return -1; - - h->Position = h->Node->Size - Offset; - return 0; - } - - // Set relative to start of file - h->Position = Offset; - return 0; -} - -/** - * \fn int VFS_IOCtl(int FD, int ID, void *Buffer) - * \brief Call an IO Control on a file - */ -int VFS_IOCtl(int FD, int ID, void *Buffer) -{ - tVFS_Handle *h; - - h = VFS_GetHandle(FD); - if(!h) return -1; - - if(!h->Node->Type || !h->Node->Type->IOCtl) return -1; - return h->Node->Type->IOCtl(h->Node, ID, Buffer); -} - -/** - * \fn int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs) - * \brief Retrieve file information - * \return Number of ACLs stored - */ -int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs) -{ - tVFS_Handle *h; - int max; - - h = VFS_GetHandle(FD); - if(!h) return -1; - - if( h->Mount ) - Dest->mount = h->Mount->Identifier; - else - Dest->mount = 0; - Dest->inode = h->Node->Inode; - Dest->uid = h->Node->UID; - Dest->gid = h->Node->GID; - Dest->size = h->Node->Size; - Dest->atime = h->Node->ATime; - Dest->ctime = h->Node->MTime; - Dest->mtime = h->Node->CTime; - Dest->numacls = h->Node->NumACLs; - - Dest->flags = 0; - if(h->Node->Flags & VFS_FFLAG_DIRECTORY) Dest->flags |= 0x10; - if(h->Node->Flags & VFS_FFLAG_SYMLINK) Dest->flags |= 0x20; - - max = (MaxACLs < h->Node->NumACLs) ? MaxACLs : h->Node->NumACLs; - memcpy(&Dest->acls, h->Node->ACLs, max*sizeof(tVFS_ACL)); - - return max; -} - -// === EXPORTS === -EXPORT(VFS_Read); -EXPORT(VFS_Write); -EXPORT(VFS_ReadAt); -EXPORT(VFS_WriteAt); -EXPORT(VFS_IOCtl); -EXPORT(VFS_Seek); -EXPORT(VFS_Tell); diff --git a/Kernel/vfs/main.c b/Kernel/vfs/main.c deleted file mode 100644 index 5ff0e7c2..00000000 --- a/Kernel/vfs/main.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Acess 2 - * Virtual File System - */ -#include -#include -#include -#include -#include -#include - -// === IMPORTS === -extern tVFS_Driver gRootFS_Info; -extern tVFS_Driver gDevFS_Info; - -// === PROTOTYPES === -#if 0 - int VFS_Init(void); -char *VFS_GetTruePath(const char *Path); -void VFS_GetMemPath(char *Dest, void *Base, Uint Length); -tVFS_Driver *VFS_GetFSByName(const char *Name); - int VFS_AddDriver(tVFS_Driver *Info); -#endif -void VFS_UpdateDriverFile(void); - -// === EXPORTS === -EXPORT(VFS_AddDriver); - -// === GLOBALS === -tVFS_Node NULLNode = {0}; -tShortSpinlock slDriverListLock; -tVFS_Driver *gVFS_Drivers = NULL; -char *gsVFS_DriverFile = NULL; - int giVFS_DriverFileID = 0; - -char *gsVFS_MountFile = NULL; - int giVFS_MountFileID = 0; - -// === CODE === -/** - * \fn int VFS_Init(void) - * \brief Initialises the VFS for use by the kernel and user - */ -int VFS_Init(void) -{ - // Core Drivers - gVFS_Drivers = &gRootFS_Info; - gVFS_Drivers->Next = &gDevFS_Info; - VFS_UpdateDriverFile(); - - // Register with SysFS - giVFS_MountFileID = SysFS_RegisterFile("VFS/Mounts", NULL, 0); - giVFS_DriverFileID = SysFS_RegisterFile("VFS/Drivers", NULL, 0); - - if( VFS_Mount("root", "/", "rootfs", "") != 0 ) { - Log_KernelPanic("VFS", "Unable to mount root (Where the **** is rootfs?)"); - return -1; - } - VFS_MkDir("/Devices"); - VFS_MkDir("/Mount"); - VFS_Mount("dev", "/Devices", "devfs", ""); - - Log_Debug("VFS", "Setting max files"); - *Threads_GetMaxFD() = 32; - return 0; -} - -/** - * \fn char *VFS_GetTruePath(const char *Path) - * \brief Gets the true path (non-symlink) of a file - */ -char *VFS_GetTruePath(const char *Path) -{ - tVFS_Node *node; - char *ret, *tmp; - - tmp = VFS_GetAbsPath(Path); - if(tmp == NULL) return NULL; - //Log(" VFS_GetTruePath: tmp = '%s'", tmp); - node = VFS_ParsePath(tmp, &ret, NULL); - free(tmp); - //Log(" VFS_GetTruePath: node=%p, ret='%s'", node, ret); - - if(!node) return NULL; - if(node->Type->Close) node->Type->Close(node); - - return ret; -} - -/** - * \fn void VFS_GetMemPath(char *Dest, void *Base, Uint Length) - * \brief Create a VFS memory pointer path - */ -void VFS_GetMemPath(char *Dest, void *Base, Uint Length) -{ - Dest[0] = '$'; - itoa( &Dest[1], (tVAddr)Base, 16, BITS/4, '0' ); - Dest[BITS/4+1] = ':'; - itoa( &Dest[BITS/4+2], Length, 16, BITS/4, '0' ); - Dest[BITS/2+2] = '\0'; -} - -/** - * \fn tVFS_Driver *VFS_GetFSByName(const char *Name) - * \brief Gets a filesystem structure given a name - */ -tVFS_Driver *VFS_GetFSByName(const char *Name) -{ - tVFS_Driver *drv = gVFS_Drivers; - - for(;drv;drv=drv->Next) - { -// Log("strcmp('%s' (%p), '%s') == 0?", drv->Name, drv->Name, Name); - if(strcmp(drv->Name, Name) == 0) - return drv; - } - return NULL; -} - -/** - * \fn int VFS_AddDriver(tVFS_Driver *Info) - */ -int VFS_AddDriver(tVFS_Driver *Info) -{ - if(!Info) return -1; - - SHORTLOCK( &slDriverListLock ); - Info->Next = gVFS_Drivers; - gVFS_Drivers = Info; - SHORTREL( &slDriverListLock ); - - VFS_UpdateDriverFile(); - - return 0; -} - -/** - * \fn void VFS_UpdateDriverFile(void) - * \brief Updates the driver list file - */ -void VFS_UpdateDriverFile(void) -{ - tVFS_Driver *drv; - int len = 0; - char *buf; - - // Format: - // \n - for( drv = gVFS_Drivers; drv; drv = drv->Next ) - { - len += 1 + strlen(drv->Name); - } - buf = malloc(len+1); - len = 0; - for( drv = gVFS_Drivers; drv; drv = drv->Next ) - { - strcpy( &buf[len], drv->Name ); - len += strlen(drv->Name); - buf[len++] = '\n'; - } - buf[len] = '\0'; - - SysFS_UpdateFile( giVFS_DriverFileID, buf, len ); - if(gsVFS_DriverFile) free(gsVFS_DriverFile); - gsVFS_DriverFile = buf; -} diff --git a/Kernel/vfs/memfile.c b/Kernel/vfs/memfile.c deleted file mode 100644 index 14b39481..00000000 --- a/Kernel/vfs/memfile.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Acess 2 - * Virtual File System - * - Memory Pseudo Files - */ -#include -#include - -// === PROTOTYPES === -tVFS_Node *VFS_MemFile_Create(const char *Path); -void VFS_MemFile_Close(tVFS_Node *Node); -Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); - -// === GLOBALS === -tVFS_NodeType gVFS_MemFileType = { - .Close = VFS_MemFile_Close, - .Read = VFS_MemFile_Read, - .Write = VFS_MemFile_Write - }; - -// === CODE === -/** - * \fn tVFS_Node *VFS_MemFile_Create(const char *Path) - */ -tVFS_Node *VFS_MemFile_Create(const char *Path) -{ - Uint base, size; - const char *str = Path; - tVFS_Node *ret; - - str++; // Eat '$' - - // Read Base address - base = 0; - for( ; ('0' <= *str && *str <= '9') || ('A' <= *str && *str <= 'F'); str++ ) - { - base *= 16; - if('A' <= *str && *str <= 'F') - base += *str - 'A' + 10; - else - base += *str - '0'; - } - - // Check separator - if(*str++ != ':') return NULL; - - // Read buffer size - size = 0; - for( ; ('0' <= *str && *str <= '9') || ('A' <= *str && *str <= 'F'); str++ ) - { - size *= 16; - if('A' <= *str && *str <= 'F') - size += *str - 'A' + 10; - else - size += *str - '0'; - } - - // Check for NULL byte - if(*str != '\0') return NULL; - - // Allocate and fill node - ret = malloc(sizeof(tVFS_Node)); - memset(ret, 0, sizeof(tVFS_Node)); - - // State - ret->ImplPtr = (void*)base; - ret->Size = size; - - // ACLs - ret->NumACLs = 1; - ret->ACLs = &gVFS_ACL_EveryoneRWX; - - // Functions - ret->Type = &gVFS_MemFileType; - - return ret; -} - -/** - * \fn void VFS_MemFile_Close(tVFS_Node *Node) - * \brief Dereference and clean up a memory file - */ -void VFS_MemFile_Close(tVFS_Node *Node) -{ - Node->ReferenceCount --; - if( Node->ReferenceCount == 0 ) { - Node->ImplPtr = NULL; - free(Node); - } -} - -/** - * \fn Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Read from a memory file - */ -Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - // Check for use of free'd file - if(Node->ImplPtr == NULL) return 0; - - // Check for out of bounds read - if(Offset > Node->Size) return 0; - - // Truncate data read if needed - if(Offset + Length > Node->Size) - Length = Node->Size - Offset; - - // Copy Data - memcpy(Buffer, (Uint8*)Node->ImplPtr + Offset, Length); - - return Length; -} - -/** - * \fn Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Write to a memory file - */ -Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - // Check for use of free'd file - if(Node->ImplPtr == NULL) return 0; - - // Check for out of bounds read - if(Offset > Node->Size) return 0; - - // Truncate data read if needed - if(Offset + Length > Node->Size) - Length = Node->Size - Offset; - - // Copy Data - memcpy((Uint8*)Node->ImplPtr + Offset, Buffer, Length); - - return Length; -} diff --git a/Kernel/vfs/mmap.c b/Kernel/vfs/mmap.c deleted file mode 100644 index 9fe9282c..00000000 --- a/Kernel/vfs/mmap.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Acess2 Kernel VFS - * - By John Hodge (thePowersGang) - * - * mmap.c - * - VFS_MMap support - */ -#define DEBUG 0 -#include -#include -#include -#include - -#define MMAP_PAGES_PER_BLOCK 16 - -// === STRUCTURES === -typedef struct sVFS_MMapPageBlock tVFS_MMapPageBlock; -struct sVFS_MMapPageBlock -{ - tVFS_MMapPageBlock *Next; - Uint64 BaseOffset; // Must be a multiple of MMAP_PAGES_PER_BLOCK*PAGE_SIZE - tPAddr PhysAddrs[MMAP_PAGES_PER_BLOCK]; -}; - -// === CODE === -void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset) -{ - tVFS_Handle *h; - tVAddr mapping_dest, mapping_base; - int npages, pagenum; - tVFS_MMapPageBlock *pb, *prev; - - ENTER("pDestHint iLength xProtection xFlags xFD XOffset", DestHint, Length, Protection, Flags, FD, Offset); - - if( Flags & MMAP_MAP_ANONYMOUS ) - Offset = (tVAddr)DestHint & 0xFFF; - - npages = ((Offset & (PAGE_SIZE-1)) + Length + (PAGE_SIZE - 1)) / PAGE_SIZE; - pagenum = Offset / PAGE_SIZE; - - mapping_base = (tVAddr)DestHint; - mapping_dest = mapping_base & ~(PAGE_SIZE-1); - - // TODO: Locate space for the allocation - - // Handle anonymous mappings - if( Flags & MMAP_MAP_ANONYMOUS ) - { - size_t ofs = 0; - LOG("%i pages anonymous to %p", npages, mapping_dest); - for( ; npages --; mapping_dest += PAGE_SIZE, ofs += PAGE_SIZE ) - { - if( MM_GetPhysAddr(mapping_dest) ) { - // TODO: Set flags to COW if needed (well, if shared) - MM_SetFlags(mapping_dest, MM_PFLAG_COW, MM_PFLAG_COW); - LOG("clear from %p, %i bytes", (void*)(mapping_base + ofs), - PAGE_SIZE - (mapping_base & (PAGE_SIZE-1)) - ); - memset( (void*)(mapping_base + ofs), 0, PAGE_SIZE - (mapping_base & (PAGE_SIZE-1))); - } - else { - LOG("New empty page"); - // TODO: Map a COW zero page instead - if( !MM_Allocate(mapping_dest) ) { - // TODO: Error - Log_Warning("VFS", "VFS_MMap: Anon alloc to %p failed", mapping_dest); - } - memset((void*)mapping_dest, 0, PAGE_SIZE); - LOG("Anon map to %p", mapping_dest); - } - } - LEAVE_RET('p', (void*)mapping_base); - } - - h = VFS_GetHandle(FD); - if( !h || !h->Node ) LEAVE_RET('n', NULL); - - LOG("h = %p", h); - - Mutex_Acquire( &h->Node->Lock ); - - // Search for existing mapping for each page - // - Sorted list of 16 page blocks - for( - pb = h->Node->MMapInfo, prev = NULL; - pb && pb->BaseOffset + MMAP_PAGES_PER_BLOCK < pagenum; - prev = pb, pb = pb->Next - ); - - LOG("pb = %p, pb->BaseOffset = %X", pb, pb ? pb->BaseOffset : 0); - - // - Allocate a block if needed - if( !pb || pb->BaseOffset > pagenum ) - { - void *old_pb = pb; - pb = malloc( sizeof(tVFS_MMapPageBlock) ); - if(!pb) { - Mutex_Release( &h->Node->Lock ); - LEAVE_RET('n', NULL); - } - pb->Next = old_pb; - pb->BaseOffset = pagenum - pagenum % MMAP_PAGES_PER_BLOCK; - memset(pb->PhysAddrs, 0, sizeof(pb->PhysAddrs)); - if(prev) - prev->Next = pb; - else - h->Node->MMapInfo = pb; - } - - // - Map (and allocate) pages - while( npages -- ) - { - if( MM_GetPhysAddr(mapping_dest) == 0 ) - { - if( pb->PhysAddrs[pagenum - pb->BaseOffset] == 0 ) - { - tVFS_NodeType *nt = h->Node->Type; - if( !nt ) - { - // TODO: error - } - else if( nt->MMap ) - nt->MMap(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE, (void*)mapping_dest); - else - { - int read_len; - // Allocate pages and read data - if( MM_Allocate(mapping_dest) == 0 ) { - // TODO: Unwrap - Mutex_Release( &h->Node->Lock ); - LEAVE('n'); - return NULL; - } - // TODO: Clip read length - read_len = nt->Read(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE, (void*)mapping_dest); -// if( read_len != PAGE_SIZE ) { -// memset( (void*)(mapping_dest+read_len), 0, PAGE_SIZE-read_len ); -// } - } - pb->PhysAddrs[pagenum - pb->BaseOffset] = MM_GetPhysAddr( mapping_dest ); - MM_SetPageNode( pb->PhysAddrs[pagenum - pb->BaseOffset], h->Node ); - MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] ); - LOG("Read and map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest, - pb->PhysAddrs[pagenum - pb->BaseOffset]); - } - else - { - MM_Map( mapping_dest, pb->PhysAddrs[pagenum - pb->BaseOffset] ); - MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] ); - LOG("Cached map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest, - pb->PhysAddrs[pagenum - pb->BaseOffset]); - } - h->Node->ReferenceCount ++; - - // Set flags - if( !(Protection & MMAP_PROT_WRITE) ) { - MM_SetFlags(mapping_dest, MM_PFLAG_RO, MM_PFLAG_RO); - } - else { - MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO); - } - - if( Protection & MMAP_PROT_EXEC ) { - MM_SetFlags(mapping_dest, MM_PFLAG_EXEC, MM_PFLAG_EXEC); - } - else { - MM_SetFlags(mapping_dest, 0, MM_PFLAG_EXEC); - } - } - else - { - LOG("Flag update on %p", mapping_dest); - if( (MM_GetFlags(mapping_dest) & MM_PFLAG_RO) && (Protection & MMAP_PROT_WRITE) ) - { - MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO); - } - } - if( Flags & MMAP_MAP_PRIVATE ) - MM_SetFlags(mapping_dest, MM_PFLAG_COW, MM_PFLAG_COW); - pagenum ++; - mapping_dest += PAGE_SIZE; - - // Roll on to next block if needed - if(pagenum - pb->BaseOffset == MMAP_PAGES_PER_BLOCK) - { - if( pb->Next && pb->Next->BaseOffset == pagenum ) - pb = pb->Next; - else - { - tVFS_MMapPageBlock *oldpb = pb; - pb = malloc( sizeof(tVFS_MMapPageBlock) ); - pb->Next = oldpb->Next; - pb->BaseOffset = pagenum; - memset(pb->PhysAddrs, 0, sizeof(pb->PhysAddrs)); - oldpb->Next = pb; - } - pagenum = 0; - } - } - - Mutex_Release( &h->Node->Lock ); - - LEAVE('p', mapping_base); - return (void*)mapping_base; -} - -int VFS_MUnmap(void *Addr, size_t Length) -{ - return 0; -} diff --git a/Kernel/vfs/mount.c b/Kernel/vfs/mount.c deleted file mode 100644 index 1773218a..00000000 --- a/Kernel/vfs/mount.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Acess Micro - VFS Server version 1 - */ -#include -#include -#include -#include - -// === IMPORTS === -extern int giVFS_MountFileID; -extern char *gsVFS_MountFile; - -// === PROTOTYPES === -#if 0 - int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options); -#endif -void VFS_UpdateMountFile(void); - -// === GLOBALS === -tMutex glVFS_MountList; -tVFS_Mount *gVFS_Mounts; -tVFS_Mount *gVFS_RootMount = NULL; -Uint32 giVFS_NextMountIdent = 1; - -// === CODE === -/** - * \brief Mount a device - * \param Device Device string to mount - * \param MountPoint Destination for the mount - * \param Filesystem Filesystem to use for the mount - * \param Options Options to be passed to the filesystem - * \return -1 on Invalid FS, -2 on No Mem, 0 on success - * - * Mounts the filesystem on \a Device at \a MountPoint using the driver - * \a Filesystem. The options in the string \a Options is passed to the - * driver's mount. - */ -int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options) -{ - tVFS_Mount *mnt; - tVFS_Driver *fs; - int deviceLen = strlen(Device); - int mountLen = strlen(MountPoint); - int argLen = strlen(Options); - - // Get the filesystem - fs = VFS_GetFSByName(Filesystem); - if(!fs) { - Log_Warning("VFS", "VFS_Mount - Unknown FS Type '%s'", Filesystem); - return -1; - } - - // Create mount information - mnt = malloc( sizeof(tVFS_Mount)+deviceLen+1+mountLen+1+argLen+1 ); - if(!mnt) { - return -2; - } - - // HACK: Forces VFS_ParsePath to fall back on root - if(mountLen == 1 && MountPoint[0] == '/') - mnt->MountPointLen = 0; - else - mnt->MountPointLen = mountLen; - - // Fill Structure - mnt->Filesystem = fs; - - mnt->Device = &mnt->StrData[0]; - memcpy( mnt->Device, Device, deviceLen+1 ); - - mnt->MountPoint = &mnt->StrData[deviceLen+1]; - memcpy( mnt->MountPoint, MountPoint, mountLen+1 ); - - mnt->Options = &mnt->StrData[deviceLen+1+mountLen+1]; - memcpy( mnt->Options, Options, argLen+1 ); - - // Initialise Volume - mnt->RootNode = fs->InitDevice(Device, NULL); //&ArgString); - if(!mnt->RootNode) { - free(mnt); - return -2; - } - - mnt->Identifier = giVFS_NextMountIdent++; - #if 0 - // Ensure identifiers don't repeat - // - Only a problem if there have been 4 billion mounts - while( giVFS_NextMountIdent == 0 || VFS_GetMountByIdent(giVFS_NextMountIdent) ) - giVFS_NextMountIdent ++; - #endif - - // Set root - if(!gVFS_RootMount) gVFS_RootMount = mnt; - - // Add to mount list - Mutex_Acquire( &glVFS_MountList ); - { - tVFS_Mount *tmp; - mnt->Next = NULL; - if(gVFS_Mounts) { - for( tmp = gVFS_Mounts; tmp->Next; tmp = tmp->Next ); - tmp->Next = mnt; - } - else { - gVFS_Mounts = mnt; - } - } - Mutex_Release( &glVFS_MountList ); - - Log_Log("VFS", "Mounted '%s' to '%s' ('%s')", Device, MountPoint, Filesystem); - - VFS_UpdateMountFile(); - - return 0; -} - -/** - * \brief Gets a mount point given the identifier - */ -tVFS_Mount *VFS_GetMountByIdent(Uint32 MountID) -{ - tVFS_Mount *mnt; - for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next) - { - if(mnt->Identifier == MountID) - return mnt; - } - return NULL; -} - -/** - * \brief Updates the mount file buffer - * - * Updates the ProcFS mounts file buffer to match the current mounts list. - */ -void VFS_UpdateMountFile(void) -{ - int len = 0; - char *buf; - tVFS_Mount *mnt; - - // Format: - // \t\t\t\n - - for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next) - { - len += 4 + strlen(mnt->Device) + strlen(mnt->MountPoint) - + strlen(mnt->Filesystem->Name) + strlen(mnt->Options); - } - - buf = malloc( len + 1 ); - len = 0; - for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next) - { - strcpy( &buf[len], mnt->Device ); - len += strlen(mnt->Device); - buf[len++] = '\t'; - - strcpy( &buf[len], mnt->MountPoint ); - len += strlen(mnt->MountPoint); - buf[len++] = '\t'; - - strcpy( &buf[len], mnt->Filesystem->Name ); - len += strlen(mnt->Filesystem->Name); - buf[len++] = '\t'; - - strcpy( &buf[len], mnt->Options ); - len += strlen(mnt->Options); - buf[len++] = '\n'; - } - buf[len] = 0; - - SysFS_UpdateFile( giVFS_MountFileID, buf, len ); - if( gsVFS_MountFile ) free( gsVFS_MountFile ); - gsVFS_MountFile = buf; -} diff --git a/Kernel/vfs/nodecache.c b/Kernel/vfs/nodecache.c deleted file mode 100644 index d2796c1b..00000000 --- a/Kernel/vfs/nodecache.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * AcessMicro VFS - * - File IO Passthru's - */ -#include -#include "vfs.h" -#include "vfs_int.h" - -// === TYPES === -typedef struct sCachedInode { - struct sCachedInode *Next; - tVFS_Node Node; -} tCachedInode; -typedef struct sInodeCache { - struct sInodeCache *Next; - int Handle; - tCachedInode *FirstNode; // Sorted List - Uint64 MaxCached; // Speeds up Searching -} tInodeCache; - -// === PROTOTYPES === -tInodeCache *Inode_int_GetFSCache(int Handle); - -// === GLOBALS === - int gVFS_NextInodeHandle = 1; -tShortSpinlock glVFS_InodeCache; -tInodeCache *gVFS_InodeCache = NULL; - -// === CODE === -/** - * \fn int Inode_GetHandle() - */ -int Inode_GetHandle() -{ - tInodeCache *ent; - - ent = malloc( sizeof(tInodeCache) ); - ent->MaxCached = 0; - ent->Handle = gVFS_NextInodeHandle++; - ent->Next = NULL; ent->FirstNode = NULL; - - // Add to list - SHORTLOCK( &glVFS_InodeCache ); - ent->Next = gVFS_InodeCache; - gVFS_InodeCache = ent; - SHORTREL( &glVFS_InodeCache ); - - return gVFS_NextInodeHandle-1; -} - -/** - * \fn tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode) - * \brief Gets a node from the cache - */ -tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode) -{ - tInodeCache *cache; - tCachedInode *ent; - - cache = Inode_int_GetFSCache(Handle); - if(!cache) return NULL; - - if(Inode > cache->MaxCached) return NULL; - - // Search Cache - ent = cache->FirstNode; - for( ; ent; ent = ent->Next ) - { - if(ent->Node.Inode < Inode) continue; - if(ent->Node.Inode > Inode) return NULL; - ent->Node.ReferenceCount ++; - return &ent->Node; - } - - return NULL; // Should never be reached -} - -/** - * \fn tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node) - */ -tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node) -{ - tInodeCache *cache; - tCachedInode *newEnt, *ent, *prev; - - cache = Inode_int_GetFSCache(Handle); - if(!cache) return NULL; - - if(Node->Inode > cache->MaxCached) - cache->MaxCached = Node->Inode; - - // Search Cache - ent = cache->FirstNode; - prev = (tCachedInode*) &cache->FirstNode; - for( ; ent; prev = ent, ent = ent->Next ) - { - if(ent->Node.Inode < Node->Inode) continue; - if(ent->Node.Inode == Node->Inode) { - ent->Node.ReferenceCount ++; - return &ent->Node; - } - break; - } - - // Create new entity - newEnt = malloc(sizeof(tCachedInode)); - newEnt->Next = ent; - memcpy(&newEnt->Node, Node, sizeof(tVFS_Node)); - prev->Next = newEnt; - - return &newEnt->Node; -} - -/** - * \fn void Inode_UncacheNode(int Handle, Uint64 Inode) - * \brief Dereferences/Removes a cached node - */ -void Inode_UncacheNode(int Handle, Uint64 Inode) -{ - tInodeCache *cache; - tCachedInode *ent, *prev; - - cache = Inode_int_GetFSCache(Handle); - if(!cache) return ; - - if(Inode > cache->MaxCached) return ; - - // Search Cache - ent = cache->FirstNode; - prev = (tCachedInode*) &cache->FirstNode; // Special case removal - for( ; ent; prev = ent, ent = ent->Next ) - { - if(ent->Node.Inode < Inode) continue; - if(ent->Node.Inode > Inode) return; - ent->Node.ReferenceCount --; - // Check if node needs to be freed - if(ent->Node.ReferenceCount == 0) - { - prev->Next = ent->Next; - if(ent->Node.Inode == cache->MaxCached) - { - if(ent != cache->FirstNode) - cache->MaxCached = prev->Node.Inode; - else - cache->MaxCached = 0; - } - - free(ent); - } - return ; - } - - return ; -} - -/** - * \fn void Inode_ClearCache(int Handle) - * \brief Removes a cache - */ -void Inode_ClearCache(int Handle) -{ - tInodeCache *cache; - tInodeCache *prev = NULL; - tCachedInode *ent, *next; - - // Find the cache - for( - cache = gVFS_InodeCache; - cache && cache->Handle < Handle; - prev = cache, cache = cache->Next - ); - if(!cache || cache->Handle != Handle) return; - - // Search Cache - ent = cache->FirstNode; - while( ent ) - { - ent->Node.ReferenceCount = 1; - next = ent->Next; - - if(ent->Node.Type && ent->Node.Type->Close) - ent->Node.Type->Close( &ent->Node ); - free(ent); - - ent = next; - } - - // Free Cache - if(prev == NULL) - gVFS_InodeCache = cache->Next; - else - prev->Next = cache->Next; - free(cache); -} - -/** - * \fn tInodeCache *Inode_int_GetFSCache(int Handle) - * \brief Gets a cache given it's handle - */ -tInodeCache *Inode_int_GetFSCache(int Handle) -{ - tInodeCache *cache = gVFS_InodeCache; - // Find Cache - for( ; cache; cache = cache->Next ) - { - if(cache->Handle > Handle) continue; - if(cache->Handle < Handle) { - Warning("Inode_int_GetFSCache - Handle %i not in cache\n", Handle); - return NULL; - } - break; - } - if(!cache) { - Warning("Inode_int_GetFSCache - Handle %i not in cache [NULL]\n", Handle); - return NULL; - } - - return cache; -} diff --git a/Kernel/vfs/open.c b/Kernel/vfs/open.c deleted file mode 100644 index 1536d1a0..00000000 --- a/Kernel/vfs/open.c +++ /dev/null @@ -1,731 +0,0 @@ -/* - * Acess2 VFS - * - Open, Close and ChDir - */ -#define DEBUG 0 -#include -#include "vfs.h" -#include "vfs_int.h" -#include "vfs_ext.h" -#include - -// === CONSTANTS === -#define OPEN_MOUNT_ROOT 1 -#define MAX_PATH_SLASHES 256 -#define MAX_NESTED_LINKS 4 -#define MAX_PATH_LEN 255 - -// === IMPORTS === -extern tVFS_Mount *gVFS_RootMount; -extern int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode); -extern tVFS_Node *VFS_MemFile_Create(const char *Path); - -// === PROTOTYPES === - int VFS_int_CreateHandle( tVFS_Node *Node, tVFS_Mount *Mount, int Mode ); - -// === CODE === -/** - * \fn char *VFS_GetAbsPath(const char *Path) - * \brief Create an absolute path from a relative one - */ -char *VFS_GetAbsPath(const char *Path) -{ - char *ret; - int pathLen = strlen(Path); - char *pathComps[MAX_PATH_SLASHES]; - char *tmpStr; - int iPos = 0; - int iPos2 = 0; - const char *chroot = *Threads_GetChroot(); - int chrootLen; - const char *cwd = *Threads_GetCWD(); - int cwdLen; - - ENTER("sPath", Path); - - // Memory File - if(Path[0] == '$') { - ret = malloc(strlen(Path)+1); - if(!ret) { - Log_Warning("VFS", "VFS_GetAbsPath: malloc() returned NULL"); - return NULL; - } - strcpy(ret, Path); - LEAVE('p', ret); - return ret; - } - - // - Fetch ChRoot - if( chroot == NULL ) - chroot = ""; - chrootLen = strlen(chroot); - - // Check if the path is already absolute - if(Path[0] == '/') { - ret = malloc(chrootLen + pathLen + 1); - if(!ret) { - Log_Warning("VFS", "VFS_GetAbsPath: malloc() returned NULL"); - return NULL; - } - strcpy(ret + chrootLen, Path); - } - else { - if(cwd == NULL) { - cwd = "/"; - cwdLen = 1; - } - else { - cwdLen = strlen(cwd); - } - // Prepend the current directory - ret = malloc(chrootLen + cwdLen + 1 + pathLen + 1 ); - strcpy(ret+chrootLen, cwd); - ret[cwdLen] = '/'; - strcpy(ret+chrootLen+cwdLen+1, Path); - //Log("ret = '%s'", ret); - } - - // Parse Path - pathComps[iPos++] = tmpStr = ret+chrootLen+1; - while(*tmpStr) - { - if(*tmpStr++ == '/') - { - pathComps[iPos++] = tmpStr; - if(iPos == MAX_PATH_SLASHES) { - LOG("Path '%s' has too many elements", Path); - free(ret); - LEAVE('n'); - return NULL; - } - } - } - pathComps[iPos] = NULL; - - // Cleanup - iPos2 = iPos = 0; - while(pathComps[iPos]) - { - tmpStr = pathComps[iPos]; - // Always Increment iPos - iPos++; - // .. - if(tmpStr[0] == '.' && tmpStr[1] == '.' && (tmpStr[2] == '/' || tmpStr[2] == '\0') ) - { - if(iPos2 != 0) - iPos2 --; - continue; - } - // . - if(tmpStr[0] == '.' && (tmpStr[1] == '/' || tmpStr[1] == '\0') ) - { - continue; - } - // Empty - if(tmpStr[0] == '/' || tmpStr[0] == '\0') - { - continue; - } - - // Set New Position - pathComps[iPos2] = tmpStr; - iPos2++; - } - pathComps[iPos2] = NULL; - - // Build New Path - iPos2 = chrootLen + 1; iPos = 0; - ret[0] = '/'; - while(pathComps[iPos]) - { - tmpStr = pathComps[iPos]; - while(*tmpStr && *tmpStr != '/') - { - ret[iPos2++] = *tmpStr; - tmpStr++; - } - ret[iPos2++] = '/'; - iPos++; - } - if(iPos2 > 1) - ret[iPos2-1] = 0; - else - ret[iPos2] = 0; - - // Prepend the chroot - if(chrootLen) - memcpy( ret, chroot, chrootLen ); - - LEAVE('s', ret); -// Log_Debug("VFS", "VFS_GetAbsPath: RETURN '%s'", ret); - return ret; -} - -/** - * \fn char *VFS_ParsePath(const char *Path, char **TruePath) - * \brief Parses a path, resolving sysmlinks and applying permissions - */ -tVFS_Node *VFS_ParsePath(const char *Path, char **TruePath, tVFS_Mount **MountPoint) -{ - tVFS_Mount *mnt, *longestMount; - int cmp, retLength = 0; - int ofs, nextSlash; - int iNestedLinks = 0; - tVFS_Node *curNode, *tmpNode; - char *tmp; - char path_buffer[MAX_PATH_LEN+1]; - - ENTER("sPath pTruePath", Path, TruePath); - - // HACK: Memory File - if(Threads_GetUID() == 0 && Path[0] == '$') { - if(TruePath) { - *TruePath = malloc(strlen(Path)+1); - strcpy(*TruePath, Path); - } - curNode = VFS_MemFile_Create(Path); - if(MountPoint) { - *MountPoint = NULL; - } - LEAVE('p', curNode); - return curNode; - } - -restart_parse: - // For root we always fast return - if(Path[0] == '/' && Path[1] == '\0') { - if(TruePath) { - *TruePath = malloc( gVFS_RootMount->MountPointLen+1 ); - strcpy(*TruePath, gVFS_RootMount->MountPoint); - } - if(MountPoint) *MountPoint = gVFS_RootMount; - LEAVE('p', gVFS_RootMount->RootNode); - return gVFS_RootMount->RootNode; - } - - // Check if there is anything mounted - if(!gVFS_Mounts) { - Log_Error("VFS", "VFS_ParsePath - No filesystems mounted"); - return NULL; - } - - // Find Mountpoint - longestMount = gVFS_RootMount; - for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next) - { - // Quick Check - if( Path[mnt->MountPointLen] != '/' && Path[mnt->MountPointLen] != '\0') - continue; - // Length Check - If the length is smaller than the longest match sofar - if(mnt->MountPointLen < longestMount->MountPointLen) continue; - // String Compare - cmp = strcmp(Path, mnt->MountPoint); - - #if OPEN_MOUNT_ROOT - // Fast Break - Request Mount Root - if(cmp == 0) { - if(TruePath) { - *TruePath = malloc( mnt->MountPointLen+1 ); - strcpy(*TruePath, mnt->MountPoint); - } - if(MountPoint) - *MountPoint = mnt; - LEAVE('p', mnt->RootNode); - return mnt->RootNode; - } - #endif - // Not a match, continue - if(cmp != '/') continue; - longestMount = mnt; - } - - // Save to shorter variable - mnt = longestMount; - - LOG("mnt = {MountPoint:\"%s\"}", mnt->MountPoint); - - // Initialise String - if(TruePath) - { - // Assumes that the resultant path (here) will not be > strlen(Path) + 1 - *TruePath = malloc( strlen(Path) + 1 ); - strcpy(*TruePath, mnt->MountPoint); - retLength = mnt->MountPointLen; - } - - curNode = mnt->RootNode; - curNode->ReferenceCount ++; - // Parse Path - ofs = mnt->MountPointLen+1; - for(; (nextSlash = strpos(&Path[ofs], '/')) != -1; ofs += nextSlash + 1) - { - char pathEle[nextSlash+1]; - - // Empty String - if(nextSlash == 0) continue; - - memcpy(pathEle, &Path[ofs], nextSlash); - pathEle[nextSlash] = 0; - - // Check permissions on root of filesystem - if( !VFS_CheckACL(curNode, VFS_PERM_EXECUTE) ) { - //Log("Permissions fail on '%s'", Path); - goto _error; - } - - // Check if the node has a FindDir method - if( !curNode->Type->FindDir ) - { - //Log("FindDir fail on '%s'", Path); - goto _error; - } - LOG("FindDir{=%p}(%p, '%s')", curNode->Type->FindDir, curNode, pathEle); - // Get Child Node - tmpNode = curNode->Type->FindDir(curNode, pathEle); - LOG("tmpNode = %p", tmpNode); - _CloseNode( curNode ); - curNode = tmpNode; - - // Error Check - if(!curNode) { - LOG("Node '%s' not found in dir '%s'", pathEle, Path); - goto _error; - } - - // Handle Symbolic Links - if(curNode->Flags & VFS_FFLAG_SYMLINK) { - if(TruePath) { - free(*TruePath); - *TruePath = NULL; - } - if(!curNode->Type || !curNode->Type->Read) { - Log_Warning("VFS", "VFS_ParsePath - Read of symlink node %p'%s' is NULL", - curNode, Path); - goto _error; - } - - if(iNestedLinks > MAX_NESTED_LINKS) { - Log_Notice("VFS", "VFS_ParsePath - Nested link limit exceeded"); - goto _error; - } - - // Parse Symlink Path - // - Just update the path variable and restart the function - // > Count nested symlinks and limit to some value (counteracts loops) - { - int remlen = strlen(Path) - (ofs + nextSlash); - if( curNode->Size + remlen > MAX_PATH_LEN ) { - Log_Warning("VFS", "VFS_ParsePath - Symlinked path too long"); - goto _error; - } - curNode->Type->Read( curNode, 0, curNode->Size, path_buffer ); - path_buffer[ curNode->Size ] = '\0'; - LOG("path_buffer = '%s'", path_buffer); - strcat(path_buffer, Path + ofs+nextSlash); - - Path = path_buffer; -// Log_Debug("VFS", "VFS_ParsePath: Symlink translated to '%s'", Path); - iNestedLinks ++; - } - - // EVIL: Goto :) - LOG("Symlink -> '%s', restart", Path); - goto restart_parse; - } - - // Handle Non-Directories - if( !(curNode->Flags & VFS_FFLAG_DIRECTORY) ) - { - Log_Warning("VFS", "VFS_ParsePath - Path segment is not a directory"); - goto _error; - } - - // Check if path needs extending - if(!TruePath) continue; - - // Increase buffer space - tmp = realloc( *TruePath, retLength + strlen(pathEle) + 1 + 1 ); - // Check if allocation succeeded - if(!tmp) { - Log_Warning("VFS", "VFS_ParsePath - Unable to reallocate true path buffer"); - goto _error; - } - *TruePath = tmp; - // Append to path - (*TruePath)[retLength] = '/'; - strcpy(*TruePath+retLength+1, pathEle); - - LOG("*TruePath = '%s'", *TruePath); - - // - Extend Path - retLength += nextSlash + 1; - } - - // Check final finddir call - if( !curNode->Type || !curNode->Type->FindDir ) { - Log_Warning("VFS", "VFS_ParsePath - FindDir doesn't exist for element of '%s'", Path); - goto _error; - } - - // Get last node - LOG("FindDir(%p, '%s')", curNode, &Path[ofs]); - tmpNode = curNode->Type->FindDir(curNode, &Path[ofs]); - LOG("tmpNode = %p", tmpNode); - // Check if file was found - if(!tmpNode) { - LOG("Node '%s' not found in dir '%s'", &Path[ofs], Path); - goto _error; - } - _CloseNode( curNode ); - - if(TruePath) - { - // Increase buffer space - tmp = realloc(*TruePath, retLength + strlen(&Path[ofs]) + 1 + 1); - // Check if allocation succeeded - if(!tmp) { - Log_Warning("VFS", "VFS_ParsePath - Unable to reallocate true path buffer"); - goto _error; - } - *TruePath = tmp; - // Append to path - (*TruePath)[retLength] = '/'; - strcpy(*TruePath + retLength + 1, &Path[ofs]); - // - Extend Path - //retLength += strlen(tmpNode->Name) + 1; - } - - if( MountPoint ) { - *MountPoint = mnt; - } - - LEAVE('p', tmpNode); - return tmpNode; - -_error: - _CloseNode( curNode ); - - if(TruePath && *TruePath) { - free(*TruePath); - *TruePath = NULL; - } - LEAVE('n'); - return NULL; -} - -/** - * \brief Create and return a handle number for the given node and mode - */ -int VFS_int_CreateHandle( tVFS_Node *Node, tVFS_Mount *Mount, int Mode ) -{ - int i; - - ENTER("pNode pMount xMode", Node, Mount, Mode); - - i = 0; - i |= (Mode & VFS_OPENFLAG_EXEC) ? VFS_PERM_EXECUTE : 0; - i |= (Mode & VFS_OPENFLAG_READ) ? VFS_PERM_READ : 0; - i |= (Mode & VFS_OPENFLAG_WRITE) ? VFS_PERM_WRITE : 0; - - LOG("i = 0b%b", i); - - // Permissions Check - if( !VFS_CheckACL(Node, i) ) { - _CloseNode( Node ); - Log_Log("VFS", "VFS_int_CreateHandle: Permissions Failed"); - errno = EACCES; - LEAVE_RET('i', -1); - } - - i = VFS_AllocHandle( !!(Mode & VFS_OPENFLAG_USER), Node, Mode ); - if( i < 0 ) { - Log_Notice("VFS", "VFS_int_CreateHandle: Out of handles"); - errno = ENFILE; - LEAVE_RET('i', -1); - } - - VFS_GetHandle(i)->Mount = Mount; - - LEAVE_RET('x', i); -} - -/** - * \fn int VFS_Open(const char *Path, Uint Mode) - * \brief Open a file - */ -int VFS_Open(const char *Path, Uint Flags) -{ - return VFS_OpenEx(Path, Flags, 0); -} - -int VFS_OpenEx(const char *Path, Uint Flags, Uint Mode) -{ - tVFS_Node *node; - tVFS_Mount *mnt; - char *absPath; - - ENTER("sPath xFlags oMode", Path, Flags); - - // Get absolute path - absPath = VFS_GetAbsPath(Path); - if(absPath == NULL) { - Log_Warning("VFS", "VFS_Open: Path expansion failed '%s'", Path); - LEAVE_RET('i', -1); - } - LOG("absPath = \"%s\"", absPath); - - // Parse path and get mount point - node = VFS_ParsePath(absPath, NULL, &mnt); - - // Create file if requested and it doesn't exist - if( !node && (Flags & VFS_OPENFLAG_CREATE) ) - { - // TODO: Translate `Mode` into ACL and node flags - // Get parent, create node - VFS_MkNod(absPath, 0); - node = VFS_ParsePath(absPath, NULL, &mnt); - } - - // Free generated path - free(absPath); - - // Check for error - if(!node) - { - LOG("Cannot find node"); - errno = ENOENT; - LEAVE_RET('i', -1); - } - - // Check for symlinks - if( !(Flags & VFS_OPENFLAG_NOLINK) && (node->Flags & VFS_FFLAG_SYMLINK) ) - { - char tmppath[node->Size+1]; - if( node->Size > MAX_PATH_LEN ) { - Log_Warning("VFS", "VFS_Open - Symlink is too long (%i)", node->Size); - LEAVE_RET('i', -1); - } - if( !node->Type || !node->Type->Read ) { - Log_Warning("VFS", "VFS_Open - No read method on symlink"); - LEAVE_RET('i', -1); - } - // Read symlink's path - node->Type->Read( node, 0, node->Size, tmppath ); - tmppath[ node->Size ] = '\0'; - _CloseNode( node ); - // Open the target - node = VFS_ParsePath(tmppath, NULL, &mnt); - if(!node) { - LOG("Cannot find symlink target node (%s)", tmppath); - errno = ENOENT; - LEAVE_RET('i', -1); - } - } - - LEAVE_RET('x', VFS_int_CreateHandle(node, mnt, Flags)); -} - - -/** - * \brief Open a file from an open directory - */ -int VFS_OpenChild(int FD, const char *Name, Uint Mode) -{ - tVFS_Handle *h; - tVFS_Node *node; - - ENTER("xFD sName xMode", FD, Name, Mode); - - // Get handle - h = VFS_GetHandle(FD); - if(h == NULL) { - Log_Warning("VFS", "VFS_OpenChild - Invalid file handle 0x%x", FD); - errno = EINVAL; - LEAVE_RET('i', -1); - } - - // Check for directory - if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) { - Log_Warning("VFS", "VFS_OpenChild - Passed handle is not a directory"); - errno = ENOTDIR; - LEAVE_RET('i', -1); - } - - // Sanity check - if( !h->Node->Type || !h->Node->Type->FindDir ) { - Log_Error("VFS", "VFS_OpenChild - Node does not have a type/is missing FindDir"); - errno = ENOTDIR; - LEAVE_RET('i', -1); - } - - // Find Child - node = h->Node->Type->FindDir(h->Node, Name); - if(!node) { - errno = ENOENT; - LEAVE_RET('i', -1); - } - - LEAVE_RET('x', VFS_int_CreateHandle(node, h->Mount, Mode)); -} - -int VFS_OpenInode(Uint32 Mount, Uint64 Inode, int Mode) -{ - tVFS_Mount *mnt; - tVFS_Node *node; - - ENTER("iMount XInode xMode", Mount, Inode, Mode); - - // Get mount point - mnt = VFS_GetMountByIdent(Mount); - if( !mnt ) { - LOG("Mount point ident invalid"); - errno = ENOENT; - LEAVE_RET('i', -1); - } - - // Does the filesystem support this? - if( !mnt->Filesystem->GetNodeFromINode ) { - LOG("Filesystem does not support inode accesses"); - errno = ENOENT; - LEAVE_RET('i', -1); - } - - // Get node - node = mnt->Filesystem->GetNodeFromINode(mnt->RootNode, Inode); - if( !node ) { - LOG("Unable to find inode"); - errno = ENOENT; - LEAVE_RET('i', -1); - } - - LEAVE_RET('x', VFS_int_CreateHandle(node, mnt, Mode)); -} - -/** - * \fn void VFS_Close(int FD) - * \brief Closes an open file handle - */ -void VFS_Close(int FD) -{ - tVFS_Handle *h; - - // Get handle - h = VFS_GetHandle(FD); - if(h == NULL) { - Log_Warning("VFS", "Invalid file handle passed to VFS_Close, 0x%x", FD); - return; - } - - #if VALIDATE_VFS_FUNCTIPONS - if(h->Node->Close && !MM_GetPhysAddr(h->Node->Close)) { - Log_Warning("VFS", "Node %p's ->Close method is invalid (%p)", - h->Node, h->Node->Close); - return ; - } - #endif - - _CloseNode(h->Node); - - h->Node = NULL; -} - -/** - * \brief Change current working directory - */ -int VFS_ChDir(const char *Dest) -{ - char *buf; - int fd; - tVFS_Handle *h; - - // Create Absolute - buf = VFS_GetAbsPath(Dest); - if(buf == NULL) { - Log_Notice("VFS", "VFS_ChDir: Path expansion failed"); - return -1; - } - - // Check if path exists - fd = VFS_Open(buf, VFS_OPENFLAG_EXEC); - if(fd == -1) { - Log_Notice("VFS", "VFS_ChDir: Path is invalid"); - return -1; - } - - // Get node so we can check for directory - h = VFS_GetHandle(fd); - if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) { - Log("VFS_ChDir: Path is not a directory"); - VFS_Close(fd); - return -1; - } - - // Close file - VFS_Close(fd); - - { - char **cwdptr = Threads_GetCWD(); - // Free old working directory - if( *cwdptr ) free( *cwdptr ); - // Set new - *cwdptr = buf; - } - - Log("Updated CWD to '%s'", buf); - - return 1; -} - -/** - * \fn int VFS_ChRoot(char *New) - * \brief Change current root directory - */ -int VFS_ChRoot(const char *New) -{ - char *buf; - int fd; - tVFS_Handle *h; - - if(New[0] == '/' && New[1] == '\0') - return 1; // What a useless thing to ask! - - // Create Absolute - buf = VFS_GetAbsPath(New); - if(buf == NULL) { - LOG("Path expansion failed"); - return -1; - } - - // Check if path exists - fd = VFS_Open(buf, VFS_OPENFLAG_EXEC); - if(fd == -1) { - LOG("Path is invalid"); - return -1; - } - - // Get node so we can check for directory - h = VFS_GetHandle(fd); - if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) { - LOG("Path is not a directory"); - VFS_Close(fd); - return -1; - } - - // Close file - VFS_Close(fd); - - // Update - { - char **chroot_ptr = Threads_GetChroot(); - if( *chroot_ptr ) free( *chroot_ptr ); - *chroot_ptr = buf; - } - - LOG("Updated Root to '%s'", buf); - - return 1; -} - -// === EXPORTS === -EXPORT(VFS_Open); -EXPORT(VFS_Close); diff --git a/Kernel/vfs/select.c b/Kernel/vfs/select.c deleted file mode 100644 index 439afc02..00000000 --- a/Kernel/vfs/select.c +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Acess2 VFS - * - By thePowersGang (John Hodge) - * - * select.c - * - Implements the select() system call (and supporting code) - * - * TODO: Implment timeouts (via an alarm event?) - * TODO: Remove malloc for read/write queues - */ -#define DEBUG 0 -#include -#include "vfs.h" -#include "vfs_int.h" -#include "vfs_ext.h" -#include -#include -#include - -// === CONSTANTS === -#define NUM_THREADS_PER_ALLOC 4 - -// === IMPORTS === -extern tThread *Proc_GetCurThread(void); - -// === TYPES === -typedef struct sVFS_SelectListEnt tVFS_SelectListEnt; - -// === STRUCTURES === -struct sVFS_SelectListEnt -{ - tVFS_SelectListEnt *Next; - tThread *Threads[NUM_THREADS_PER_ALLOC]; -}; - -// NOTE: Typedef is in vfs.h -struct sVFS_SelectList -{ - tMutex Lock; - tVFS_SelectListEnt FirstEnt; -}; - -// === PROTOTYPES === -// int VFS_SelectNode(tVFS_Node *Node, enum eVFS_SelectTypes Type, tTime *Timeout); -// int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel); -// int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull); -// int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable); -// int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState); - int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed); - int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel); - int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel); - int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed); -void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread); -void VFS_int_Select_SignalAll(tVFS_SelectList *List); - -// === GLOBALS === - -// === FUNCTIONS === -int VFS_SelectNode(tVFS_Node *Node, int TypeFlags, tTime *Timeout, const char *Name) -{ - tThread *thisthread = Proc_GetCurThread(); - int ret, type; - - ENTER("pNode iTypeFlags pTimeout sName", Node, TypeFlags, Timeout, Name); - - // Initialise - for( type = 0; type < 3; type ++ ) - { - tVFS_SelectList **list; - int *flag, wanted, maxAllowed; - if( !(TypeFlags & (1 << type)) ) continue; - if( VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed) ) { - LEAVE('i', -1); - return -1; - } - - // Alloc if needed - if( !*list ) *list = calloc(1, sizeof(tVFS_SelectList)); - - VFS_int_Select_AddThread(*list, thisthread, maxAllowed); - if( *flag == wanted ) - { - VFS_int_Select_RemThread(*list, thisthread); - LEAVE('i', 1); - return 1; - } - } - - // - Fast return for polling - if( Timeout && *Timeout == 0 ) return 0; - - // Wait for things - if( !Timeout || *Timeout > 0 ) - { - LOG("Semaphore_Wait()"); - // TODO: Actual timeout - Threads_WaitEvents( THREAD_EVENT_VFS ); - } - - // Get return value - ret = 0; - for( type = 0; type < 3; type ++ ) - { - tVFS_SelectList **list; - int *flag, wanted, maxAllowed; - if( !(TypeFlags & (1 << type)) ) continue; - VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed); - LOG("VFS_int_Select_RemThread()"); - VFS_int_Select_RemThread(*list, thisthread); - ret = ret || *flag == wanted; - } - - LEAVE('i', ret); - return ret; -} - -int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel) -{ - tThread *thisthread = Proc_GetCurThread(); - int ret; - - ENTER("iMaxHandle pReadHandles pWriteHandles pErrHandles pTimeout bIsKernel", - MaxHandle, ReadHandles, WriteHandles, ErrHandles, Timeout, IsKernel); - - // Notes: The idea is to make sure we only enter wait (Threads_WaitEvents) - // if we are going to be woken up (either by an event at a later time, - // or by an event that happened while or before we were registering). - // Hence, register must happen _before_ we check the state flag - // (See VFS_int_Select_Register), that way either we pick up the flag, - // or the semaphore is incremeneted (or both, but never none) - - // Register with nodes - ret = VFS_int_Select_Register(thisthread, MaxHandle, ReadHandles, 0, IsKernel); - ret += VFS_int_Select_Register(thisthread, MaxHandle, WriteHandles, 1, IsKernel); - ret += VFS_int_Select_Register(thisthread, MaxHandle, ErrHandles, 2, IsKernel); - - LOG("Register ret = %i", ret); - - // If there were events waiting, de-register and return - if( ret > 0 ) - { - ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel); - ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel); - ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel); - LEAVE('i', ret); - return ret; - } - - // TODO: Implement timeout - LOG("Timeout = %p", Timeout); - - // Wait (only if there is no timeout, or it is greater than zero - if( !Timeout || *Timeout > 0 ) - { - // TODO: Timeout - // TODO: Allow extra events to be waited upon - Threads_WaitEvents( THREAD_EVENT_VFS|ExtraEvents ); - } - - // Fill output (modify *Handles) - // - Also, de-register - ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel); - ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel); - ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel); - LEAVE('i', ret); - return ret; -} - -// Mark a node as having data ready for reading -int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable) -{ - ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable); - Node->DataAvaliable = !!IsDataAvaliable; - if( Node->DataAvaliable ) - VFS_int_Select_SignalAll(Node->ReadThreads); - LEAVE('i', 0); - return 0; -} - -// Mark a node as having a full buffer -int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull) -{ - ENTER("pNode bIsBufferFull", Node, IsBufferFull); - Node->BufferFull = !!IsBufferFull; - if( !Node->BufferFull ) - VFS_int_Select_SignalAll(Node->WriteThreads); - LEAVE('i', 0); - return 0; -} - -// Mark a node as errored -int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState) -{ - ENTER("pNode bIsErrorState", Node, IsErrorState); - Node->ErrorOccurred = !!IsErrorState; - if( Node->ErrorOccurred ) - VFS_int_Select_SignalAll(Node->ErrorThreads); - LEAVE('i', 0); - return 0; -} - -// --- Internal --- -int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed) -{ - // Get the type of the listen - switch(Type) - { - case 0: // Read - if(List) *List = &Node->ReadThreads; - if(Flag) *Flag = &Node->DataAvaliable; - if(WantedFlag) *WantedFlag = 1; - if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for read - break; - case 1: // Write - if(List) *List = &Node->WriteThreads; - if(Flag) *Flag = &Node->BufferFull; - if(WantedFlag) *WantedFlag = 0; - if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for write - break; - case 2: // Error - if(List) *List = &Node->ErrorThreads; - if(Flag) *Flag = &Node->ErrorOccurred; - if(WantedFlag) *WantedFlag = 1; - if(MaxAllowed) *MaxAllowed = -1; // No max for error listeners - break; - default: - Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type); - return 1; - } - return 0; -} - -/** - * \return Number of files with an action - */ -int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel) -{ - int i, numFlagged = 0; - tVFS_SelectList **list; - int *flag, wantedFlagValue; - int maxAllowed; - - if( !Handles ) return 0; - - ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel); - - for( i = 0; i < MaxHandle; i ++ ) - { - tVFS_Handle *handle; - - // Is the descriptor set - if( !FD_ISSET(i, Handles) ) continue; - LOG("FD #%i", i); - - handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) ); - // Is the handle valid? - if( !handle || !handle->Node ) - { - if( Type == 2 ) { // Bad FD counts as an error - numFlagged ++; - } - else { - FD_CLR(i, Handles); - } - continue; - } - - // Get the type of the listen - if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) { - LEAVE('i', 0); - return 0; - } - - // Alloc if needed - if( !*list ) { - *list = calloc(1, sizeof(tVFS_SelectList)); - } - - // Register - if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) ) - { - // Oops, error (or just no space) - FD_CLR(i, Handles); - } - - // Check for the flag - if( !!*flag == !!wantedFlagValue ) - numFlagged ++; - } - - LEAVE('i', numFlagged); - - return numFlagged; -} -/** - * \return Number of files with an action - */ -int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel) -{ - int i, numFlagged = 0; - tVFS_SelectList **list; - int *flag, wantedFlagValue; - - if( !Handles ) return 0; - - ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel); - - for( i = 0; i < MaxHandle; i ++ ) - { - tVFS_Handle *handle; - - // Is the descriptor set - if( !FD_ISSET(i, Handles) ) continue; - LOG("FD #%i", i); - - handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) ); - // Is the handle valid? - if( !handle || !handle->Node ) - { - if( Type == 2 ) { // Bad FD counts as an error - numFlagged ++; - } - else { - FD_CLR(i, Handles); - } - continue; - } - - // Get the type of the listen - - // Get the type of the listen - if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) { - LEAVE('i', 0); - return 0; - } - - // Remove - VFS_int_Select_RemThread(*list, Thread ); - - // Check for the flag - if( !!*flag == !!wantedFlagValue ) { - numFlagged ++; - } - else { - FD_CLR(i, Handles); - } - } - - LEAVE('i', numFlagged); - - return numFlagged; -} - -/** - * \return Boolean failure - */ -int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed) -{ - int i, count = 0; - tVFS_SelectListEnt *block, *prev; - - ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed); - - // Lock to avoid concurrency issues - Mutex_Acquire(&List->Lock); - - block = &List->FirstEnt; - - // Look for free space - do - { - for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ ) - { - if( block->Threads[i] == NULL ) - { - block->Threads[i] = Thread; - Mutex_Release(&List->Lock); - LEAVE('i', 0); - return 0; - } - count ++; - if( MaxAllowed && count >= MaxAllowed ) { - LEAVE('i', 1); - return 1; - } - } - - prev = block; - block = block->Next; - } while(block); - - LOG("New block"); - - // Create new block - block = malloc( sizeof(tVFS_SelectListEnt) ); - if( !block ) { - Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed"); - Mutex_Release(&List->Lock); - return -1; - } - block->Next = NULL; - block->Threads[0] = Thread; - for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ ) - { - block->Threads[i] = NULL; - } - - // Add to list - prev->Next = block; - - // Release - Mutex_Release(&List->Lock); - - LEAVE('i', 0); - return 0; -} - -void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread) -{ - int i; - tVFS_SelectListEnt *block, *prev = NULL; - - ENTER("pList pThread", List, Thread); - - // Lock to avoid concurrency issues - Mutex_Acquire(&List->Lock); - - block = &List->FirstEnt; - - // Look for the thread - do - { - for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ ) - { - if( block->Threads[i] == Thread ) - { - block->Threads[i] = NULL; - - // Check if this block is empty - if( block != &List->FirstEnt ) - { - for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ ) - if( block->Threads[i] ) - break; - // If empty, free it - if( i == NUM_THREADS_PER_ALLOC ) { - LOG("Deleting block"); - prev->Next = block->Next; - free(block); - } - //TODO: If not empty, check if it can be merged downwards - } - - Mutex_Release(&List->Lock); - LEAVE('-'); - return ; - } - } - - prev = block; - block = block->Next; - } while(block); - - // Not on list, is this an error? - - Mutex_Release(&List->Lock); - - LOG("Not on list"); - LEAVE('-'); -} - -/** - * \brief Signal all threads on a list - */ -void VFS_int_Select_SignalAll(tVFS_SelectList *List) -{ - int i; - tVFS_SelectListEnt *block; - - if( !List ) return ; - - ENTER("pList", List); - - // Lock to avoid concurrency issues - Mutex_Acquire(&List->Lock); - - block = &List->FirstEnt; - - // Look for the thread - do - { - for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ ) - { - if( block->Threads[i] ) - { - LOG("block(%p)->Threads[%i] = %p", block, i, block->Threads[i]); - Threads_PostEvent( block->Threads[i], THREAD_EVENT_VFS ); - } - } - - block = block->Next; - } while(block); - - Mutex_Release(&List->Lock); - - LEAVE('-'); -} diff --git a/Kernel/workqueue.c b/Kernel/workqueue.c deleted file mode 100644 index b1a63851..00000000 --- a/Kernel/workqueue.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Acess2 Kernel - * - By John Hodge (thePowersGang) - * - * workqueue.c - * - Worker FIFO Queue (Single Consumer, Interrupt Producer) - */ -#include -#include -#include - -// === CODE === -void Workqueue_Init(tWorkqueue *Queue, const char *Name, size_t NextOfset) -{ - Queue->Name = Name; - Queue->NextOffset = NextOfset; -} - -void *Workqueue_GetWork(tWorkqueue *Queue) -{ - tThread *us; - - for( ;; ) - { - // Check for work - SHORTLOCK(&Queue->Protector); - if(Queue->Head) - { - void *ret = Queue->Head; - Queue->Head = *( (void**)ret + Queue->NextOffset/sizeof(void*) ); - if(Queue->Tail == ret) - Queue->Tail = NULL; - SHORTREL(&Queue->Protector); - return ret; - } - - // Go to sleep - SHORTLOCK(&glThreadListLock); - us = Threads_RemActive(); - us->WaitPointer = Queue; - us->Status = THREAD_STAT_QUEUESLEEP; - Queue->Sleeper = us; - SHORTREL(&Queue->Protector); - SHORTREL(&glThreadListLock); - - // Yield and sleep - Threads_Yield(); - if(us->Status == THREAD_STAT_QUEUESLEEP) { - // Why are we awake?! - } - - us->WaitPointer = NULL; - } -} - -void Workqueue_AddWork(tWorkqueue *Queue, void *Ptr) -{ - SHORTLOCK(&Queue->Protector); - - if( Queue->Tail ) - *( (void**)Queue->Tail + Queue->NextOffset/sizeof(void*) ) = Ptr; - else - Queue->Head = Ptr; - Queue->Tail = Ptr; - - if( Queue->Sleeper ) - { - if( Queue->Sleeper->Status != THREAD_STAT_ACTIVE ) - Threads_AddActive(Queue->Sleeper); - Queue->Sleeper = NULL; - } - SHORTREL(&Queue->Protector); -} diff --git a/KernelLand/Kernel/.gitignore b/KernelLand/Kernel/.gitignore new file mode 100644 index 00000000..439ce3e7 --- /dev/null +++ b/KernelLand/Kernel/.gitignore @@ -0,0 +1 @@ +*.BuildNum.* diff --git a/KernelLand/Kernel/Doxyfile b/KernelLand/Kernel/Doxyfile new file mode 100644 index 00000000..a474dd56 --- /dev/null +++ b/KernelLand/Kernel/Doxyfile @@ -0,0 +1,1510 @@ +# Doxyfile 1.5.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Acess2 + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ../SrcDoc/Kernel + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, +# Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = YES + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.c *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = arch/archdoc.h + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to FRAME, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. Other possible values +# for this tag are: HIERARCHIES, which will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list; +# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which +# disables this behavior completely. For backwards compatibility with previous +# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE +# respectively. + +GENERATE_TREEVIEW = NONE + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Options related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/KernelLand/Kernel/Doxyfile.api b/KernelLand/Kernel/Doxyfile.api new file mode 100644 index 00000000..6872e55f --- /dev/null +++ b/KernelLand/Kernel/Doxyfile.api @@ -0,0 +1,1775 @@ +# Doxyfile 1.7.5.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = Acess2 + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ../APIDoc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +#INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = YES + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. + +#CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = include/apidoc_mainpage.h \ + include/acess.h \ + include/hal_proc.h \ + include/binary.h \ + include/modules.h \ + include/vfs.h \ + include/vfs_ext.h \ + include/fs_devfs.h \ + include/fs_sysfs.h \ + include/iocache.h \ + arch/archdoc.h \ + include/apidoc/arch_x86.h \ + include/api_drv_common.h \ + include/api_drv_video.h \ + include/api_drv_terminal.h \ + include/api_drv_disk.h \ + include/api_drv_keyboard.h \ + include/api_drv_joystick.h \ + include/api_drv_network.h + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = api_drv_*.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to directory from which doxygen is run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is adviced to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NONE + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +#MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = NO + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +#LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +#INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/KernelLand/Kernel/DoxygenLayout.xml b/KernelLand/Kernel/DoxygenLayout.xml new file mode 100644 index 00000000..e8faa6d8 --- /dev/null +++ b/KernelLand/Kernel/DoxygenLayout.xml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/KernelLand/Kernel/GenSyscalls.php b/KernelLand/Kernel/GenSyscalls.php new file mode 100644 index 00000000..d451d1dd --- /dev/null +++ b/KernelLand/Kernel/GenSyscalls.php @@ -0,0 +1,76 @@ +$call) +{ + if($i != $num) { + $lHeader .= "\n"; + $lAsmInc .= "\n"; + } + + $lHeader .= "#define {$call[0]}\t{$num}"; + $lHeader .= "\t// {$num} - {$call[1]}\n"; + + $lAsmInc .= "%define {$call[0]}\t{$num}\t; {$call[1]}\n"; + + + if($i != $num) + $i = $num+1; + else + $i ++; +} +$lHeader .= "#define NUM_SYSCALLS\t$i\n"; +$lHeader .= "#define SYS_DEBUG\t0x100 // 0x100 - Print a debug string\n"; +$lHeader .= "\n"; +$lHeader .= "#ifdef __GNUC__\n"; +$lHeader .= "static const char *cSYSCALL_NAMES[] = {\n\t"; + +$j = 0; +for($i=0;$i<$lMax;$i++) +{ + if(!isset($lSyscalls[$i])) + $lHeader .= "\"\","; + else + $lHeader .= "\"".$lSyscalls[$i][0]."\","; + $j ++; + if($j == 6) { + $lHeader .= "\n\t"; + $j = 0; + } +} +$lHeader .= "\"\"\n};\n" +$lHeader .= "#endif\n"; +$lHeader .= "#endif\n"; + +$fp = fopen("include/syscalls.h", "w"); fwrite($fp, $lHeader); fclose($fp); +$fp = fopen("include/syscalls.inc.asm", "w"); fwrite($fp, $lAsmInc); fclose($fp); + +?> diff --git a/KernelLand/Kernel/GenSyscalls.pl b/KernelLand/Kernel/GenSyscalls.pl new file mode 100755 index 00000000..487ceca4 --- /dev/null +++ b/KernelLand/Kernel/GenSyscalls.pl @@ -0,0 +1,85 @@ +#!/usr/bin/perl +# +# + +open(FILE, "syscalls.lst"); + +$num = 0; +@calls = (); +while($_ = ) +{ + if(/(\d+)/) + { + $num = $1; + } + elsif(/([A-Z_]+)\s+(.+)/) + { + push @calls, [$num, $1, $2]; + $num ++; + } +} + +close(FILE); + +# C header +open(HEADER, ">include/syscalls.h"); +print HEADER "/* + * Acess2 + * syscalls.h + * - System Call List + * + * NOTE: Generated from Kernel/syscalls.lst + */ +#ifndef _SYSCALLS_H +#define _SYSCALLS_H + +"; + +$lastid = -1; +$i = 0; +foreach my $call (@calls) +{ + print HEADER "#define ", $call->[1], "\t", $call->[0], "\t// ", $call->[2], "\n"; + $i = $call->[0] + 1; +} +print HEADER " +#define NUM_SYSCALLS ",$i," +#define SYS_DEBUG 0x100 + +#ifndef __ASSEMBLER__ +static const char *cSYSCALL_NAMES[] = { +"; + +$lastid = -1; +foreach $call (@calls) +{ + while( $lastid + 1 < $call->[0] ) + { + print HEADER "\t\"\",\n"; + $lastid = $lastid + 1; + } + print HEADER "\t\"", $call->[1], "\",\n"; + $lastid = $lastid + 1; +} +print HEADER " +\t\"\" +}; +#endif + +#endif +"; + +close(HEADER); + +# Assembly Header +open(ASM, ">include/syscalls.inc.asm"); +print ASM "; Acess2 +; System Calls List +; + +"; +foreach $call (@calls) +{ + print ASM "%define ", $call->[1], "\t", $call->[0], "\t ;", $call->[2], "\n"; +} +close(ASM); diff --git a/KernelLand/Kernel/Makefile b/KernelLand/Kernel/Makefile new file mode 100644 index 00000000..d0c8dbf6 --- /dev/null +++ b/KernelLand/Kernel/Makefile @@ -0,0 +1,138 @@ +# Acess2 Kernel +# Root Makefile +# NOTE: Does some voodoo to allow differing architecture builds to co-exist +# - The built objects and dependency files are suffixed with the arch name +# - The final binary is Acess2..bin + +-include ../Makefile.cfg + +-include arch/$(ARCHDIR)/Makefile + +-include Makefile.BuildNum.$(ARCH) + +ifeq ($(BUILD_NUM),) +BUILD_NUM = 0 +endif + +KERNEL_VERSION = $(ACESS_VERSION) +MAKEDEP = $(CC) -M + +ifeq ($(AS_SUFFIX),) + AS_SUFFIX = S +endif + +ASFLAGS += -D ARCHDIR_IS_$(ARCHDIR)=1 -D PLATFORM_is_$(PLATFORM)=1 +CPPFLAGS += -I./include -I./arch/$(ARCHDIR)/include -D_MODULE_NAME_=\"Kernel\" +CPPFLAGS += -D ARCH=$(ARCH) -D ARCHDIR=$(ARCHDIR) -D PLATFORM=\"$(PLATFORM)\" -D ARCHDIR_IS_$(ARCHDIR)=1 -D PLATFORM_is_$(PLATFORM)=1 +CPPFLAGS += -D KERNEL_VERSION=$(KERNEL_VERSION) +CFLAGS += -Wall -fno-stack-protector -Wstrict-prototypes -g +CFLAGS += -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wuninitialized +CFLAGS += -O3 +LDFLAGS += -T arch/$(ARCHDIR)/link.ld -g + +ifeq ($(PLATFORM),default) + OBJDIR := obj-$(ARCH)/ + #OBJSUFFIX := .$(ARCH) + BIN := ../Acess2.$(ARCH).bin + GZBIN := ../Acess2.$(ARCH).gz +else + OBJDIR := obj-$(ARCH)-$(PLATFORM)/ + #OBJSUFFIX := .$(ARCH)-$(PLATFORM) + BIN := ../Acess2.$(ARCH)-$(PLATFORM).bin + GZBIN := ../Acess2.$(ARCH)-$(PLATFORM).gz +endif + +ifeq ($(DEBUG_BUILD),yes) + LDFLAGS += -g + CFLAGS += -g +endif + +BUILDINFO_OBJ := $(OBJDIR)buildinfo.o$(OBJSUFFIX) +BUILDINFO_SRC := $(OBJDIR)buildinfo.c$(OBJSUFFIX) + +OBJ := $(addprefix arch/$(ARCHDIR)/,$(A_OBJ)) +OBJ += heap.o drvutil.o logging.o debug.o lib.o adt.o time.o +OBJ += messages.o modules.o syscalls.o system.o +OBJ += threads.o mutex.o semaphore.o workqueue.o events.o +OBJ += drv/proc.o drv/fifo.o drv/iocache.o drv/pci.o +OBJ += drv/vterm.o drv/vterm_font.o drv/vterm_vt100.o drv/vterm_output.o drv/vterm_input.o drv/vterm_termbuf.o +OBJ += binary.o bin/elf.o bin/pe.o +OBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/dir.o vfs/io.o vfs/mount.o +OBJ += vfs/memfile.o vfs/nodecache.o vfs/handle.o vfs/select.o vfs/mmap.o +OBJ += vfs/fs/root.o vfs/fs/devfs.o +OBJ += $(addprefix drv/, $(addsuffix .o,$(DRIVERS))) + +OBJ := $(addsuffix $(OBJSUFFIX), $(OBJ)) +OBJ := $(addprefix $(OBJDIR), $(OBJ)) + +MODS += $(addprefix ../Modules/, $(addsuffix .xo.$(ARCH),$(MODULES))) + +DEPFILES := $(OBJ:%$(OBJSUFFIX)=%.dep$(OBJSUFFIX)) + +SRCFILES = $(OBJ:$(OBJDIR)%.o$(OBJSUFFIX)=%.c) +SRCFILES := $(SRCFILES:$(OBJDIR)%.ao$(OBJSUFFIX)=%.$(AS_SUFFIX)) + +OBJ += $(BUILDINFO_OBJ) + +.PHONY: all clean install apidoc + +all: $(BIN) + +clean: + @$(RM) $(BIN) ../Acess2.$(ARCH).gz $(BIN).dsm ../Map.$(ARCH).txt LineCounts.$(ARCH).txt + @$(RM) -r $(OBJDIR) $(OBJ) $(DEPFILES) $(BUILDINFO_SRC) + +install: $(BIN) + @cp $(BIN) $(BIN)_ + @$(STRIP) $(BIN)_ + @gzip -c $(BIN)_ > $(GZBIN) + @$(RM) $(BIN)_ + $(xCP) $(GZBIN) $(DISTROOT) + +apidoc: + doxygen Doxyfile.api + +$(BIN): $(OBJ) $(MODS) arch/$(ARCHDIR)/link.ld Makefile ../BuildConf/$(ARCH)/Makefile.cfg ../BuildConf/$(ARCH)/$(PLATFORM).mk + @echo --- LD -o $(BIN) + @$(LD) $(LDFLAGS) -o $(BIN) $(OBJ) $(MODS) --defsym __buildnum=$$(( $(BUILD_NUM) + 1 )) -Map ../Map.$(ARCH).txt + @$(DISASM) -S $(BIN) > $(BIN).dsm + @wc -l $(SRCFILES) include/*.h > LineCounts.$(ARCH).txt + @echo BUILD_NUM = $$(( $(BUILD_NUM) + 1 )) > Makefile.BuildNum.$(ARCH) + $(POSTBUILD) + +$(OBJDIR)%.ao$(OBJSUFFIX): %.$(AS_SUFFIX) Makefile + @echo --- AS -o $@ + @mkdir -p $(dir $@) + @$(AS) $(ASFLAGS) $< -o $@ +ifeq ($(AS_SUFFIX),S) + @$(MAKEDEP) $(CPPFLAGS) -MT $@ -o $(OBJDIR)$*.ao.dep$(OBJSUFFIX) $< +endif + +$(OBJDIR)%.o$(OBJSUFFIX): %.c Makefile +# if exists %*/Makefile +# @make -C %*/ all +# else + @echo --- CC -o $@ + @mkdir -p $(dir $@) + @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + @$(MAKEDEP) $(CPPFLAGS) -MT $@ -o $(OBJDIR)$*.o.dep$(OBJSUFFIX) $< +# endif + +%.xo.$(ARCH): + @BUILDTYPE=static make -C $* all + +include/syscalls.h include/syscalls.inc.asm: syscalls.lst Makefile GenSyscalls.pl + perl GenSyscalls.pl + +Makefile: ../Makefile.cfg arch/$(ARCHDIR)/Makefile + +$(BUILDINFO_SRC): $(filter-out $(BUILDINFO_OBJ), $(OBJ)) $(MODS) arch/$(ARCHDIR)/link.ld Makefile + @echo "#include " > $@ + @echo "const char gsGitHash[] = \""`git log -n 1 | head -n 1 | awk '{print $$2}'`"\";" >> $@ + @echo "const int giBuildNumber = $(BUILD_NUM);" >> $@ +$(BUILDINFO_OBJ): $(BUILDINFO_SRC) + @echo --- CC -o $@ + @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + +# Dependency Files +-include $(DEPFILES) diff --git a/KernelLand/Kernel/adt.c b/KernelLand/Kernel/adt.c new file mode 100644 index 00000000..ef2ae058 --- /dev/null +++ b/KernelLand/Kernel/adt.c @@ -0,0 +1,86 @@ +/* + * Acess2 Kernel + * + * adt.c + * - Complex data type code + */ +#include +#include + + +// === CODE === +// --- Ring Buffers --- +tRingBuffer *RingBuffer_Create(size_t Space) +{ + tRingBuffer *ret = malloc(sizeof(tRingBuffer)+Space); + ret->Start = 0; + ret->Length = 0; + ret->Space = Space; + return ret; +} + +size_t RingBuffer_Read(void *Dest, tRingBuffer *Buffer, size_t Length) +{ + size_t tmpLen; + + tmpLen = Buffer->Length; // Changed in Write, so cache it for our read + + if(Length > tmpLen) Length = tmpLen; + + if( Buffer->Start + Length > Buffer->Space ) + { + int endData = Buffer->Space - Buffer->Start; + memcpy(Dest, &Buffer->Data[Buffer->Start], endData); + memcpy((Uint8*)Dest + endData, Buffer->Data, Length - endData); + } + else + { + memcpy(Dest, &Buffer->Data[Buffer->Start], Length); + } + + // Lock then modify + SHORTLOCK( &Buffer->Lock ); + Buffer->Start += Length; + if( Buffer->Start > Buffer->Space ) + Buffer->Start -= Buffer->Space; + Buffer->Length -= Length; + SHORTREL( &Buffer->Lock ); + + return Length; +} + +size_t RingBuffer_Write(tRingBuffer *Buffer, const void *Source, size_t Length) +{ + size_t bufEnd, endSpace; + size_t tmpLen, tmpStart; + + // Cache Start and Length because _Read can change these + SHORTLOCK( &Buffer->Lock ); + tmpStart = Buffer->Start; + tmpLen = Buffer->Length; + SHORTREL( &Buffer->Lock ); + + bufEnd = (tmpStart + Buffer->Length) % Buffer->Space; + endSpace = Buffer->Space - bufEnd; + + // Force to bounds + if(Length > Buffer->Space - tmpLen) Length = Buffer->Space - tmpLen; + + if(endSpace < Length) + { + memcpy( &Buffer->Data[bufEnd], Source, endSpace ); + memcpy( Buffer->Data, (Uint8*)Source + endSpace, Length - endSpace ); + } + else + { + memcpy( &Buffer->Data[bufEnd], Source, Length ); + } + + // Lock then modify + SHORTLOCK( &Buffer->Lock ); + Buffer->Length += Length; + SHORTREL( &Buffer->Lock ); + + return Length; +} + diff --git a/KernelLand/Kernel/arch/archdoc.h b/KernelLand/Kernel/arch/archdoc.h new file mode 100644 index 00000000..ef3331f7 --- /dev/null +++ b/KernelLand/Kernel/arch/archdoc.h @@ -0,0 +1,124 @@ +/** + * \file archdoc.h + * \brief Documentation Definitions for the Acess 2 Architecture Interface + * \author John Hodge (thePowersGang) + * + * Acess 2 allows different architecture builds to be made off almost + * the same source tree. The difference between the trees is the inclusion + * of a different directory from the "Kernel/arch/" directory that + * contains the architecture specific intialisation and management code. + * Each achitecture tree must provide all the definitions from this + * document in some form or another (usually in the most efficient way + * avaliable) + * The values and types used in this documentation are a guide only and + * will most probably be incorrect for most architectures. + */ +#ifndef _ARCHDOC_H_ +#define _ARCHDOC_H_ + +/** + * \brief Maximum number of CPUs supported by this architecture driver + * (in the current build) + */ +#define MAX_CPUS 1 +/** + * \brief Number of bits in a machine word (Uint) + */ +#define BITS 32 +/** + * \brief Number of valid bits in a \a tPAddr + */ +#define PHYS_BITS 32 + +/** + * \name Syncronisation Primitives + * \{ + */ +/** + * \brief Spinlock type + */ +typedef volatile int tSpinlock; +/** + * \brief Acquire a spinlock + */ +#define LOCK(lockptr) do{while(*(tSpinlock*)lockptr)Threads_Yield();*(tSpinlock*)lockptr=1;}while(0) +/** + * \brief Release a held spinlock + */ +#define RELEASE(lockptr) do{*(tSpinlock*)lockptr=0;}while(0) +/** + * \} + */ +//#define HALT() __asm__ __volatile__ ("hlt") + + +/** + * \name Atomic Types + * \{ + */ +typedef unsigned int Uint; //!< Unsigned machine native integer +typedef unsigned char Uint8; //!< Unsigned 8-bit integer +typedef unsigned short Uint16; //!< Unsigned 16-bit integer +typedef unsigned long Uint32; //!< Unsigned 32-bit integer +typedef unsigned long long Uint64; //!< Unsigned 64-bit integer +typedef signed int Sint; //!< Signed Machine Native integer +typedef signed char Sint8; //!< Signed 8-bit integer +typedef signed short Sint16; //!< Signed 16-bit integer +typedef signed long Sint32; //!< Signed 32-bit integer +typedef signed long long Sint64; //!< Signed 16-bit integer +/** + * \} + */ + +typedef Uint size_t; //!< Counter/Byte count type +typedef Uint32 tVAddr; //!< Virtual address type +typedef Uint32 tPAddr; //!< Physical Address type + +/** + * \brief Register state passed to the syscall handler + * + * The \a tSyscallRegs structure allows the system call handler to read + * the user state to get the arguments for the call. It also allows the + * handler to alter specific parts of the user state to reflect the + * result of the call. + * \note The fields shown here are need only be present in the actual + * structure, in any order. The implementation may also add more + * fields for padding purposes. + */ +typedef struct { + Uint Arg4; //!< Fourth argument + Uint Arg3; //!< Third argument + Uint Arg2; //!< Second argument + union { + Uint Arg1; //!< First arugment + Uint RetHi; //!< High part of the return + }; + union { + Uint Num; //!< Call Number + Uint Return; //!< Low return value + }; + + Uint StackPointer; //!< User stack pointer +} tSyscallRegs; + +/** + * \brief Opaque structure definining the MMU state for a task + */ +typedef struct sMemoryState tMemoryState; + +/** + * \brief Opque structure defining the CPU state for a task + */ +typedef struct sTaskState tTaskState; + + +/** + * \name Memory Management + * \{ + */ +/** + * \} + */ + + +#endif diff --git a/KernelLand/Kernel/arch/armv7/Makefile b/KernelLand/Kernel/arch/armv7/Makefile new file mode 100644 index 00000000..fad1b55d --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/Makefile @@ -0,0 +1,20 @@ +# +# Acess2 Kernel +# arm7 Architecture Makefile +# arch/arm7/Makefile + +CPPFLAGS = +CFLAGS = +ASFLAGS = + +CPPFLAGS += -DMMU_PRESENT=1 +LDFLAGS += `$(CC) --print-libgcc-file-name` + +A_OBJ = start.ao main.o lib.o lib.ao time.o pci.o debug.o +A_OBJ += mm_phys.o mm_virt.o proc.o proc.ao + +#main.c: Makefile.BuildNum.$(ARCH) + +ifeq ($(PLATFORM),tegra2) + POSTBUILD = arm-elf-objcopy $(BIN) -O binary $(BIN) +endif diff --git a/KernelLand/Kernel/arch/armv7/debug.c b/KernelLand/Kernel/arch/armv7/debug.c new file mode 100644 index 00000000..7b9e55dd --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/debug.c @@ -0,0 +1,57 @@ +/** + * Acess2 + * - By John Hodge (thePowersGang) + * + * arch/arm7/debug.c + * - ARM7 Debug output + * NOTE: Currently designed for the realview-pb-a8 emulated by Qemu + */ +#include + +// === CONSTANTS === +//#define UART0_BASE 0x10009000 +#define UART0_BASE 0xF1000000 // Boot time mapped + +// === PROTOTYPES === +void KernelPanic_SetMode(void); +void KernelPanic_PutChar(char Ch); +void StartupPrint(const char *str); + +// === GLOBALS === + int giDebug_SerialInitialised = 0; + +// === CODE === +void Debug_PutCharDebug(char ch) +{ + if(ch == '\n') + Debug_PutCharDebug('\r'); + + #if PLATFORM_is_tegra2 + // Tegra2 + while( !(*(volatile Uint32*)(UART0_BASE + 0x14) & (1 << 5)) ) + ; + #endif + +// *(volatile Uint32*)(SERIAL_BASE + SERIAL_REG_DATA) = ch; + *(volatile Uint32*)(UART0_BASE) = ch; +} + +void Debug_PutStringDebug(const char *str) +{ + for( ; *str; str++ ) + Debug_PutCharDebug( *str ); +} + +void KernelPanic_SetMode(void) +{ +} + +void KernelPanic_PutChar(char ch) +{ +// Debug_PutCharDebug(ch); +} + +void StartupPrint(const char *str) +{ +} + diff --git a/KernelLand/Kernel/arch/armv7/include/arch.h b/KernelLand/Kernel/arch/armv7/include/arch.h new file mode 100644 index 00000000..837a5e10 --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/include/arch.h @@ -0,0 +1,44 @@ +/* + * Acess2 + * ARM7 Architecture Header + */ +#ifndef _ARCH_H_ +#define _ARCH_H_ + +// === CONSTANTS === +#define INVLPTR ((void*)-1) +#define BITS 32 +#define PAGE_SIZE 0x1000 +#define KERNEL_BASE 0x80000000 // 2GiB + +// === TYPES === +typedef unsigned int Uint; +typedef unsigned char Uint8; +typedef unsigned short Uint16; +typedef unsigned long Uint32; +typedef unsigned long long Uint64; +typedef signed int Sint; +typedef signed char Sint8; +typedef signed short Sint16; +typedef signed long Sint32; +typedef signed long long Sint64; + +typedef int size_t; +typedef char BOOL; + +typedef Uint32 tVAddr; +typedef Uint32 tPAddr; + +#include "lock.h" + +// --- Debug +extern void Debug_PutCharDebug(char Ch); +extern void Debug_PutStringDebug(const char *String); + +// This should be elsewhere, but CBF +extern void MM_SetupPhys(void); +extern int MM_InitialiseVirtual(void); + +#define NO_IO_BUS 1 + +#endif diff --git a/KernelLand/Kernel/arch/armv7/include/assembly.h b/KernelLand/Kernel/arch/armv7/include/assembly.h new file mode 100644 index 00000000..0c5c57fb --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/include/assembly.h @@ -0,0 +1,46 @@ +/* + * Acess2 ARMv7 + * - By John Hodge (thePowersGang) + * + * arch/arm7/include/assembly.h + * - Assembly specific macros + */ +#ifndef _ASSEMBLY_H_ +#define _ASSEMBLY_H_ + +#define PUSH_GPRS \ + str r0, [sp,#-1*4];\ + str r1, [sp,#-2*4];\ + str r2, [sp,#-3*4];\ + str r3, [sp,#-4*4];\ + str r4, [sp,#-5*4];\ + str r5, [sp,#-6*4];\ + str r6, [sp,#-7*4];\ + str r7, [sp,#-8*4];\ + str r8, [sp,#-9*4];\ + str r9, [sp,#-10*4];\ + str r10, [sp,#-11*4];\ + str r11, [sp,#-12*4];\ + str r12, [sp,#-13*4];\ + str sp, [sp,#-14*4];\ + str lr, [sp,#-15*4];\ + sub sp, #16*4 + +#define POP_GPRS add sp, #16*4; \ + ldr r0, [sp,#-1*4]; \ + ldr r1, [sp,#-2*4]; \ + ldr r2, [sp,#-3*4]; \ + ldr r3, [sp,#-4*4]; \ + ldr r4, [sp,#-5*4]; \ + ldr r5, [sp,#-6*4]; \ + ldr r6, [sp,#-7*4]; \ + ldr r7, [sp,#-8*4]; \ + ldr r8, [sp,#-9*4]; \ + ldr r9, [sp,#-10*4]; \ + ldr r10, [sp,#-11*4]; \ + ldr r11, [sp,#-12*4]; \ + ldr r12, [sp,#-13*4]; \ + ldr lr, [sp,#-15*4]; + +#endif + diff --git a/KernelLand/Kernel/arch/armv7/include/lock.h b/KernelLand/Kernel/arch/armv7/include/lock.h new file mode 100644 index 00000000..6688af48 --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/include/lock.h @@ -0,0 +1,63 @@ +/* + * Acess2 + * ARM7 Architecture + * + * lock.h - Hardware level spinlocks + */ +#ifndef _LOCK_H_ +#define _LOCK_H_ + +// === CODE === +struct sShortSpinlock { + int Lock; +}; + +// --- Spinlocks --- +static inline int IS_LOCKED(struct sShortSpinlock *Lock) +{ + return !!Lock->Lock; +} + +static inline int CPU_HAS_LOCK(struct sShortSpinlock *Lock) +{ + // TODO: Handle multiple CPUs + return !!Lock->Lock; +} + +static inline int SHORTLOCK(struct sShortSpinlock *Lock) +{ + #if 0 + // Coped from linux, yes, but I know what it does now :) + Uint tmp; + __asm__ __volatile__ ( + "1: ldrex %0, [%1]\n" // Exclusive LOAD + " teq %0, #0\n" // Check if zero + " strexeq %0, %2, [%1]\n" // Set to one if it is zero (releasing lock on the memory) + " teqeq %0, #0\n" // If the lock was avaliable, check if the write succeeded + " bne 1b" // If the lock was unavaliable, or the write failed, loop + : "=&r" (tmp) // Temp + : "r" (&Lock->Lock), "r" (1) + : "cc" // Condition codes clobbered + ); + #elif 1 + while( *(volatile int*)&Lock->Lock ) ; + Lock->Lock = 1; + #else + int v = 1; + while( v ) + __asm__ __volatile__ ( + "swp %0, %0, [%1]" + : "=r" (v) : "r" (&Lock->Lock) + : "cc" + ); + #endif + return 1; +} + +static inline void SHORTREL(struct sShortSpinlock *Lock) +{ + Lock->Lock = 0; +} + +#endif + diff --git a/KernelLand/Kernel/arch/armv7/include/mm_virt.h b/KernelLand/Kernel/arch/armv7/include/mm_virt.h new file mode 100644 index 00000000..c1f10deb --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/include/mm_virt.h @@ -0,0 +1,57 @@ +/* + * Acess2 + * ARM7 Virtual Memory Manager Header + */ +#ifndef _MM_VIRT_H_ +#define _MM_VIRT_H_ + +#include "options.h" + +#define USER_STACK_COMM 0x04000 // Pages to allocate up front +#define USER_STACK_SIZE 0x10000 // Stack space +#define USER_STACK_TOP 0x78000000 + +#define MM_USER_MIN 0x00001000 +#define USER_LIB_MAX 0x70000000 +#define MM_PPD_HANDLES 0x7F800000 +#define MM_TABLE1USER 0x7FC00000 // 2 GiB - 4 MiB +#define MM_TABLE0USER 0x7FE00000 // 2 GiB - 2 MiB +#define MM_KSTACK_BASE 0x7FE00000 +#define MM_KSTACK_END 0x80000000 + +// Page Blocks are 12-bits wide (12 address bits used) +// Hence, the table is 16KiB large (and must be so aligned) +// and each block addresses 1MiB of data + +// First level table is aligned to 16KiB (restriction of TTBR reg) +// - VMSAv6 uses two TTBR regs, determined by bit 31 + +//#define KERNEL_BASE 0x80000000 // 2GiB + +#define MM_KHEAP_BASE 0x80800000 // 8MiB of kernel code +#define MM_KHEAP_MAX 0xC0000000 // ~1GiB of kernel heap + +#define MM_MODULE_MIN 0xC0000000 // - 0xD0000000 +#define MM_MODULE_MAX 0xCF000000 + +#define MM_GLOBALSTACKS 0xCF000000 // Global stacks +#define MM_GLOBALSTACKS_END 0xD0000000 + +// PMM Data, giving it 256MiB is overkill, but it's unused atm +#define MM_MAXPHYSPAGE (1024*1024) +// 2^(32-12) max pages +// 8.125 bytes per page (for bitmap allocation) +// = 8.125 MiB +#define MM_PMM_BASE 0xE0000000 +#define MM_PMM_END 0xF0000000 + +#define MM_HWMAP_BASE 0xF0000000 // Ent 0xF00 +#define MM_HWMAP_END 0xFE000000 +#define MM_TMPMAP_BASE 0xFE000000 +#define MM_TMPMAP_END 0xFF000000 + +#define MM_KERNEL_VFS 0xFF000000 // +#define MM_TABLE1KERN 0xFF800000 // - 0x???????? 4MiB +//#define MM_TABLE0KERN 0xFFC00000 // - 0xFFE04000 16KiB + +#endif diff --git a/KernelLand/Kernel/arch/armv7/include/options.h b/KernelLand/Kernel/arch/armv7/include/options.h new file mode 100644 index 00000000..95345ed6 --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/include/options.h @@ -0,0 +1,33 @@ +/* + * Acess2 ARMv7 Port + * - By John Hodge (thePowersGang) + * + * options.h + * - C/ASM Shared constants + */ +#ifndef _ARMV7_OPTIONS_H_ +#define _ARMV7_OPTIONS_H_ + +#define KERNEL_BASE 0x80000000 + +//#define PCI_PADDR 0x60000000 // Realview (Non-PB) + +#if PLATFORM_is_realview_pb +# define UART0_PADDR 0x10009000 // Realview +# define GICI_PADDR 0x1e000000 +# define GICD_PADDR 0x1e001000 +# define PL110_BASE 0x10020000 // Integrator + +#endif + +#if PLATFORM_is_tegra2 // Tegra2 +# define UART0_PADDR 0x70006000 +# define GICD_PADDR 0x50041000 +# define GICI_PADDR 0x50040100 +//# define PL110_BASE 0x10020000 // Integrator +#endif + +#define MM_KSTACK_SIZE 0x2000 // 2 Pages + +#endif + diff --git a/KernelLand/Kernel/arch/armv7/include/proc.h b/KernelLand/Kernel/arch/armv7/include/proc.h new file mode 100644 index 00000000..d6ef3d55 --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/include/proc.h @@ -0,0 +1,48 @@ +/* + * Acess2 + * ARM7 Architecture + * + * proc.h - Arch-Dependent Process Management + */ +#ifndef _PROC_H_ +#define _PROC_H_ + +#define MAX_CPUS 4 +#define USER_MAX 0x80000000 + +// === STRUCTURES === +typedef struct { + Uint32 IP, SP; + Uint32 UserIP, UserSP; +} tTaskState; + +typedef struct { + Uint32 Base; +} tMemoryState; + +typedef struct { + union { + Uint32 Num; + Uint32 Error; + }; + union { + Uint32 Arg1; + Uint32 Return; + }; + union { + Uint32 Arg2; + Uint32 RetHi; + }; + Uint32 Arg3; + Uint32 Arg4; + Uint32 Arg5; + Uint32 Arg6; // R6 +} tSyscallRegs; + +// === MACROS === +#define HALT() do{}while(0) + +// === PROTOTYPES === + +#endif + diff --git a/KernelLand/Kernel/arch/armv7/lib.S b/KernelLand/Kernel/arch/armv7/lib.S new file mode 100644 index 00000000..e2f06130 --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/lib.S @@ -0,0 +1,84 @@ +/* + * Acess2 ARM + * - By John Hodge (thePowersGang) + * + * arch/arm7/lib.S + * - Assembly editions of library functions + */ +#include "include/assembly.h" + +.globl __memcpy_byte +__memcpy_byte: +1: + tst r2, r2 @ Check counter + moveq pc, lr @ Return if zero + ldrb r3, [r1],#1 @ Read + strb r3, [r0],#1 @ Write + sub r2, #1 + b 1b + +@ +@ Pre-aligned memcpy (32-bit blocks) +@ +.globl __memcpy_align4 +__memcpy_align4: + push {r4} + mvn r3, #3 @ Mask for checking length + + @ 4 byte chunk copies +1: tst r2, r3 + ldrne r4, [r1],#4 + strne r4, [r0],#4 + subne r2, #4 + bne 1b + + @ single byte copies to finish off +2: tst r2, #3 + beq 3f + ldrb r4, [r1],#1 + strb r4, [r0],#1 + sub r2, #1 + b 2b + +3: pop {r4} + mov pc, lr + +@ +@ Division +@ +.globl __divmod32_asm +__divmod32_asm: + push {r4} + mov r4, #0 @ Return value + mov r3, #1 @ add value + + @ Scan up for first larger multiple of 2 +1: cmp r0, r1 @ N < D + bmi 2f @ ^^ + lsl r1, r1, #1 @ D <<= 1 + lsls r3, r3, #1 @ add <<= 1 + beq .err @ result is zero + b 1b + + @ Go back down +2: lsrs r3, r3, #1 @ add >>= 1 + beq 3f @ Done (value is zero) + lsr r1, r1, #1 @ D >>= 1 + cmp r0, r1 @ N < D + bmi 2b + sub r0, r1 @ N -= D + add r4, r3 @ ret += add + b 2b +3: + tst r2, r2 @ Remainder (if wanted) + strne r0,[r2] + mov r0, r4 @ Return value + pop {r4} + mov pc, lr +.err: + mov r0, #0 + tst r2, r2 + strne r0, [r2] + pop {r4} + mov pc, lr + diff --git a/KernelLand/Kernel/arch/armv7/lib.c b/KernelLand/Kernel/arch/armv7/lib.c new file mode 100644 index 00000000..5a6928e7 --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/lib.c @@ -0,0 +1,217 @@ +/* + * Acess2 ARM7 Port + * + * lib.c - Library Functions + */ +#include +#include "../helpers.h" + +// === IMPORTS === +extern void __memcpy_align4(void *_dest, const void *_src, size_t _length); +extern void __memcpy_byte(void *_dest, const void *_src, size_t _length); +extern Uint32 __divmod32_asm(Uint32 Num, Uint32 Den, Uint32 *Rem); + +// === PROTOTYPES === +Uint64 __divmod64(Uint64 Num, Uint64 Den, Uint64 *Rem); +Uint32 __divmod32(Uint32 Num, Uint32 Den, Uint32 *Rem); +Uint64 __udivdi3(Uint64 Num, Uint64 Den); +Uint64 __umoddi3(Uint64 Num, Uint64 Den); +Uint32 __udivsi3(Uint32 Num, Uint32 Den); +Uint32 __umodsi3(Uint32 Num, Uint32 Den); +Sint32 __divsi3(Sint32 Num, Sint32 Den); +Sint32 __modsi3(Sint32 Num, Sint32 Den); + +// === CODE === +void *memcpy(void *_dest, const void *_src, size_t _length) +{ + Uint8 *dst8 = _dest; + const Uint8 *src8 = _src; + + if( ((tVAddr)_dest & 3) == 0 && ((tVAddr)_src & 3) == 0 ) + { + __memcpy_align4(_dest, _src, _length); + return _dest; + } + + // Handle small copies / Non-aligned + if( _length < 4 || ((tVAddr)_dest & 3) != ((tVAddr)_src & 3) ) + { + __memcpy_byte(_dest, _src, _length); + return _dest; + } + + // Force alignment + while( (tVAddr)dst8 & 3 ) *dst8 ++ = *src8++, _length --; + + __memcpy_align4(dst8, src8, _length); + + return _dest; +} + +int memcmp(const void *_m1, const void *_m2, size_t _length) +{ + const Uint32 *m1, *m2; + const Uint8 *m1_8 = _m1, *m2_8 = _m2; + + // Handle small copies / Non-aligned + if( _length < 4 || ((tVAddr)_m1 & 3) != ((tVAddr)_m1 & 3) ) + { + for( ; _length--; m1_8++,m2_8++ ) { + if(*m1_8 != *m2_8) return *m1_8 - *m2_8; + } + return 0; + } + + // Force alignment + for( ; (tVAddr)m1_8 & 3; m1_8 ++, m2_8 ++) { + if(*m1_8 != *m2_8) return *m1_8 - *m2_8; + } + m1 = (void *)m1_8; m2 = (void *)m2_8; + + // DWORD copies + for( ; _length > 3; _length -= 4, m1++, m2++) + if(*m1 != *m2) return *m1 - *m2; + + // Trailing bytes + m1_8 = (void*)m1; m2_8 = (void*)m2; + for( ; _length; _length --, m1_8++, m2_8++ ) + if(*m1_8 != *m2_8) return *m1_8 - *m2_8; + + return 0; +} + +void *memset(void *_dest, int _value, size_t _length) +{ + Uint32 *dst, val32; + Uint8 *dst8 = _dest; + + _value = (Uint8)_value; + + // Handle small copies + if( _length < 4 ) + { + for( ; _length--; dst8++ ) + *dst8 = _value; + return _dest; + } + + val32 = _value; + val32 |= val32 << 8; + val32 |= val32 << 16; + + // Force alignment + while( (tVAddr)dst8 & 3 ) *dst8 ++ = _value; + dst = (void *)dst8; + + // DWORD copies + for( ; _length > 3; _length -= 4) + *dst++ = val32; + + // Trailing bytes + dst8 = (void*)dst; + for( ; _length; _length -- ) + *dst8 ++ = _value; + + return _dest; +} + +DEF_DIVMOD(64) +DEF_DIVMOD(32) + +Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem) +{ + Uint64 ret; + if(Den == 0) return 0; // TODO: #div0 + if(Num < Den) { + if(Rem) *Rem = Num; + return 0; + } + if(Num == 0) { + if(Rem) *Rem = 0; + return 0; + } + if(Den == 1) { + if(Rem) *Rem = 0; + return Num; + } + if(Den == 2) { + if(Rem) *Rem = Num & 1; + return Num >> 1; + } + if(Den == 16) { + if(Rem) *Rem = Num & 0xF; + return Num >> 4; + } + if(Den == 32) { + if(Rem) *Rem = Num & 0x1F; + return Num >> 5; + } + if(Den == 0x1000) { + if(Rem) *Rem = Num & 0xFFF; + return Num >> 12; + } + + if( !(Den >> 32) && !(Num >> 32) ) { + if(Rem) *Rem = 0; // Clear high bits + return __divmod32_asm(Num, Den, (Uint32*)Rem); + } + + ret = __divmod64(Num, Den, Rem); + return ret; +} + +// Unsigned Divide 64-bit Integer +Uint64 __udivdi3(Uint64 Num, Uint64 Den) +{ + return DivMod64U(Num, Den, NULL); +} + +// Unsigned Modulus 64-bit Integer +Uint64 __umoddi3(Uint64 Num, Uint64 Den) +{ + Uint64 ret = 0; + DivMod64U(Num, Den, &ret); + return ret; +} + +Uint32 __udivsi3(Uint32 Num, Uint32 Den) +{ + return __divmod32_asm(Num, Den, NULL); +} + +Uint32 __umodsi3(Uint32 Num, Uint32 Den) +{ + Uint32 rem; + __divmod32_asm(Num, Den, &rem); + return rem; +} + +static inline Sint32 DivMod32S(Sint32 Num, Sint32 Den, Sint32 *Rem) +{ + Sint32 ret = 1; + if( Num < 0 ) { + ret = -ret; + Num = -Num; + } + if( Den < 0 ) { + ret = -ret; + Den = -Den; + } + if(ret < 0) + ret = -__divmod32(Num, Den, (Uint32*)Rem); + else + ret = __divmod32(Num, Den, (Uint32*)Rem); + return ret; +} + +Sint32 __divsi3(Sint32 Num, Sint32 Den) +{ + return DivMod32S(Num, Den, NULL); +} + +Sint32 __modsi3(Sint32 Num, Sint32 Den) +{ + Sint32 rem; + DivMod32S(Num, Den, &rem); + return rem; +} diff --git a/KernelLand/Kernel/arch/armv7/link.ld b/KernelLand/Kernel/arch/armv7/link.ld new file mode 100644 index 00000000..d10dcc46 --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/link.ld @@ -0,0 +1,55 @@ +ENTRY (_start) + +_kernel_base = 0x80000000; +_usertext_vbase = 0xFFFFE000; + +SECTIONS +{ + . = 0; + .init : + { + *(.init) + } + . += _kernel_base; + .text : AT( ADDR(.text) - _kernel_base ) + { + *(.text*) + *(.rodata*) + } + + + /* HACKS: User accesible .text section */ + . = ALIGN(0x1000); + gUsertextPhysStart = . - _kernel_base; + . = _usertext_vbase; + .usertext : AT( gUsertextPhysStart ) + { + *(.usertext) + } + . += gUsertextPhysStart + _kernel_base - _usertext_vbase; + + /* 0x4000 (4 pages) alignment needed for root table */ + .data ALIGN(0x4000) : AT( ADDR(.data) - _kernel_base ) + { + *(.padata) + *(.data*) + + gKernelSymbols = .; + *(KEXPORT) + gKernelSymbolsEnd = .; + + gKernelModules = .; + *(KMODULES) + gKernelModulesEnd = .; + } + .bss : AT( ADDR(.bss) - _kernel_base ) + { + bss_start = .; + *(.bss*) + *(COMMON*) + . = ALIGN(0x1000); + *(.pabss) + bss_end = .; + } + gKernelEnd = .; +} diff --git a/KernelLand/Kernel/arch/armv7/main.c b/KernelLand/Kernel/arch/armv7/main.c new file mode 100644 index 00000000..248c17c5 --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/main.c @@ -0,0 +1,96 @@ +/* + * Acess2 + * + * ARM7 Entrypoint + * arch/arm7/main.c + */ +#define DEBUG 0 + +#include +#include + +// === IMPORTS === +extern void Interrupts_Setup(void); +extern void Arch_LoadBootModules(void); +extern void Heap_Install(void); +extern void Threads_Init(void); +extern void System_Init(const char *Commandline); + +// === PROTOTYPES === + int kmain(void); +Uint32 ARMv7_int_HandleSyscalls(Uint32 Num, Uint32 *Args); + +// === CODE === +int kmain(void) +{ + LogF("Acess2 ARMv7 v"EXPAND_STR(KERNEL_VERSION)"\n"); + LogF(" Git Hash %s\n", gsGitHash); + LogF(" Build %i\n", BUILD_NUM); + + MM_SetupPhys(); + + LogF("Heap Setup...\n"); + Heap_Install(); + + LogF("Threads Init...\n"); + Threads_Init(); + + LogF("VFS Init...\n"); + VFS_Init(); + + // Boot modules? + Module_EnsureLoaded("armv7_GIC"); + + // + LogF("Moving to arch-independent init\n"); + #if PLATFORM_is_tegra2 + System_Init("Acess2.armv7.bin /Acess=initrd: -VTerm:Video=Tegra2Vid"); + #else + System_Init("Acess2.armv7.bin /Acess=initrd: -VTerm:Video=PL110"); + #endif +// System_Init("Acess2.armv7.bin /Acess=initrd:"); + //TODO: + LogF("End of kmain(), for(;;) Threads_Sleep();\n"); + for(;;) + Threads_Sleep(); +} + +void Arch_LoadBootModules(void) +{ +} + +Uint32 ARMv7_int_HandleSyscalls(Uint32 Num, Uint32 *Args) +{ + Uint32 ret = -1, err = 0; + Uint32 addr; + ENTER("iNum xArgs[0] xArgs[1] xArgs[2] xArgs[3]", + Num, Args[0], Args[1], Args[2], Args[3] + ); + switch(Num) + { + case 1: +// Log_Debug("ARMv7", "__clear_cache(%p, %p)", Args[0], Args[1]); + // Align + Args[0] &= ~0xFFF; + Args[1] += 0xFFF; Args[1] &= ~0xFFF; + // Invalidate! + for( addr = Args[0]; addr < Args[1]; addr += 0x1000 ) + { + LOG("addr = %p", addr); + __asm__ __volatile__ ( + "mcrlt p15, 0, %0, c7, c5, 1;\n\t" + "mcrlt p15, 0, %0, c7, c6, 1;\n\t" + : + : "r" (addr) + ); + } + ret = 0; + break; + } + Args[0] = ret; // RetLow + Args[1] = 0; // RetHi + Args[2] = err; // Errno + LEAVE('x', ret); + return ret; +} + diff --git a/KernelLand/Kernel/arch/armv7/mm_phys.c b/KernelLand/Kernel/arch/armv7/mm_phys.c new file mode 100644 index 00000000..5e4a2428 --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/mm_phys.c @@ -0,0 +1,60 @@ +/* + * Acess2 + * + * ARM7 Physical Memory Manager + * arch/arm7/mm_phys.c + */ +#define DEBUG 0 + +#include +#include + +#define MM_NUM_RANGES 1 // Single range +#define MM_RANGE_MAX 0 +#define TRACE_ALLOCS 0 + +#define NUM_STATIC_ALLOC 4 + +char gStaticAllocPages[NUM_STATIC_ALLOC][PAGE_SIZE] __attribute__ ((section(".padata"))); +tPAddr gaiStaticAllocPages[NUM_STATIC_ALLOC] = { + (tPAddr)(&gStaticAllocPages[0]) - KERNEL_BASE, + (tPAddr)(&gStaticAllocPages[1]) - KERNEL_BASE, + (tPAddr)(&gStaticAllocPages[2]) - KERNEL_BASE, + (tPAddr)(&gStaticAllocPages[3]) - KERNEL_BASE +}; +extern char gKernelEnd[]; + + +#include + +//#define REALVIEW_LOWRAM_SIZE 0x10000000 +#define REALVIEW_LOWRAM_SIZE (32*1024*1024) + +void MM_SetupPhys(void) +{ + LogF("MM_SetupPhys: ()\n"); + MM_Tpl_InitPhys( REALVIEW_LOWRAM_SIZE/0x1000, NULL ); +} + +int MM_int_GetMapEntry( void *Data, int Index, tPAddr *Start, tPAddr *Length ) +{ + switch(Index) + { + case 0: + *Start = ((tVAddr)&gKernelEnd - KERNEL_BASE + 0xFFF) & ~0xFFF; + *Length = REALVIEW_LOWRAM_SIZE - *Start; + return 1; + default: + return 0; + } +} + +/** + * \brief Takes a physical address and returns the ID of its range + * \param Addr Physical address of page + * \return Range ID from eMMPhys_Ranges + */ +int MM_int_GetRangeID( tPAddr Addr ) +{ + return MM_RANGE_MAX; // ARM doesn't need ranges +} diff --git a/KernelLand/Kernel/arch/armv7/mm_virt.c b/KernelLand/Kernel/arch/armv7/mm_virt.c new file mode 100644 index 00000000..460b334f --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/mm_virt.c @@ -0,0 +1,1078 @@ +/* + * Acess2 + * + * ARM7 Virtual Memory Manager + * - arch/arm7/mm_virt.c + */ +#define DEBUG 0 +#include +#include +#include + +#define TRACE_MAPS 0 + +#define AP_KRW_ONLY 1 // Kernel page +#define AP_KRO_ONLY 5 // Kernel RO page +#define AP_RW_BOTH 3 // Standard RW +#define AP_RO_BOTH 7 // COW Page +#define AP_RO_USER 2 // User RO Page +#define PADDR_MASK_LVL1 0xFFFFFC00 + +// === IMPORTS === +extern Uint32 kernel_table0[]; + +// === TYPES === +typedef struct +{ + tPAddr PhysAddr; + Uint8 Size; + Uint8 Domain; + BOOL bExecutable; + BOOL bGlobal; + BOOL bShared; + int AP; +} tMM_PageInfo; + +//#define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>20)]) +#define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>22)]) +#define USRFRACTAL(addr) (*((Uint32*)(0x7FDFF000) + ((addr)>>22))) +#define TLBIALL() __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0)) +#define TLBIMVA(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 1;dsb;isb" : : "r" ((addr)&~0xFFF):"memory") +#define DCCMVAC(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 1" : : "r" ((addr)&~0xFFF)) + +// === PROTOTYPES === +void MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1); + int MM_int_AllocateCoarse(tVAddr VAddr, int Domain); + int MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi); + int MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi); +tVAddr MM_NewUserStack(void); +tPAddr MM_AllocateZero(tVAddr VAddr); +tPAddr MM_AllocateRootTable(void); +void MM_int_CloneTable(Uint32 *DestEnt, int Table); +tPAddr MM_Clone(void); +tVAddr MM_NewKStack(int bGlobal); +void MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info); +//void MM_DumpTables(tVAddr Start, tVAddr End); +void MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch); + +// === GLOBALS === +tPAddr giMM_ZeroPage; + +// === CODE === +int MM_InitialiseVirtual(void) +{ + return 0; +} + +void MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1) +{ + if(VAddr & 0x80000000) { + *Table0 = (void*)&kernel_table0; // Level 0 + *Table1 = (void*)MM_TABLE1KERN; // Level 1 + } + else { + *Table0 = (void*)MM_TABLE0USER; + *Table1 = (void*)MM_TABLE1USER; + } +} + +int MM_int_AllocateCoarse(tVAddr VAddr, int Domain) +{ + Uint32 *table0, *table1; + Uint32 *desc; + tPAddr paddr; + + ENTER("xVAddr iDomain", VAddr, Domain); + + MM_int_GetTables(VAddr, &table0, &table1); + + VAddr &= ~(0x400000-1); // 4MiB per "block", 1 Page + + desc = &table0[ VAddr>>20]; + LOG("desc = %p", desc); + + // table0: 4 bytes = 1 MiB + + LOG("desc[0] = %x", desc[0]); + LOG("desc[1] = %x", desc[1]); + LOG("desc[2] = %x", desc[2]); + LOG("desc[3] = %x", desc[3]); + + if( (desc[0] & 3) != 0 || (desc[1] & 3) != 0 + || (desc[2] & 3) != 0 || (desc[3] & 3) != 0 ) + { + // Error? + LEAVE('i', 1); + return 1; + } + + paddr = MM_AllocPhys(); + if( !paddr ) + { + // Error + LEAVE('i', 2); + return 2; + } + + *desc = paddr | (Domain << 5) | 1; + desc[1] = desc[0] + 0x400; + desc[2] = desc[0] + 0x800; + desc[3] = desc[0] + 0xC00; + + if( VAddr < 0x80000000 ) { + USRFRACTAL(VAddr) = paddr | 0x13; + } + else { + FRACTAL(table1, VAddr) = paddr | 0x13; + } + + // TLBIALL + TLBIALL(); + + memset( (void*)&table1[ (VAddr >> 12) & ~(1024-1) ], 0, 0x1000 ); + + LEAVE('i', 0); + return 0; +} + +int MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi) +{ + Uint32 *table0, *table1; + Uint32 *desc; + + ENTER("pVAddr ppi", VAddr, pi); + + MM_int_GetTables(VAddr, &table0, &table1); + + desc = &table0[ VAddr >> 20 ]; + LOG("desc = %p", desc); + + switch(pi->Size) + { + case 12: // Small Page + case 16: // Large Page + LOG("Page"); + if( (*desc & 3) == 0 ) { + MM_int_AllocateCoarse( VAddr, pi->Domain ); + } + desc = &table1[ VAddr >> 12 ]; + LOG("desc (2) = %p", desc); + if( pi->Size == 12 ) + { + // Small page + // - Error if overwriting a large page + if( (*desc & 3) == 1 ) LEAVE_RET('i', 1); + if( pi->PhysAddr == 0 ) { + *desc = 0; + TLBIMVA( VAddr ); + DCCMVAC( (tVAddr) desc ); + #warning "HACK: TLBIALL" + TLBIALL(); + LEAVE('i', 0); + return 0; + } + + *desc = (pi->PhysAddr & 0xFFFFF000) | 2; + if(!pi->bExecutable) *desc |= 1; // XN + if(!pi->bGlobal) *desc |= 1 << 11; // nG + if( pi->bShared) *desc |= 1 << 10; // S + *desc |= (pi->AP & 3) << 4; // AP + *desc |= ((pi->AP >> 2) & 1) << 9; // APX + TLBIMVA( VAddr ); + #warning "HACK: TLBIALL" + TLBIALL(); + DCCMVAC( (tVAddr) desc ); + LEAVE('i', 0); + return 0; + } + else + { + // Large page + Log_Warning("MMVirt", "TODO: Implement large pages in MM_int_SetPageInfo"); + } + break; + case 20: // Section or unmapped + Log_Warning("MMVirt", "TODO: Implement sections in MM_int_SetPageInfo"); + break; + case 24: // Supersection + // Error if not aligned + if( VAddr & 0xFFFFFF ) { + LEAVE('i', 1); + return 1; + } + if( (*desc & 3) == 0 || ((*desc & 3) == 2 && (*desc & (1 << 18))) ) + { + if( pi->PhysAddr == 0 ) { + *desc = 0; + } + else { + // Apply + *desc = pi->PhysAddr & 0xFF000000; +// *desc |= ((pi->PhysAddr >> 32) & 0xF) << 20; +// *desc |= ((pi->PhysAddr >> 36) & 0x7) << 5; + *desc |= 2 | (1 << 18); + } + // TODO: Apply to all entries + Log_Warning("MMVirt", "TODO: Apply changes to all entries of supersections"); + LEAVE('i', 0); + return 0; + } + // TODO: What here? + Log_Warning("MMVirt", "TODO: 24-bit not on supersection?"); + LEAVE('i', 1); + return 1; + } + + LEAVE('i', 1); + return 1; +} + +int MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi) +{ + Uint32 *table0, *table1; + Uint32 desc; + +// LogF("MM_int_GetPageInfo: VAddr=%p, pi=%p\n", VAddr, pi); + + MM_int_GetTables(VAddr, &table0, &table1); + + desc = table0[ VAddr >> 20 ]; + +// if( VAddr > 0x90000000) +// LOG("table0 desc(%p) = %x", &table0[ VAddr >> 20 ], desc); + + pi->bExecutable = 1; + pi->bGlobal = 0; + pi->bShared = 0; + pi->AP = 0; + + switch( (desc & 3) ) + { + // 0: Unmapped + case 0: + pi->PhysAddr = 0; + pi->Size = 20; + pi->Domain = 0; + return 1; + + // 1: Coarse page table + case 1: + // Domain from top level table + pi->Domain = (desc >> 5) & 7; + // Get next level + desc = table1[ VAddr >> 12 ]; +// LOG("table1 desc(%p) = %x", &table1[ VAddr >> 12 ], desc); + switch( desc & 3 ) + { + // 0: Unmapped + case 0: + pi->Size = 12; + return 1; + // 1: Large Page (64KiB) + case 1: + pi->Size = 16; + pi->PhysAddr = desc & 0xFFFF0000; + pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2); + pi->bExecutable = !(desc & 0x8000); + pi->bShared = (desc >> 10) & 1; + return 0; + // 2/3: Small page + case 2: + case 3: + pi->Size = 12; + pi->PhysAddr = desc & 0xFFFFF000; + pi->bExecutable = !(desc & 1); + pi->bGlobal = !(desc >> 11); + pi->bShared = (desc >> 10) & 1; + pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2); + return 0; + } + return 1; + + // 2: Section (or Supersection) + case 2: + if( desc & (1 << 18) ) { + // Supersection + pi->PhysAddr = desc & 0xFF000000; + pi->PhysAddr |= (Uint64)((desc >> 20) & 0xF) << 32; + pi->PhysAddr |= (Uint64)((desc >> 5) & 0x7) << 36; + pi->Size = 24; + pi->Domain = 0; // Supersections default to zero + pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2); + return 0; + } + + // Section + pi->PhysAddr = desc & 0xFFF80000; + pi->Size = 20; + pi->Domain = (desc >> 5) & 7; + pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2); + return 0; + + // 3: Reserved (invalid) + case 3: + pi->PhysAddr = 0; + pi->Size = 20; + pi->Domain = 0; + return 2; + } + return 2; +} + +// --- Exports --- +tPAddr MM_GetPhysAddr(tVAddr VAddr) +{ + tMM_PageInfo pi; + if( MM_int_GetPageInfo(VAddr, &pi) ) + return 0; + return pi.PhysAddr | (VAddr & ((1 << pi.Size)-1)); +} + +Uint MM_GetFlags(tVAddr VAddr) +{ + tMM_PageInfo pi; + int ret; + + if( MM_int_GetPageInfo(VAddr, &pi) ) + return 0; + + ret = 0; + + switch(pi.AP) + { + case 0: + break; + case AP_KRW_ONLY: + ret |= MM_PFLAG_KERNEL; + break; + case AP_KRO_ONLY: + ret |= MM_PFLAG_KERNEL|MM_PFLAG_RO; + break; + case AP_RW_BOTH: + break; + case AP_RO_BOTH: + ret |= MM_PFLAG_COW; + break; + case AP_RO_USER: + ret |= MM_PFLAG_RO; + break; + } + + if( pi.bExecutable ) ret |= MM_PFLAG_EXEC; + return ret; +} + +void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask) +{ + tMM_PageInfo pi; + Uint curFlags; + + if( MM_int_GetPageInfo(VAddr, &pi) ) + return ; + + curFlags = MM_GetFlags(VAddr); + if( (curFlags & Mask) == Flags ) + return ; + curFlags &= ~Mask; + curFlags |= Flags; + + if( curFlags & MM_PFLAG_COW ) + pi.AP = AP_RO_BOTH; + else + { + switch(curFlags & (MM_PFLAG_KERNEL|MM_PFLAG_RO) ) + { + case 0: + pi.AP = AP_RW_BOTH; break; + case MM_PFLAG_KERNEL: + pi.AP = AP_KRW_ONLY; break; + case MM_PFLAG_RO: + pi.AP = AP_RO_USER; break; + case MM_PFLAG_KERNEL|MM_PFLAG_RO: + pi.AP = AP_KRO_ONLY; break; + } + } + + pi.bExecutable = !!(curFlags & MM_PFLAG_EXEC); + + MM_int_SetPageInfo(VAddr, &pi); +} + +int MM_IsValidBuffer(tVAddr Addr, size_t Size) +{ + tMM_PageInfo pi; + int bUser = 0; + + Size += Addr & (PAGE_SIZE-1); + Addr &= ~(PAGE_SIZE-1); + + if( MM_int_GetPageInfo(Addr, &pi) ) return 0; + Addr += PAGE_SIZE; + + if(pi.AP != AP_KRW_ONLY && pi.AP != AP_KRO_ONLY) + bUser = 1; + + while( Size >= PAGE_SIZE ) + { + if( MM_int_GetPageInfo(Addr, &pi) ) + return 0; + if(bUser && (pi.AP == AP_KRW_ONLY || pi.AP == AP_KRO_ONLY)) + return 0; + Addr += PAGE_SIZE; + Size -= PAGE_SIZE; + } + + return 1; +} + +int MM_Map(tVAddr VAddr, tPAddr PAddr) +{ + tMM_PageInfo pi = {0}; + #if TRACE_MAPS + Log("MM_Map %P=>%p", PAddr, VAddr); + #endif + + pi.PhysAddr = PAddr; + pi.Size = 12; + if(VAddr < USER_STACK_TOP) + pi.AP = AP_RW_BOTH; + else + pi.AP = AP_KRW_ONLY; // Kernel Read/Write + pi.bExecutable = 1; + if( MM_int_SetPageInfo(VAddr, &pi) ) { +// MM_DerefPhys(pi.PhysAddr); + return 0; + } + return pi.PhysAddr; +} + +tPAddr MM_Allocate(tVAddr VAddr) +{ + tMM_PageInfo pi = {0}; + + ENTER("pVAddr", VAddr); + + pi.PhysAddr = MM_AllocPhys(); + if( pi.PhysAddr == 0 ) LEAVE_RET('i', 0); + pi.Size = 12; + if(VAddr < USER_STACK_TOP) + pi.AP = AP_RW_BOTH; + else + pi.AP = AP_KRW_ONLY; + pi.bExecutable = 0; + if( MM_int_SetPageInfo(VAddr, &pi) ) { + MM_DerefPhys(pi.PhysAddr); + LEAVE('i', 0); + return 0; + } + LEAVE('x', pi.PhysAddr); + return pi.PhysAddr; +} + +tPAddr MM_AllocateZero(tVAddr VAddr) +{ + if( !giMM_ZeroPage ) { + giMM_ZeroPage = MM_Allocate(VAddr); + MM_RefPhys(giMM_ZeroPage); + memset((void*)VAddr, 0, PAGE_SIZE); + } + else { + MM_RefPhys(giMM_ZeroPage); + MM_Map(VAddr, giMM_ZeroPage); + } + MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW); + return giMM_ZeroPage; +} + +void MM_Deallocate(tVAddr VAddr) +{ + tMM_PageInfo pi; + + if( MM_int_GetPageInfo(VAddr, &pi) ) return ; + if( pi.PhysAddr == 0 ) return; + MM_DerefPhys(pi.PhysAddr); + + pi.PhysAddr = 0; + pi.AP = 0; + pi.bExecutable = 0; + MM_int_SetPageInfo(VAddr, &pi); +} + +tPAddr MM_AllocateRootTable(void) +{ + tPAddr ret; + + ret = MM_AllocPhysRange(2, -1); + if( ret & 0x1000 ) { + MM_DerefPhys(ret); + MM_DerefPhys(ret+0x1000); + ret = MM_AllocPhysRange(3, -1); + if( ret & 0x1000 ) { + MM_DerefPhys(ret); + ret += 0x1000; +// Log("MM_AllocateRootTable: Second try not aligned, %P", ret); + } + else { + MM_DerefPhys(ret + 0x2000); +// Log("MM_AllocateRootTable: Second try aligned, %P", ret); + } + } +// else +// Log("MM_AllocateRootTable: Got it in one, %P", ret); + return ret; +} + +void MM_int_CloneTable(Uint32 *DestEnt, int Table) +{ + tPAddr table; + Uint32 *tmp_map; + Uint32 *cur = (void*)MM_TABLE1USER; +// Uint32 *cur = &FRACTAL(MM_TABLE1USER,0); + int i; + + table = MM_AllocPhys(); + if(!table) return ; + + cur += 256*Table; + + tmp_map = (void*)MM_MapTemp(table); + + for( i = 0; i < 1024; i ++ ) + { +// Log_Debug("MMVirt", "cur[%i] (%p) = %x", Table*256+i, &cur[Table*256+i], cur[Table*256+i]); + switch(cur[i] & 3) + { + case 0: tmp_map[i] = 0; break; + case 1: + tmp_map[i] = 0; + Log_Error("MMVirt", "TODO: Support large pages in MM_int_CloneTable (%p)", (Table*256+i)*0x1000); + // Large page? + break; + case 2: + case 3: + // Small page + // - If full RW +// Debug("%p cur[%i] & 0x230 = 0x%x", Table*256*0x1000, i, cur[i] & 0x230); + if( (cur[i] & 0x230) == 0x010 ) + { + void *dst, *src; + tPAddr newpage; + newpage = MM_AllocPhys(); + src = (void*)( (Table*256+i)*0x1000 ); + dst = (void*)MM_MapTemp(newpage); +// Debug("Taking a copy of kernel page %p (%P)", src, cur[i] & ~0xFFF); + memcpy(dst, src, PAGE_SIZE); + MM_FreeTemp( (tVAddr)dst ); + tmp_map[i] = newpage | (cur[i] & 0xFFF); + } + else + { + if( (cur[i] & 0x230) == 0x030 ) + cur[i] |= 0x200; // Set to full RO (Full RO=COW, User RO = RO) + tmp_map[i] = cur[i]; + MM_RefPhys( tmp_map[i] & ~0xFFF ); + } + break; + } + } + MM_FreeTemp( (tVAddr) tmp_map ); + + DestEnt[0] = table + 0*0x400 + 1; + DestEnt[1] = table + 1*0x400 + 1; + DestEnt[2] = table + 2*0x400 + 1; + DestEnt[3] = table + 3*0x400 + 1; +} + +tPAddr MM_Clone(void) +{ + tPAddr ret; + Uint32 *new_lvl1_1, *new_lvl1_2, *cur; + Uint32 *tmp_map; + int i; + +// MM_DumpTables(0, KERNEL_BASE); + + ret = MM_AllocateRootTable(); + + cur = (void*)MM_TABLE0USER; + new_lvl1_1 = (void*)MM_MapTemp(ret); + new_lvl1_2 = (void*)MM_MapTemp(ret+0x1000); + tmp_map = new_lvl1_1; + for( i = 0; i < 0x800-4; i ++ ) + { + // HACK! Ignore the original identity mapping + if( i == 0 && Threads_GetTID() == 0 ) { + tmp_map[0] = 0; + continue; + } + if( i == 0x400 ) + tmp_map = &new_lvl1_2[-0x400]; + switch( cur[i] & 3 ) + { + case 0: tmp_map[i] = 0; break; + case 1: + MM_int_CloneTable(&tmp_map[i], i); + i += 3; // Tables are alocated in blocks of 4 + break; + case 2: + case 3: + Log_Error("MMVirt", "TODO: Support Sections/Supersections in MM_Clone (i=%i)", i); + tmp_map[i] = 0; + break; + } + } + + // Allocate Fractal table + { + int j, num; + tPAddr tmp = MM_AllocPhys(); + Uint32 *table = (void*)MM_MapTemp(tmp); + Uint32 sp; + register Uint32 __SP asm("sp"); + + // Map table to last 4MiB of user space + new_lvl1_2[0x3FC] = tmp + 0*0x400 + 1; + new_lvl1_2[0x3FD] = tmp + 1*0x400 + 1; + new_lvl1_2[0x3FE] = tmp + 2*0x400 + 1; + new_lvl1_2[0x3FF] = tmp + 3*0x400 + 1; + + tmp_map = new_lvl1_1; + for( j = 0; j < 512; j ++ ) + { + if( j == 256 ) + tmp_map = &new_lvl1_2[-0x400]; + if( (tmp_map[j*4] & 3) == 1 ) + { + table[j] = tmp_map[j*4] & PADDR_MASK_LVL1;// 0xFFFFFC00; + table[j] |= 0x813; // nG, Kernel Only, Small page, XN + } + else + table[j] = 0; + } + // Fractal + table[j++] = (ret + 0x0000) | 0x813; + table[j++] = (ret + 0x1000) | 0x813; + // Nuke the rest + for( ; j < 1024; j ++ ) + table[j] = 0; + + // Get kernel stack bottom + sp = __SP & ~(MM_KSTACK_SIZE-1); + j = (sp / 0x1000) % 1024; + num = MM_KSTACK_SIZE/0x1000; + +// Log("num = %i, sp = %p, j = %i", num, sp, j); + + // Copy stack pages + for(; num--; j ++, sp += 0x1000) + { + tVAddr page; + void *tmp_page; + + page = MM_AllocPhys(); +// Log("page = %P", page); + table[j] = page | 0x813; + + tmp_page = (void*)MM_MapTemp(page); + memcpy(tmp_page, (void*)sp, 0x1000); + MM_FreeTemp( (tVAddr) tmp_page ); + } + + MM_FreeTemp( (tVAddr)table ); + } + + MM_FreeTemp( (tVAddr)new_lvl1_1 ); + MM_FreeTemp( (tVAddr)new_lvl1_2 ); + +// Log("MM_Clone: ret = %P", ret); + + return ret; +} + +void MM_ClearUser(void) +{ + int i, j; + const int user_table_count = USER_STACK_TOP / (256*0x1000); + Uint32 *cur = (void*)MM_TABLE0USER; + Uint32 *tab; + +// MM_DumpTables(0, 0x80000000); + +// Log("user_table_count = %i (as opposed to %i)", user_table_count, 0x800-4); + + for( i = 0; i < user_table_count; i ++ ) + { + switch( cur[i] & 3 ) + { + case 0: break; // Already unmapped + case 1: // Sub pages + tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32)); + for( j = 0; j < 1024; j ++ ) + { + switch( tab[j] & 3 ) + { + case 0: break; // Unmapped + case 1: + Log_Error("MMVirt", "TODO: Support large pages in MM_ClearUser"); + break; + case 2: + case 3: + MM_DerefPhys( tab[j] & ~(PAGE_SIZE-1) ); + break; + } + } + MM_DerefPhys( cur[i] & ~(PAGE_SIZE-1) ); + cur[i+0] = 0; + cur[i+1] = 0; + cur[i+2] = 0; + i += 3; + break; + case 2: + case 3: + Log_Error("MMVirt", "TODO: Implement sections/supersections in MM_ClearUser"); + break; + } + cur[i] = 0; + } + + // Final block of 4 tables are KStack + i = 0x800 - 4; + + // Clear out unused stacks + { + register Uint32 __SP asm("sp"); + int cur_stack_base = ((__SP & ~(MM_KSTACK_SIZE-1)) / PAGE_SIZE) % 1024; + + tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32)); + + // First 512 is the Table1 mapping + 2 for Table0 mapping + for( j = 512+2; j < 1024; j ++ ) + { + // Skip current stack + if( j == cur_stack_base ) { + j += (MM_KSTACK_SIZE / PAGE_SIZE) - 1; + continue ; + } + if( !(tab[j] & 3) ) continue; + ASSERT( (tab[j] & 3) == 2 ); + MM_DerefPhys( tab[j] & ~(PAGE_SIZE) ); + tab[j] = 0; + } + } + + +// MM_DumpTables(0, 0x80000000); +} + +tVAddr MM_MapTemp(tPAddr PAddr) +{ + tVAddr ret; + tMM_PageInfo pi; + + for( ret = MM_TMPMAP_BASE; ret < MM_TMPMAP_END - PAGE_SIZE; ret += PAGE_SIZE ) + { + if( MM_int_GetPageInfo(ret, &pi) == 0 ) + continue; + +// Log("MapTemp %P at %p by %p", PAddr, ret, __builtin_return_address(0)); + MM_RefPhys(PAddr); // Counter the MM_Deallocate in FreeTemp + MM_Map(ret, PAddr); + + return ret; + } + Log_Warning("MMVirt", "MM_MapTemp: All slots taken"); + return 0; +} + +void MM_FreeTemp(tVAddr VAddr) +{ + if( VAddr < MM_TMPMAP_BASE || VAddr >= MM_TMPMAP_END ) { + Log_Warning("MMVirt", "MM_FreeTemp: Passed an addr not from MM_MapTemp (%p)", VAddr); + return ; + } + + MM_Deallocate(VAddr); +} + +tVAddr MM_MapHWPages(tPAddr PAddr, Uint NPages) +{ + tVAddr ret; + int i; + tMM_PageInfo pi; + + ENTER("xPAddr iNPages", PAddr, NPages); + + // Scan for a location + for( ret = MM_HWMAP_BASE; ret < MM_HWMAP_END - NPages * PAGE_SIZE; ret += PAGE_SIZE ) + { +// LOG("checking %p", ret); + // Check if there is `NPages` free pages + for( i = 0; i < NPages; i ++ ) + { + if( MM_int_GetPageInfo(ret + i*PAGE_SIZE, &pi) == 0 ) + break; + } + // Nope, jump to after the used page found and try again +// LOG("i = %i, ==? %i", i, NPages); + if( i != NPages ) { + ret += i * PAGE_SIZE; + continue ; + } + + // Map the pages + for( i = 0; i < NPages; i ++ ) + MM_Map(ret+i*PAGE_SIZE, PAddr+i*PAGE_SIZE); + // and return + LEAVE('p', ret); + return ret; + } + Log_Warning("MMVirt", "MM_MapHWPages: No space for a %i page block", NPages); + LEAVE('p', 0); + return 0; +} + +tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PAddr) +{ + tPAddr phys; + tVAddr ret; + + phys = MM_AllocPhysRange(Pages, MaxBits); + if(!phys) { + Log_Warning("MMVirt", "No space left for a %i page block (MM_AllocDMA)", Pages); + return 0; + } + + ret = MM_MapHWPages(phys, Pages); + *PAddr = phys; + + return ret; +} + +void MM_UnmapHWPages(tVAddr Vaddr, Uint Number) +{ + Log_Error("MMVirt", "TODO: Implement MM_UnmapHWPages"); +} + +tVAddr MM_NewKStack(int bShared) +{ + tVAddr min_addr, max_addr; + tVAddr addr, ofs; + + if( bShared ) { + min_addr = MM_GLOBALSTACKS; + max_addr = MM_GLOBALSTACKS_END; + } + else { + min_addr = MM_KSTACK_BASE; + max_addr = MM_KSTACK_END; + } + + // Locate a free slot + for( addr = min_addr; addr < max_addr; addr += MM_KSTACK_SIZE ) + { + tMM_PageInfo pi; + if( MM_int_GetPageInfo(addr+MM_KSTACK_SIZE-PAGE_SIZE, &pi) ) break; + } + + // Check for an error + if(addr >= max_addr) { + return 0; + } + + // 1 guard page + for( ofs = PAGE_SIZE; ofs < MM_KSTACK_SIZE; ofs += PAGE_SIZE ) + { + if( MM_Allocate(addr + ofs) == 0 ) + { + while(ofs) + { + ofs -= PAGE_SIZE; + MM_Deallocate(addr + ofs); + } + Log_Warning("MMVirt", "MM_NewKStack: Unable to allocate"); + return 0; + } + } + return addr + ofs; +} + +tVAddr MM_NewUserStack(void) +{ + tVAddr addr, ofs; + + addr = USER_STACK_TOP - USER_STACK_SIZE; + if( MM_GetPhysAddr(addr + PAGE_SIZE) ) { + Log_Error("MMVirt", "Unable to create initial user stack, addr %p taken", + addr + PAGE_SIZE + ); + return 0; + } + + // 1 guard page + for( ofs = PAGE_SIZE; ofs < USER_STACK_SIZE; ofs += PAGE_SIZE ) + { + tPAddr rv; + if(ofs >= USER_STACK_SIZE - USER_STACK_COMM) + rv = MM_Allocate(addr + ofs); + else + rv = MM_AllocateZero(addr + ofs); + if(rv == 0) + { + while(ofs) + { + ofs -= PAGE_SIZE; + MM_Deallocate(addr + ofs); + } + Log_Warning("MMVirt", "MM_NewUserStack: Unable to allocate"); + return 0; + } + MM_SetFlags(addr+ofs, 0, MM_PFLAG_KERNEL); + } +// Log("Return %p", addr + ofs); +// MM_DumpTables(0, 0x80000000); + return addr + ofs; +} + +void MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info) +{ + if( giMM_ZeroPage && Info->PhysAddr == giMM_ZeroPage ) + { + Debug("%p => %8s - 0x%7x %i %x %s", + Start, "ZERO", Len, + Info->Domain, Info->AP, + Info->bGlobal ? "G" : "nG" + ); + } + else + { + Debug("%p => %8x - 0x%7x %i %x %s", + Start, Info->PhysAddr-Len, Len, + Info->Domain, Info->AP, + Info->bGlobal ? "G" : "nG" + ); + } +} + +void MM_DumpTables(tVAddr Start, tVAddr End) +{ + tVAddr range_start = 0, addr; + tMM_PageInfo pi, pi_old; + int i = 0, inRange=0; + + memset(&pi_old, 0, sizeof(pi_old)); + + Debug("Page Table Dump (%p to %p):", Start, End); + range_start = Start; + for( addr = Start; i == 0 || (addr && addr < End); i = 1 ) + { + int rv; +// Log("addr = %p", addr); + rv = MM_int_GetPageInfo(addr, &pi); + if( rv + || pi.Size != pi_old.Size + || pi.Domain != pi_old.Domain + || pi.AP != pi_old.AP + || pi.bGlobal != pi_old.bGlobal + || pi_old.PhysAddr != pi.PhysAddr ) + { + if(inRange) { + MM_int_DumpTableEnt(range_start, addr - range_start, &pi_old); + } + addr &= ~((1 << pi.Size)-1); + range_start = addr; + } + + pi_old = pi; + // Handle the zero page + if( !giMM_ZeroPage || pi_old.Size != 12 || pi_old.PhysAddr != giMM_ZeroPage ) + pi_old.PhysAddr += 1 << pi_old.Size; + addr += 1 << pi_old.Size; + inRange = (rv == 0); + } + if(inRange) + MM_int_DumpTableEnt(range_start, addr - range_start, &pi); + Debug("Done"); +} + +// NOTE: Runs in abort context, not much difference, just a smaller stack +void MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch) +{ + int rv; + tMM_PageInfo pi; + + rv = MM_int_GetPageInfo(Addr, &pi); + + // Check for COW + if( rv == 0 && pi.AP == AP_RO_BOTH ) + { + pi.AP = AP_RW_BOTH; + if( giMM_ZeroPage && pi.PhysAddr == giMM_ZeroPage ) + { + tPAddr newpage; + newpage = MM_AllocPhys(); + if( !newpage ) { + Log_Error("MMVirt", "Unable to allocate new page for COW of ZERO"); + for(;;); + } + + #if TRACE_COW + Log_Notice("MMVirt", "COW %p caused by %p, ZERO duped to %P (RefCnt(%i)--)", Addr, PC, + newpage, MM_GetRefCount(pi.PhysAddr)); + #endif + + MM_DerefPhys(pi.PhysAddr); + pi.PhysAddr = newpage; + pi.AP = AP_RW_BOTH; + MM_int_SetPageInfo(Addr, &pi); + + memset( (void*)(Addr & ~(PAGE_SIZE-1)), 0, PAGE_SIZE ); + + return ; + } + else if( MM_GetRefCount(pi.PhysAddr) > 1 ) + { + // Duplicate the page + tPAddr newpage; + void *dst, *src; + + newpage = MM_AllocPhys(); + if(!newpage) { + Log_Error("MMVirt", "Unable to allocate new page for COW"); + for(;;); + } + dst = (void*)MM_MapTemp(newpage); + src = (void*)(Addr & ~(PAGE_SIZE-1)); + memcpy( dst, src, PAGE_SIZE ); + MM_FreeTemp( (tVAddr)dst ); + + #if TRACE_COW + Log_Notice("MMVirt", "COW %p caused by %p, %P duped to %P (RefCnt(%i)--)", Addr, PC, + pi.PhysAddr, newpage, MM_GetRefCount(pi.PhysAddr)); + #endif + + MM_DerefPhys(pi.PhysAddr); + pi.PhysAddr = newpage; + } + #if TRACE_COW + else { + Log_Notice("MMVirt", "COW %p caused by %p, took last reference to %P", + Addr, PC, pi.PhysAddr); + } + #endif + // Unset COW + pi.AP = AP_RW_BOTH; + MM_int_SetPageInfo(Addr, &pi); + return ; + } + + + Log_Error("MMVirt", "Code at %p accessed %p (DFSR = 0x%x)%s", PC, Addr, DFSR, + (bPrefetch ? " - Prefetch" : "") + ); + if( Addr < 0x80000000 ) + MM_DumpTables(0, 0x80000000); + else + MM_DumpTables(0x80000000, -1); + for(;;); +} + diff --git a/KernelLand/Kernel/arch/armv7/pci.c b/KernelLand/Kernel/arch/armv7/pci.c new file mode 100644 index 00000000..2e674bbc --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/pci.c @@ -0,0 +1,32 @@ +/* + * + */ +#include +#include + +// Realview +//#define PCI_BASE 0x60000000 + +//#define PCI_BASE 0xF0400000 // VMM Mapping +#define PCI_BASE 0 + +// === CODE === +void PCI_CfgWriteDWord(Uint32 Addr, Uint32 Data) +{ + #if PCI_BASE + Uint32 address = PCI_BASE | Addr; + *(Uint32*)(address) = Data; + #else + #endif +} + +Uint32 PCI_CfgReadDWord(Uint32 Addr) +{ + #if PCI_BASE + Uint32 address = PCI_BASE | Addr; + return *(Uint32*)address; + #else + return 0xFFFFFFFF; + #endif +} + diff --git a/KernelLand/Kernel/arch/armv7/proc.S b/KernelLand/Kernel/arch/armv7/proc.S new file mode 100644 index 00000000..531de299 --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/proc.S @@ -0,0 +1,104 @@ +/* + * Acess2 ARM + * - By John Hodge (thePowersGang) + * + * arch/arm7/proc.S + * - Process management assembly + */ + +#include "include/assembly.h" + +.globl KernelThreadHeader +@ SP+12: Argument 1 +@ SP+8: Argument Count +@ SP+4: Function +@ SP+0: Thread Pointer +KernelThreadHeader: + ldr r0, [sp],#4 + @ TODO: Do something with the thread pointer + + ldr r4, [sp],#4 @ Function + @ Get argument + ldr r0, [sp],#4 + + blx r4 + + ldr r0, =0 + bl Threads_Exit + b . + +.globl SwitchTask +@ R0: New stack +@ R1: Pointer to where to save old stack +@ R2: New IP +@ R3: Pointer to save old IP +@ SP+0: New address space +SwitchTask: + push {r4-r12,lr} + + @ Save IP + ldr r4, =.return + str r4, [r3] + @ Save SP + str sp, [r1] + + @ Only update TTBR0 if the task has an explicit address space + ldr r1, [sp,#4*10] + tst r1, r1 + mcrne p15, 0, r1, c2, c0, 0 @ Set TTBR0 to r0 + mov r1, #0 + mcrne p15, 0, r1, c8, c7, 0 @ TLBIALL - Invalidate all + + @ Restore SP + mov sp, r0 + + bx r2 + +.return: + pop {r4-r12,pc} + +.extern MM_Clone +.extern MM_DumpTables +.globl Proc_CloneInt +Proc_CloneInt: + @ R0: SP Destination + @ R1: Mem Destination + push {r4-r12,lr} + mov r4, r1 @ Save mem destination + str sp, [r0] @ Save SP to SP dest + + bl MM_Clone + str r0, [r4] @ Save clone return to Mem Dest + + ldr r0, =Proc_CloneInt_new + pop {r4-r12,pc} +Proc_CloneInt_new: + mov r0, #0 + pop {r4-r12,pc} + +@ R0: New user SP +@ Return: Old user SP +.globl Proc_int_SwapUserSP +Proc_int_SwapUserSP: + cps #31 @ Go to system mode + mov r1, sp + tst r0, r0 @ Only update if non-zero + movne sp, r0 + mov r0, r1 + cps #19 + mov pc, lr + +.section .usertext, "ax" +.globl Proc_int_DropToUser +@ R0: User IP +@ R1: User SP +Proc_int_DropToUser: + cps #16 + mov sp, r1 + mov pc, r0 + +.section .rodata +csProc_CloneInt_NewTaskMessage: + .asciz "New task PC=%p, R4=%p, sp=%p" +csProc_CloneInt_OldTaskMessage: + .asciz "Parent task PC=%p, R4=%p, SP=%p" diff --git a/KernelLand/Kernel/arch/armv7/proc.c b/KernelLand/Kernel/arch/armv7/proc.c new file mode 100644 index 00000000..cd998f2b --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/proc.c @@ -0,0 +1,235 @@ +/* + * Acess2 + * - By John Hodge (thePowersGang) + * + * arch/arm7/proc.c + * - ARM7 Process Switching + */ +#include +#include +#include + +// === IMPORTS === +extern tThread gThreadZero; +extern tProcess gProcessZero; +extern void SwitchTask(Uint32 NewSP, Uint32 *OldSP, Uint32 NewIP, Uint32 *OldIP, Uint32 MemPtr); +extern void KernelThreadHeader(void); // Actually takes args on stack +extern void Proc_int_DropToUser(Uint32 IP, Uint32 SP) NORETURN __attribute__((long_call)); +extern Uint32 Proc_int_SwapUserSP(Uint32 NewSP); +extern Uint32 Proc_CloneInt(Uint32 *SP, Uint32 *MemPtr); +extern tVAddr MM_NewKStack(int bGlobal); // TODO: Move out into a header +extern tVAddr MM_NewUserStack(void); +extern char kernel_table0[]; + +// === PROTOTYPES === +void Proc_IdleThread(void *unused); + +// === GLOBALS === +tThread *gpCurrentThread = &gThreadZero; +tThread *gpIdleThread = NULL; + +// === CODE === +void ArchThreads_Init(void) +{ + gProcessZero.MemState.Base = (tPAddr)&kernel_table0 - KERNEL_BASE; +} + +void Proc_IdleThread(void *unused) +{ + Threads_SetPriority(gpIdleThread, -1); + for(;;) { + Proc_Reschedule(); + __asm__ __volatile__ ("wfi"); + } +} + +void Proc_Start(void) +{ + tTID tid; + + tid = Proc_NewKThread( Proc_IdleThread, NULL ); + gpIdleThread = Threads_GetThread(tid); + gpIdleThread->ThreadName = (char*)"Idle Thread"; +} + +int GetCPUNum(void) +{ + return 0; +} + +tThread *Proc_GetCurThread(void) +{ + return gpCurrentThread; +} + +void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) +{ + Uint32 *usr_sp; + int i; + const char **envp; + tVAddr delta; + +// Log_Debug("Proc", "Proc_StartUser: (Entrypoint=%p, Base=%p, ArgC=%i, ArgV=%p, DataSize=0x%x)", +// Entrypoint, Base, ArgC, ArgV, DataSize); + + // Write data to the user's stack + usr_sp = (void*)MM_NewUserStack(); + usr_sp -= (DataSize+3)/4; + memcpy(usr_sp, ArgV, DataSize); + free(ArgV); + + // Adjust user's copy of the arguments + delta = (tVAddr)usr_sp - (tVAddr)ArgV; + ArgV = (void*)usr_sp; + for(i = 0; ArgV[i]; i ++) ArgV[i] += delta; + envp = &ArgV[i+1]; + for(i = 0; envp[i]; i ++) envp[i] += delta; + + *--usr_sp = (Uint32)envp; + *--usr_sp = (Uint32)ArgV; + *--usr_sp = (Uint32)ArgC; + *--usr_sp = Base; + + // Drop to user code + Log_Debug("Proc", "Proc_int_DropToUser(%p, %p)", Entrypoint, usr_sp); + Proc_int_DropToUser(Entrypoint, (Uint32)usr_sp); +} + +void Proc_ClearProcess(tProcess *Process) +{ + Log_Warning("Proc", "TODO: Nuke address space etc"); +} + +void Proc_ClearThread(tThread *Thread) +{ +} + +tTID Proc_Clone(Uint Flags) +{ + tThread *new; + Uint32 pc, sp, mem; + + new = Threads_CloneTCB(Flags); + if(!new) return -1; + + // Actual clone magic + pc = Proc_CloneInt(&sp, &mem); + if(pc == 0) { + Log("Proc_Clone: In child"); + return 0; + } + + new->SavedState.IP = pc; + new->SavedState.SP = sp; + new->SavedState.UserSP = Proc_int_SwapUserSP(0); + new->SavedState.UserIP = Proc_GetCurThread()->SavedState.UserIP; + new->Process->MemState.Base = mem; + + Threads_AddActive(new); + + return new->TID; +} + +int Proc_SpawnWorker( void (*Fnc)(void*), void *Ptr ) +{ + tThread *new; + Uint32 sp; + + new = Threads_CloneThreadZero(); + if(!new) return -1; + if(new->ThreadName) free(new->ThreadName); + new->ThreadName = NULL; + + new->KernelStack = MM_NewKStack(1); + if(!new->KernelStack) { + // TODO: Delete thread + Log_Error("Proc", "Unable to allocate kernel stack"); + return -1; + } + + sp = new->KernelStack; + + *(Uint32*)(sp -= 4) = (Uint)Ptr; + *(Uint32*)(sp -= 4) = (Uint)Fnc; + *(Uint32*)(sp -= 4) = (Uint)new; + + new->SavedState.SP = sp; + new->SavedState.IP = (Uint)KernelThreadHeader; + + Threads_AddActive(new); + + return new->TID; +} + +tTID Proc_NewKThread( void (*Fnc)(void*), void *Ptr ) +{ + tThread *new; + Uint32 sp; + + new = Threads_CloneTCB(0); + if(!new) return -1; + free(new->ThreadName); + new->ThreadName = NULL; + + // TODO: Non-shared stack + new->KernelStack = MM_NewKStack(1); + if(!new->KernelStack) { + // TODO: Delete thread + Log_Error("Proc", "Unable to allocate kernel stack"); + return -1; + } + + sp = new->KernelStack; + + *(Uint32*)(sp -= 4) = (Uint)Ptr; + *(Uint32*)(sp -= 4) = (Uint)Fnc; + *(Uint32*)(sp -= 4) = (Uint)new; + + new->SavedState.SP = sp; + new->SavedState.IP = (Uint)KernelThreadHeader; + + Threads_AddActive(new); + + return new->TID; +} + +void Proc_CallFaultHandler(tThread *Thread) +{ + +} + +void Proc_Reschedule(void) +{ + tThread *cur, *next; + + cur = gpCurrentThread; + + next = Threads_GetNextToRun(0, cur); + if(!next) next = gpIdleThread; + if(!next || next == cur) return; + + Log("Switching to %p (%i %s) IP=%p SP=%p TTBR0=%p UsrSP=%p", + next, next->TID, next->ThreadName, + next->SavedState.IP, next->SavedState.SP, next->Process->MemState.Base, + next->SavedState.UserSP + ); + + Log("Requested by %p", __builtin_return_address(0)); + + gpCurrentThread = next; + + cur->SavedState.UserSP = Proc_int_SwapUserSP( next->SavedState.UserSP ); + + SwitchTask( + next->SavedState.SP, &cur->SavedState.SP, + next->SavedState.IP, &cur->SavedState.IP, + next->Process->MemState.Base + ); + +} + +void Proc_DumpThreadCPUState(tThread *Thread) +{ + +} + diff --git a/KernelLand/Kernel/arch/armv7/start.S b/KernelLand/Kernel/arch/armv7/start.S new file mode 100644 index 00000000..8d9f3e4d --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/start.S @@ -0,0 +1,367 @@ + +#include "include/assembly.h" +#include "include/options.h" + +@ +@ Exception defs taken from ARM DDI 0406B +@ +.section .init +interrupt_vector_table: +ivt_reset: b _start @ 0x00 Reset +ivt_undef: b Undef_Handler @ 0x04 #UD +ivt_svc: b SVC_Handler @ 0x08 SVC (used to be called SWI) +ivt_prefetch: b PrefetchAbort @ 0x0C Prefetch abort +ivt_data: b DataAbort @ 0x10 Data abort +ivt_unused: b . @ 0x14 Not Used +ivt_irq: b IRQHandler @ 0x18 IRQ +ivt_fiq: b . @ 0x1C FIQ (Fast interrupt) + +.globl _start +_start: + ldr r2, =UART0_PADDR + mov r1, #'A' + str r1, [r2] + + ldr r0, =kernel_table0-KERNEL_BASE + mcr p15, 0, r0, c2, c0, 1 @ Set TTBR1 to r0 + mcr p15, 0, r0, c2, c0, 0 @ Set TTBR0 to r0 too (for identity) + + mov r1, #'c' + str r1, [r2] + + mov r0, #1 + mcr p15, 0, r0, c2, c0, 2 @ Set TTCR to 1 (50/50 split) + + mov r1, #'e' + str r1, [r2] + + mov r0, #3 + mcr p15, 0, r0, c3, c0, 0 @ Set Domain 0 to Manager + + mov r1, #'s' + str r1, [r2] + + @ Enable VMSA + mrc p15, 0, r0, c1, c0, 0 + orr r0, r0, #1 + orr r0, r0, #1 << 23 + mvn r1, #1 << 2 + and r0, r0, r1 + mcr p15, 0, r0, c1, c0, 0 + + @ HACK! Disable caching + mrc p15, 0, r1, c1, c0, 0 + + ldr r2, =0xF1000000 + mov r1, #'s' + str r1, [r2] + + @ Enable access faults on domains 0 & 1 + mov r0, #0x55 @ 01010101b + mcr p15, 0, r0, c3, c0, 0 + + mov r1, #'2' + str r1, [r2] + + @ + @ Check for security extensions + @ + mrc p15, 0, r0, c0, c1, 1 + and r0, #0xF0 + @ - Present + ldrne r0,=KERNEL_BASE + mcrne p15, 0, r0, c12, c0, 0 @ Set the VBAR (brings exceptions into high memory) + @ - Absent + mrceq p15, 0, r0, c1, c0, 0 @ Set SCTLR.V + orreq r0, #0x2000 + mcreq p15, 0, r0, c1, c0, 0 + + mov r1, #'-' + str r1, [r2] + + @ Prepare for interrupts + cps #18 @ IRQ Mode + ldr sp, =irqstack+0x1000 @ Set up stack + cps #23 @ Abort Mode + ldr sp, =abortstack+0x1000 + cps #19 + + mov r1, #'a' + str r1, [r2] + mov r1, #'r' + str r1, [r2] + mov r1, #'m' + str r1, [r2] + mov r1, #13 + str r1, [r2] + mov r1, #10 + str r1, [r2] + +.extern bss_start +.extern bss_size_div_4 +.zero_bss: + ldr r0, =bss_start + ldr r1, =bss_end + mov r3, #0 +.zero_bss_loop: + str r3, [r0],#4 + cmp r0, r1 + bls .zero_bss_loop + +.goto_c: + ldr sp, =0x80000000-8 @ Set up stack (top of user range) + ldr r0, =kmain + mov pc, r0 +1: b 1b @ Infinite loop + +.comm irqstack, 0x1000 @ ; 4KiB Stack +.comm abortstack, 0x1000 @ ; 4KiB Stack + +.extern SyscallHandler +SVC_Handler: +@ sub lr, #4 + srsdb sp!, #19 @ Save state to stack + cpsie ifa, #19 @ Ensure we're in supervisor with interrupts enabled (should already be there) + push {r0-r12} + + ldr r4, [lr,#-4] + mvn r5, #0xFF000000 + and r4, r5 + + tst r4, #0x1000 + bne .arm_specifics + + push {r4} + + mov r0, sp + ldr r4, =SyscallHandler + blx r4 + +@ ldr r0, =csSyscallPrintRetAddr +@ ldr r1, [sp,#9*4+5*4] +@ ldr r4, =Log +@ blx r4 + + pop {r2} @ errno + pop {r0,r1} @ Ret/RetHi + add sp, #2*4 @ Saved r2/r3 + + pop {r4-r12} + rfeia sp! @ Pop state (actually RFEFD) +.arm_specifics: + and r4, #0xFF + mov r0, r4 @ Number + mov r1, sp @ Arguments + + ldr r4, =ARMv7_int_HandleSyscalls + blx r4 + + add sp, #4*4 + pop {r4-r12} + rfeia sp! + + +.globl gpIRQHandler +gpIRQHandler: .long 0 +IRQ_saved_sp: .long 0 +IRQ_saved_lr: .long 0 +.globl IRQHandler +IRQHandler: + sub lr, #4 @ Adjust LR to the correct value + srsdb sp!, #19 @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD) + cps #19 + + PUSH_GPRS + +@ ldr r0, =csIRQ_Tag +@ ldr r1, =csIRQ_Fmt +@ ldr r4, =Log_Debug +@ blx r4 + + @ Call the registered handler + ldr r0, gpIRQHandler + blx r0 + + @ Restore CPU state + POP_GPRS + cpsie i + rfeia sp! @ Pop state (actually RFEFD) + bx lr + +.globl DataAbort +DataAbort: + sub lr, #8 @ Adjust LR to the correct value + srsdb sp!, #23 @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD) +@ cpsid ifa, #19 + PUSH_GPRS + + mov r3, #0 @ not a prefetch abort + mrc p15, 0, r2, c5, c0, 0 @ Read DFSR (Data Fault Status Register) to R2 + mrc p15, 0, r1, c6, c0, 0 @ Read DFAR (Data Fault Address Register) into R1 + mov r0, lr @ PC + ldr r4, =MM_PageFault + blx r4 + + POP_GPRS + rfeia sp! @ Pop state (actually RFEFD) + +.globl PrefetchAbort +PrefetchAbort: + sub lr, #4 @ Adjust LR to the correct value + srsdb sp!, #23 @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD) +@ cpsid ifa, #19 + PUSH_GPRS + + ldr r0, =csAbort_Tag + ldr r1, =csPrefetchAbort_Fmt +# mov r2, lr + mrc p15, 0, r2, c6, c0, 2 @ Read IFAR (Instruction Fault Address Register) into R3 + mrc p15, 0, r3, c5, c0, 1 @ Read IFSR (Instruction Fault Status Register) into R3 + ldr r5, =Log_Error + blx r5 + +.loop: + wfi + b .loop +.globl Undef_Handler +Undef_Handler: + wfi + b Undef_Handler + + + +.section .rodata +csIRQ_Tag: +csAbort_Tag: + .asciz "ARMv7" +csIRQ_Fmt: + .asciz "IRQ" +csDataAbort_Fmt: + .asciz "Data Abort - %p accessed %p, DFSR=%x Unk:%x Unk:%x" +csPrefetchAbort_Fmt: + .asciz "Prefetch Abort at %p, IFSR=%x" +csSyscallPrintRetAddr: + .asciz "Syscall ret to %p" + +.section .padata +.globl kernel_table0 + +kernel_table0: + .long 0x00000402 @ Identity map the first 1 MiB + .rept 0x7FC - 1 + .long 0 + .endr + .long user_table1_map + 0x000 - KERNEL_BASE + 1 @ 0x7FC00000 + .long user_table1_map + 0x400 - KERNEL_BASE + 1 @ 0x7FD00000 + .long user_table1_map + 0x800 - KERNEL_BASE + 1 @ KStacks + .long user_table1_map + 0xC00 - KERNEL_BASE + 1 + @ 0x80000000 - User/Kernel split + .long 0x00000402 @ Map first 4 MiB to 2GiB (KRW only) + .long 0x00100402 @ + .long 0x00200402 @ + .long 0x00300402 @ + .rept 0xF00 - 0x800 - 4 + .long 0 + .endr +#if PCI_PADDR + .long PCI_PADDR + 0*(1 << 20) + 0x402 @ Map PCI config space + .long PCI_PADDR + 1*(1 << 20) + 0x402 + .long PCI_PADDR + 2*(1 << 20) + 0x402 + .long PCI_PADDR + 3*(1 << 20) + 0x402 + .long PCI_PADDR + 4*(1 << 20) + 0x402 + .long PCI_PADDR + 5*(1 << 20) + 0x402 + .long PCI_PADDR + 6*(1 << 20) + 0x402 + .long PCI_PADDR + 7*(1 << 20) + 0x402 + .long PCI_PADDR + 8*(1 << 20) + 0x402 + .long PCI_PADDR + 9*(1 << 20) + 0x402 + .long PCI_PADDR + 10*(1 << 20) + 0x402 + .long PCI_PADDR + 11*(1 << 20) + 0x402 + .long PCI_PADDR + 12*(1 << 20) + 0x402 + .long PCI_PADDR + 13*(1 << 20) + 0x402 + .long PCI_PADDR + 14*(1 << 20) + 0x402 + .long PCI_PADDR + 15*(1 << 20) + 0x402 +#else + .rept 16 + .long 0 + .endr +#endif + .long hwmap_table_0 + 0x000 - KERNEL_BASE + 1 + .long hwmap_table_0 + 0x400 - KERNEL_BASE + 1 + .long hwmap_table_0 + 0x800 - KERNEL_BASE + 1 + .long hwmap_table_0 + 0xC00 - KERNEL_BASE + 1 + .rept 0xFF8 - 0xF00 - 16 - 4 + .long 0 + .endr + @ Page fractals + .long kernel_table1_map + 0x000 - KERNEL_BASE + 1 + .long kernel_table1_map + 0x400 - KERNEL_BASE + 1 + .long kernel_table1_map + 0x800 - KERNEL_BASE + 1 + .long kernel_table1_map + 0xC00 - KERNEL_BASE + 1 + .long kernel_exception_map + 0x000 - KERNEL_BASE + 1 + .long kernel_exception_map + 0x400 - KERNEL_BASE + 1 + .long kernel_exception_map + 0x800 - KERNEL_BASE + 1 + .long kernel_exception_map + 0xC00 - KERNEL_BASE + 1 + +@ PID0 user table +.globl user_table1_map +@ User table1 data table (only the first half is needed) +@ - Abused to provide kernel stacks in the unused half of the table +user_table1_map: @ Size = 4KiB (only 2KiB used) + .rept 0x800/4-1 + .long 0 + .endr + .long user_table1_map - KERNEL_BASE + 0x13 @ ...1FF000 = 0x7FDFF000 + @ Kernel stack zone + .long kernel_table0 + 0x0000 - KERNEL_BASE + 0x13 @ ...200000 = 0x7FE00000 + .long kernel_table0 + 0x1000 - KERNEL_BASE + 0x13 @ ...201000 = 0x7FE01000 + .rept (0x800/4)-(MM_KSTACK_SIZE/0x1000)-2 + .long 0 + .endr + #if MM_KSTACK_SIZE != 0x2000 + #error Kernel stack size not changed in start.S + #endif + .long stack + 0x0000 - KERNEL_BASE + 0x13 @ Kernel Stack + .long stack + 0x1000 - KERNEL_BASE + 0x13 @ + +.globl kernel_table1_map +kernel_table1_map: @ Size = 4KiB + .rept (0xF00+16)/4 + .long 0 + .endr + .long hwmap_table_0 - KERNEL_BASE + 0x13 + .rept 0xFF8/4 - (0xF00+16)/4 - 1 + .long 0 + .endr + .long kernel_table1_map - KERNEL_BASE + 0x13 + .long kernel_exception_map - KERNEL_BASE + 0x13 + +@ Hardware mappings +.globl hwmap_table_0 +hwmap_table_0: + .long UART0_PADDR + 0x13 @ UART0 + .rept 1024 - 1 + .long 0 + .endr +.globl kernel_exception_map +kernel_exception_map: + @ Padding + .rept 1024-256 + .long 0 + .endr + @ Align to nearly the end + .rept 256-16 + .long 0 + .endr + .long 0x212 @ Map first page for exceptions (Kernel RO, Execute) + .rept 16-1-2 + .long 0 + .endr + .long gUsertextPhysStart + 0x22 @ User .text (User RO, Kernel RW, because both is COW) + .long 0 + +.section .padata +stack: + .space MM_KSTACK_SIZE, 0 @ Original kernel stack + +// vim: ts=8 ft=armv7 + diff --git a/KernelLand/Kernel/arch/armv7/time.c b/KernelLand/Kernel/arch/armv7/time.c new file mode 100644 index 00000000..d4ae4fa6 --- /dev/null +++ b/KernelLand/Kernel/arch/armv7/time.c @@ -0,0 +1,16 @@ +/* + * Acess2 + * + * ARM7 Time code + * arch/arm7/time.c + */ +#include + +// === GLOBALS === +tTime giTimestamp; + +// === CODE === +tTime now(void) +{ + return giTimestamp; +} diff --git a/KernelLand/Kernel/arch/helpers.h b/KernelLand/Kernel/arch/helpers.h new file mode 100644 index 00000000..4d85fcb4 --- /dev/null +++ b/KernelLand/Kernel/arch/helpers.h @@ -0,0 +1,27 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * arch/helpers.h + * - Misc helper functions for the arch code + */ +#ifndef _ARCH_HELPERS_H_ +#define _ARCH_HELPERS_H_ + +// Divide +// - Find what power of two times Den is > Num +// - Iterate down in bit significance +// > If the `N` value is greater than `D`, we can set this bit +#define DEF_DIVMOD(s) Uint##s __divmod##s(Uint##s N, Uint##s D, Uint##s*Rem){\ + Uint##s ret=0,add=1;\ + while(N>=D&&add) {D<<=1;add<<=1;}\ + while(add>1){\ + add>>=1;D>>=1;\ + if(N>=D){ret+=add;N-=D;}\ + }\ + if(Rem)*Rem = N;\ + return ret;\ +} + +#endif + diff --git a/KernelLand/Kernel/arch/m68k/Makefile b/KernelLand/Kernel/arch/m68k/Makefile new file mode 100644 index 00000000..4caea1e1 --- /dev/null +++ b/KernelLand/Kernel/arch/m68k/Makefile @@ -0,0 +1,11 @@ +# +# Acess2 Kernel +# m68k Architecture Makefile +# arch/m68k/Makefile + +CFLAGS = + +A_OBJ = main.o debug.o lib.o time.o proc.o mm_virt.o mm_phys.o + +LDFLAGS += `$(CC) --print-libgcc-file-name` + diff --git a/KernelLand/Kernel/arch/m68k/debug.c b/KernelLand/Kernel/arch/m68k/debug.c new file mode 100644 index 00000000..8c7de3b1 --- /dev/null +++ b/KernelLand/Kernel/arch/m68k/debug.c @@ -0,0 +1,27 @@ +/* + * Acess2 M68K port + * - By John Hodge (thePowersGang) + * + * arch/m68k/debug.c + * - Debugging output + */ +#include + +// === PROTOTYPES === +void StartupPrint(const char *Str); +void KernelPanic_SetMode(void); +void KernelPanic_PutChar(char ch); + +// === CODE === +void StartupPrint(const char *Str) +{ +} + +void KernelPanic_SetMode(void) +{ +} + +void KernelPanic_PutChar(char ch) +{ +} + diff --git a/KernelLand/Kernel/arch/m68k/include/arch.h b/KernelLand/Kernel/arch/m68k/include/arch.h new file mode 100644 index 00000000..0b2dc5b4 --- /dev/null +++ b/KernelLand/Kernel/arch/m68k/include/arch.h @@ -0,0 +1,53 @@ +/* + * Acess2 M68000 port + * - By John Hodge (thePowersGang) + * + * arch/m68k/include/arch.h + * - Architectre config + */ +#ifndef _M68K_ARCH_H_ +#define _M68K_ARCH_H_ + +#define INVLPTR ((void*)-1) +#define BITS 32 + +typedef unsigned long long Uint64; +typedef unsigned long Uint32; +typedef unsigned short Uint16; +typedef unsigned char Uint8; + +typedef signed long long Sint64; +typedef signed long Sint32; +typedef signed short Sint16; +typedef signed char Sint8; + +typedef unsigned int Uint; +typedef unsigned int size_t; + +typedef char BOOL; + +typedef Uint32 tVAddr; +typedef Uint32 tPAddr; + +struct sShortSpinlock +{ + int v; +}; + +// Non preemptive and no SMP, no need for these +#define SHORTLOCK(lock) do{}while(0) +#define SHORTREL(lock) do{}while(0) +#define IS_LOCKED(lock) (0) +#define CPU_HAS_LOCK(lock) (0) + +#define Debug_PutCharDebug(ch) do{}while(0) +#define Debug_PutStringDebug(ch) do{}while(0) + +#define HALT() do{}while(0) + +#define USER_MAX 0 + +#define PAGE_SIZE 0x1000 + +#endif + diff --git a/KernelLand/Kernel/arch/m68k/include/mm_virt.h b/KernelLand/Kernel/arch/m68k/include/mm_virt.h new file mode 100644 index 00000000..01480b9b --- /dev/null +++ b/KernelLand/Kernel/arch/m68k/include/mm_virt.h @@ -0,0 +1,23 @@ +/* + * Acess2 M68000 port + * - By John Hodge (thePowersGang) + * + * arch/m68k/include/mm_virt.h + * - Virtual memory addresses + */ +#ifndef _M68K_MM_VIRT_H_ +#define _M68K_MM_VIRT_H_ + +#define MM_KHEAP_BASE 0 +#define MM_KHEAP_MAX 0 + +#define MM_USER_MIN 0 +#define USER_LIB_MAX 0 +#define MM_MODULE_MIN 0 +#define MM_MODULE_MAX 0 + +#define MM_PPD_HANDLES 0 +#define MM_KERNEL_VFS 0 + +#endif + diff --git a/KernelLand/Kernel/arch/m68k/include/proc.h b/KernelLand/Kernel/arch/m68k/include/proc.h new file mode 100644 index 00000000..8cf6aa0c --- /dev/null +++ b/KernelLand/Kernel/arch/m68k/include/proc.h @@ -0,0 +1,40 @@ +/* + * Acess2 M68000 port + * - By John Hodge (thePowersGang) + * + * arch/m68k/include/proc.h + * - Task management defs + */ +#ifndef _M68K_PROC_H_ +#define _M68K_PROC_H_ + +#define MAX_CPUS 1 + +typedef int tMemoryState; // Unused + +typedef struct { + Uint32 IP; + Uint32 SP; +} tTaskState; + +typedef struct { + Uint32 Num; + union { + Uint32 Arg1; + Uint32 Return; + }; + union { + Uint32 Arg2; + Uint32 RetHi; + }; + union { + Uint32 Arg3; + Uint32 Error; + }; + Uint32 Arg4; + Uint32 Arg5; + Uint32 Arg6; +} tSyscallRegs; + +#endif + diff --git a/KernelLand/Kernel/arch/m68k/lib.c b/KernelLand/Kernel/arch/m68k/lib.c new file mode 100644 index 00000000..2888a318 --- /dev/null +++ b/KernelLand/Kernel/arch/m68k/lib.c @@ -0,0 +1,114 @@ +/* + * Acess2 M68K port + * - By John Hodge (thePowersGang) + * + * arch/m68k/lib.c + * - Library functions + */ +#include +#include "../helpers.h" +#include + +Uint64 __divmod64(Uint64 Num, Uint64 Den, Uint64 *Rem); +Uint64 __udivdi3(Uint64 Num, Uint64 Den); +Uint64 __umoddi3(Uint64 Num, Uint64 Den); + +// === CODE === +void *memcpy(void *__dest, const void *__src, size_t __len) +{ + register Uint8 *dest = __dest; + register const Uint8 *src = __src; + + while(__len --) + *dest++ = *src++; + + return __dest; +} + +void *memset(void *__dest, int __val, size_t __count) +{ + register Uint8 *dest = __dest; + + while(__count --) + *dest = __val; + + return __dest; +} + +int memcmp(const void *__p1, const void *__p2, size_t __maxlen) +{ + const char *m1 = __p1, *m2 = __p2; + while( __maxlen-- ) + { + if(*m1 != *m2) return *m1 - *m2; + } + return 0; +} + +DEF_DIVMOD(64) + +Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem) +{ + Uint64 ret; + if(Den == 0) return 0; // TODO: #div0 + if(Num < Den) { + if(Rem) *Rem = Num; + return 0; + } + if(Num == 0) { + if(Rem) *Rem = 0; + return 0; + } + if(Den == 1) { + if(Rem) *Rem = 0; + return Num; + } + if(Den == 2) { + if(Rem) *Rem = Num & 1; + return Num >> 1; + } + if(Den == 16) { + if(Rem) *Rem = Num & 0xF; + return Num >> 4; + } + if(Den == 32) { + if(Rem) *Rem = Num & 0x1F; + return Num >> 5; + } + if(Den == 0x1000) { + if(Rem) *Rem = Num & 0xFFF; + return Num >> 12; + } + + if( !(Den >> 32) && !(Num >> 32) ) { + if(Rem) *Rem = (Uint32)Num % (Uint32)Den; // Clear high bits + return (Uint32)Num / (Uint32)Den; + } + + ret = __divmod64(Num, Den, Rem); + return ret; +} + +// Unsigned Divide 64-bit Integer +Uint64 __udivdi3(Uint64 Num, Uint64 Den) +{ + return DivMod64U(Num, Den, NULL); +} + +// Unsigned Modulus 64-bit Integer +Uint64 __umoddi3(Uint64 Num, Uint64 Den) +{ + Uint64 ret = 0; + DivMod64U(Num, Den, &ret); + return ret; +} + +// ---- PCI (stubbed) +Uint32 PCI_CfgReadDWord(Uint32 Addr) +{ + return 0xFFFFFFFF; +} +void PCI_CfgWriteDWord(Uint32 Addr, Uint32 Data) +{ +} + diff --git a/KernelLand/Kernel/arch/m68k/link.ld b/KernelLand/Kernel/arch/m68k/link.ld new file mode 100644 index 00000000..c0b0c9d4 --- /dev/null +++ b/KernelLand/Kernel/arch/m68k/link.ld @@ -0,0 +1,40 @@ +ENTRY (_start) + +_kernel_base = 0x0; + +SECTIONS +{ + . = 0; + .init : + { + *(.init) + } + . += _kernel_base; + .text : AT( ADDR(.text) - _kernel_base ) + { + *(.text*) + *(.rodata*) + } + /* 0x4000 (4 pages) alignment needed for root table */ + .data ALIGN(0x4000) : AT( ADDR(.data) - _kernel_base ) + { + *(.padata) + *(.data*) + + gKernelSymbols = .; + *(KEXPORT) + gKernelSymbolsEnd = .; + + gKernelModules = .; + *(KMODULES) + gKernelModulesEnd = .; + } + .bss : AT( ADDR(.bss) - _kernel_base ) + { + *(.bss*) + *(COMMON*) + . = ALIGN(0x1000); + *(.pabss) + } + gKernelEnd = .; +} diff --git a/KernelLand/Kernel/arch/m68k/main.c b/KernelLand/Kernel/arch/m68k/main.c new file mode 100644 index 00000000..3f78e4bb --- /dev/null +++ b/KernelLand/Kernel/arch/m68k/main.c @@ -0,0 +1,26 @@ +/* + * Acess2 M68K port + * - By John Hodge (thePowersGang) + * + * arch/m68k/main.c + * - C entrypoint + */ +#include +#include + +// === PROTOTYPES === +void kmain(void); + +// === CODE === +void kmain(void) +{ + LogF("Acess2 m68k v"EXPAND_STR(KERNEL_VERSION)"\n"); + LogF(" Build %i, Git Hash %s\n", BUILD_NUM, gsGitHash); + +} + +void Arch_LoadBootModules(void) +{ + +} + diff --git a/KernelLand/Kernel/arch/m68k/mm_phys.c b/KernelLand/Kernel/arch/m68k/mm_phys.c new file mode 100644 index 00000000..24fd1f2c --- /dev/null +++ b/KernelLand/Kernel/arch/m68k/mm_phys.c @@ -0,0 +1,22 @@ +/* + * Acess2 M68K port + * - By John Hodge (thePowersGang) + * + * arch/m68k/mm_phys.c + * - Stubbed physical memory management + */ +#include + +// === CODE === +void MM_RefPhys(tPAddr Page) +{ + // TODO: Refcount pages + Log_Warning("MMPhys", "TODO: Implement MM_RefPhys"); +} + +int MM_SetPageNode(tPAddr Page, void *Node) +{ + Log_Warning("MMPhys", "TODO: Implement MM_SetPageNode"); + return -1; +} + diff --git a/KernelLand/Kernel/arch/m68k/mm_virt.c b/KernelLand/Kernel/arch/m68k/mm_virt.c new file mode 100644 index 00000000..88990a1e --- /dev/null +++ b/KernelLand/Kernel/arch/m68k/mm_virt.c @@ -0,0 +1,54 @@ +/* + * Acess2 M68K port + * - By John Hodge (thePowersGang) + * + * arch/m68k/mm_virt.c + * - Stubbed virtual memory management (no MMU) + */ +#include +#include +#include + +// === CODE === +tPAddr MM_GetPhysAddr(tVAddr Addr) +{ + return Addr; +} + +void MM_SetFlags(tVAddr Addr, Uint Val, Uint Mask) +{ + return ; +} + +Uint MM_GetFlags(tVAddr Addr) +{ + return 0; +} + +int MM_Map(tVAddr Dest, tPAddr Src) +{ + Dest &= (PAGE_SIZE-1); + Src &= (PAGE_SIZE-1); + if(Dest != Src) + memcpy((void*)Dest, (void*)Src, PAGE_SIZE); + return 0; +} + +tPAddr MM_Allocate(tVAddr Dest) +{ + return Dest; +} + +void MM_Deallocate(tVAddr Addr) +{ +} + +void MM_ClearUser(void) +{ +} + +void MM_DumpTables(tVAddr Start, tVAddr End) +{ + +} + diff --git a/KernelLand/Kernel/arch/m68k/proc.c b/KernelLand/Kernel/arch/m68k/proc.c new file mode 100644 index 00000000..c148bff7 --- /dev/null +++ b/KernelLand/Kernel/arch/m68k/proc.c @@ -0,0 +1,75 @@ +/* + * Acess2 M68K port + * - By John Hodge (thePowersGang) + * + * arch/m68k/proc.c + * - Multithreading + */ +#include +#include +#include + +// === IMPORTS === +extern tThread gThreadZero; + +// === GLOBALS === +tThread *gpCurrentThread = &gThreadZero; + +// === CODE === +void ArchThreads_Init(void) +{ +} + +void Proc_Start(void) +{ +} + +tThread *Proc_GetCurThread(void) +{ + return gpCurrentThread; +} + +int GetCPUNum(void) +{ + return 0; +} + +tTID Proc_Clone(Uint Flags) +{ + UNIMPLEMENTED(); + return -1; +} + +tTID Proc_NewKThread(tThreadFunction Fcn, void *Arg) +{ + UNIMPLEMENTED(); + return -1; +} + +tTID Proc_SpawnWorker(tThreadFunction Fcn, void *Arg) +{ + UNIMPLEMENTED(); + return -1; +} + +void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, char **ArgV, int DataSize) +{ + Log_KernelPanic("Proc", "TODO: Implement Proc_StartUser"); + for(;;); +} + +void Proc_CallFaultHandler(tThread *Thread) +{ + +} + +void Proc_DumpThreadCPUState(tThread *Thread) +{ + +} + +void Proc_Reschedule(void) +{ + Log_Notice("Proc", "TODO: Implement Proc_Reschedule"); +} + diff --git a/KernelLand/Kernel/arch/m68k/time.c b/KernelLand/Kernel/arch/m68k/time.c new file mode 100644 index 00000000..0fffaf66 --- /dev/null +++ b/KernelLand/Kernel/arch/m68k/time.c @@ -0,0 +1,14 @@ +/* + * Acess2 M68K port + * - By John Hodge (thePowersGang) + * + * arch/m68k/time.c + * - Timekeeping + */ +#include + +// === CODE === +Sint64 now(void) +{ + return 0; +} diff --git a/KernelLand/Kernel/arch/x86/Makefile b/KernelLand/Kernel/arch/x86/Makefile new file mode 100644 index 00000000..c83a5af2 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/Makefile @@ -0,0 +1,26 @@ +# +# Acess2 Kernel +# i386 Architecture Makefile +# arch/i386/Makefile + +AS_SUFFIX = asm + +CPPFLAGS = +CFLAGS = +ASFLAGS = -f elf + +USE_MP=0 + +ifeq ($(PLATFORM),default) + USE_MP=0 +else ifeq ($(PLATFORM),smp) + USE_MP=1 +endif + +ASFLAGS += -D USE_MP=$(USE_MP) +CPPFLAGS += -DUSE_MP=$(USE_MP) + +A_OBJ = start.ao main.o lib.o desctab.ao errors.o irq.o +A_OBJ += mm_phys.o mm_virt.o +A_OBJ += proc.o proc.ao time.o vm8086.o +A_OBJ += kpanic.o pci.o diff --git a/KernelLand/Kernel/arch/x86/desctab.asm b/KernelLand/Kernel/arch/x86/desctab.asm new file mode 100644 index 00000000..6eaa651a --- /dev/null +++ b/KernelLand/Kernel/arch/x86/desctab.asm @@ -0,0 +1,327 @@ +; AcessOS Microkernel Version +; +; desctab.asm +[BITS 32] + + +[section .data] +; IDT +ALIGN 8 +[global gIDT] +gIDT: + ; CS = 0x08, Type = 32-bit Interrupt (0xE = 1 110) + times 256 dd 0x00080000,0x00000E00 +[global gIDTPtr] +gIDTPtr: + dw 256 * 16 - 1 ; Limit + dd gIDT ; Base + +[section .text] + +[global Desctab_Install] +Desctab_Install: + ; Set up IDT + ; Helper Macros + ; - Set an IDT entry to an ISR +%macro SETISR 1 + mov eax, Isr%1 + mov WORD [gIDT + %1*8], ax + shr eax, 16 + mov WORD [gIDT + %1*8+6], ax + ; Enable + mov ax, WORD [gIDT + %1*8 + 4] + or ax, 0x8000 + mov WORD [gIDT + %1*8 + 4], ax +%endmacro + ; Enable user calling of an ISR +%macro SET_USER 1 + or WORD [gIDT + %1*8 + 4], 0x6000 +%endmacro + ; Set an ISR as a trap (leaves interrupts enabled when invoked) +%macro SET_TRAP 1 + or WORD [gIDT + %1*8 + 4], 0x0100 +%endmacro + + ; Error handlers + %assign i 0 + %rep 32 + SETISR i + %assign i i+1 + %endrep + + ; User Syscall + SETISR 0xAC + SET_USER 0xAC + SET_TRAP 0xAC ; Interruptable + + ; MP ISRs + %if USE_MP + SETISR 0xED ; 0xED Inter-processor HALT + SETISR 0xEE ; 0xEE Timer + SETISR 0xEF ; 0xEF Spurious Interrupt + %endif + + ; IRQs + %assign i 0xF0 + %rep 16 + SETISR i + %assign i i+1 + %endrep + + ; Load IDT + lidt [gIDTPtr] + + ; Remap PIC + push edx ; Save EDX + mov dx, 0x20 + mov al, 0x11 + out dx, al ; Init Command + mov dx, 0x21 + mov al, 0xF0 + out dx, al ; Offset (Start of IDT Range) + mov al, 0x04 + out dx, al ; IRQ connected to Slave (00000100b) = IRQ2 + mov al, 0x01 + out dx, al ; Set Mode + mov al, 0x00 + out dx, al ; Set Mode + + mov dx, 0xA0 + mov al, 0x11 + out dx, al ; Init Command + mov dx, 0xA1 + mov al, 0xF8 + out dx, al ; Offset (Start of IDT Range) + mov al, 0x02 + out dx, al ; IRQ Line connected to master + mov al, 0x01 + out dx, al ; Set Mode + mov dl, 0x00 + out dx, al ; Set Mode + pop edx + + ret + + +; =============== +; = Define ISRs = +; =============== +%macro ISR_ERRNO 1 +[global Isr%1] +Isr%1: + ;xchg bx, bx + push %1 + jmp ErrorCommon +%endmacro +%macro ISR_NOERR 1 +[global Isr%1] +Isr%1: + ;xchg bx, bx + push 0 + push %1 + jmp ErrorCommon +%endmacro + +%macro DEF_SYSCALL 1 +[global Isr%1] +Isr%1: + push 0 + push %1 + jmp SyscallCommon +%endmacro + +%macro DEF_IRQ 1 +[global Isr%1] +Isr%1: + push 0 + push %1 + jmp IRQCommon +%endmacro + +ISR_NOERR 0; 0: Divide By Zero Exception +ISR_NOERR 1; 1: Debug Exception +ISR_NOERR 2; 2: Non Maskable Interrupt Exception +ISR_NOERR 3; 3: Int 3 Exception +ISR_NOERR 4; 4: INTO Exception +ISR_NOERR 5; 5: Out of Bounds Exception +ISR_NOERR 6; 6: Invalid Opcode Exception +ISR_NOERR 7; 7: Coprocessor Not Available Exception +ISR_ERRNO 8; 8: Double Fault Exception (With Error Code!) +ISR_NOERR 9; 9: Coprocessor Segment Overrun Exception +ISR_ERRNO 10; 10: Bad TSS Exception (With Error Code!) +ISR_ERRNO 11; 11: Segment Not Present Exception (With Error Code!) +ISR_ERRNO 12; 12: Stack Fault Exception (With Error Code!) +ISR_ERRNO 13; 13: General Protection Fault Exception (With Error Code!) +ISR_ERRNO 14; 14: Page Fault Exception (With Error Code!) +ISR_NOERR 15; 15: Reserved Exception +ISR_NOERR 16; 16: Floating Point Exception +ISR_NOERR 17; 17: Alignment Check Exception +ISR_NOERR 18; 18: Machine Check Exception +ISR_NOERR 19; 19: Reserved +ISR_NOERR 20; 20: Reserved +ISR_NOERR 21; 21: Reserved +ISR_NOERR 22; 22: Reserved +ISR_NOERR 23; 23: Reserved +ISR_NOERR 24; 24: Reserved +ISR_NOERR 25; 25: Reserved +ISR_NOERR 26; 26: Reserved +ISR_NOERR 27; 27: Reserved +ISR_NOERR 28; 28: Reserved +ISR_NOERR 29; 29: Reserved +ISR_NOERR 30; 30: Reserved +ISR_NOERR 31; 31: Reserved + +DEF_SYSCALL 0xAC ; Acess System Call + +%if USE_MP +[global Isr0xED] +; 0xED - Interprocessor HALT +Isr0xED: + cli +.jmp: hlt + jmp .jmp + +[global Isr0xEE] +[extern SchedulerBase] +; AP's Timer Interrupt +Isr0xEE: + push eax ; Line up with interrupt number + mov eax, dr1 ; CPU Number + push eax + mov eax, [esp+4] ; Load EAX back + jmp SchedulerBase +; Spurious Interrupt +[global Isr0xEF] +Isr0xEF: + xchg bx, bx ; MAGIC BREAK + iret +%endif + +; IRQs +; - Timer +[global Isr240] +[global Isr240.jmp] +[extern SchedulerBase] +[extern SetAPICTimerCount] +Isr240: + push 0 ; Line up with Argument in errors + push 0 ; CPU Number + ;xchg bx, bx ; MAGIC BREAK +Isr240.jmp: + %if USE_MP + jmp SetAPICTimerCount ; This is reset once the bus speed has been calculated + %else + jmp SchedulerBase + %endif +; - Assignable +%assign i 0xF1 +%rep 16 + DEF_IRQ i +%assign i i+1 +%endrep + +; --------------------- +; Common error handling +; --------------------- +[extern ErrorHandler] +ErrorCommon: + ;xchg bx, bx ; MAGIC BREAK + + pusha + push ds + push es + push fs + push gs + + ; Clear TF +; pushf +; and WORD [esp], 0xFEFF +; popf + + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + push esp + call ErrorHandler + add esp, 4 + + pop gs + pop fs + pop es + pop ds + popa + add esp, 8 ; Error Code and ID + iret + +; -------------------------- +; Common System Call Handler +; -------------------------- +[extern SyscallHandler] +SyscallCommon: + pusha + push ds + push es + push fs + push gs + + push esp + call SyscallHandler + add esp, 4 + + ; Pass changes to TF on to the user + ; EFLAGS is stored at ESP[4+8+2+2] + ; 4 Segment Registers + ; 8 GPRs + ; 2 Error Code / Interrupt ID + ; 2 CS/EIP + pushf + pop eax + and eax, 0x100 ; 0x100 = Trace Flag + and WORD [esp+(4+8+2+2)*4], ~0x100 ; Clear + or DWORD [esp+(4+8+2+2)*4], eax ; Set for user + + pop gs + pop fs + pop es + pop ds + popa + add esp, 8 ; Error Code and ID + iret + +; ------------ +; IRQ Handling +; ------------ +[extern IRQ_Handler] +[global IRQCommon] +[global IRQCommon_handled] +IRQCommon_handled equ IRQCommon.handled +IRQCommon: + pusha + push ds + push es + push fs + push gs + + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + push esp + call IRQ_Handler +.handled: + add esp, 4 + + pop gs + pop fs + pop es + pop ds + popa + add esp, 8 ; Error Code and ID + iret + +; vim: ft=nasm ts=8 diff --git a/KernelLand/Kernel/arch/x86/errors.c b/KernelLand/Kernel/arch/x86/errors.c new file mode 100644 index 00000000..d2d2cef1 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/errors.c @@ -0,0 +1,281 @@ +/* + * Acess2 - x86 Architecture + * arch/x86/errors.c + * - CPU Error Handler + */ +#include +#include +#include + +// === CONSTANTS === +#define MAX_BACKTRACE 8 //!< Maximum distance to trace the stack backwards + +// === IMPORTS === +extern void MM_PageFault(Uint Addr, Uint ErrorCode, tRegs *Regs); +extern void VM8086_GPF(tRegs *Regs); +extern void Threads_Dump(void); +extern void Threads_Fault(int Num); +extern int GetCPUNum(void); +extern void MM_DumpTables(tVAddr, tVAddr); +extern void Proc_EnableSSE(void); +extern void Proc_RestoreSSE(Uint32 Data); + +// === PROTOTYPES === +void __stack_chk_fail(void); +void ErrorHandler(tRegs *Regs); +void Proc_PrintBacktrace(void); +void Error_Backtrace(Uint eip, Uint ebp); +void StartupPrint(char *Str); + +// === GLOBALS === +const char *csaERROR_NAMES[] = { + "Divide By Zero", "Debug", "NMI Exception", "INT3", + "INTO Instr - Overflow", "BOUND Instr - Out of Bounds", "Invalid Opcode", "Coprocessor not avaliable", + "Double Fault", "Coprocessor Segment Overrun", "Bad TSS", "Segment Not Present", + "Stack Fault Exception", "GPF", "#PF", "Reserved", + "Floating Point Exception", "Alignment Check Exception", "Machine Check Exception", "Reserved", + "Reserved", "Reserved", "Reserved", "Reserved", + "Reserved", "Reserved", "Reserved", "Reserved", + "Reserved", "Reserved", "Reserved", "Reserved" + }; + +// === CODE === +/** + * \brief Keeps GCC happy + */ +void __stack_chk_fail(void) +{ + Panic("FATAL ERROR: Stack Check Failed\n"); + for(;;); +} + +/** + * \fn void ErrorHandler(tRegs *Regs) + * \brief General Error Handler + * \param Regs Register state at error + */ +void ErrorHandler(tRegs *Regs) +{ + Uint cr; + + //if( Regs && !(Regs->int_num == 13 && Regs->eflags & 0x20000) ) + // __asm__ __volatile__ ("xchg %bx, %bx"); + //Log_Debug("X86", "Regs = %p", Regs); + //Log_Debug("X86", "Error %i at 0x%08x", Regs->int_num, Regs->eip); + + __asm__ __volatile__ ("cli"); + + // Debug exception (used for single-stepping) + if(Regs->int_num == 1) + { + static Uint32 lastEIP = 0; + tThread *thread = Proc_GetCurThread(); + if( Regs->eip == lastEIP ) + return; + Log("%p(%i %s) IP=%08x", thread, thread->TID, thread->ThreadName, Regs->eip); + lastEIP = Regs->eip; + return ; + } + + // Page Fault + if(Regs->int_num == 14) + { + __asm__ __volatile__ ("sti"); // Should be OK, TODO: Test + __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr)); + MM_PageFault( cr, Regs->err_code, Regs ); + return ; + } + + // #NM - Coprocessor unavaliable + if(Regs->int_num == 7) + { + tThread *thread = Proc_GetCurThread(); + if(!thread->SavedState.bSSEModified) + { + Proc_EnableSSE(); + if(!thread->SavedState.SSE) + thread->SavedState.SSE = malloc(sizeof(tSSEState) + 0xF); + else + Proc_RestoreSSE( ((Uint)thread->SavedState.SSE + 0xF) & ~0xF ); + thread->SavedState.bSSEModified = 1; + __asm__ __volatile__ ("sti"); + return ; + } + // oops, SSE enabled but a #NM, bad news + } + + // VM8086 GPF + if(Regs->int_num == 13 && Regs->eflags & 0x20000) + { + VM8086_GPF(Regs); + return ; + } + + // Check if it's a user mode fault + if( (Regs->cs & 3) == 3 ) { + Log_Warning("Arch", "User Fault - %s, Code: 0x%x", + csaERROR_NAMES[Regs->int_num], Regs->err_code); + Log_Warning("Arch", "at CS:EIP %04x:%08x", + Regs->cs, Regs->eip); + MM_DumpTables(0, KERNEL_BASE); + switch( Regs->int_num ) + { + // Division by Zero + case 0: Threads_Fault(FAULT_DIV0); break; + // Invalid opcode + case 6: Threads_Fault(FAULT_OPCODE); break; + // GPF + case 13: Threads_Fault(FAULT_ACCESS); break; + // Floating Point Exception + case 16: Threads_Fault(FAULT_FLOAT); break; + + default: Threads_Fault(FAULT_MISC); break; + } + return ; + } + + Debug_KernelPanic(); + + LogF("CPU %i Error %i - %s, Code: 0x%x - At %08x\n", + GetCPUNum(), + Regs->int_num, csaERROR_NAMES[Regs->int_num], Regs->err_code, + Regs->eip); + + //Warning("CPU Error %i - %s, Code: 0x%x", + // Regs->int_num, csaERROR_NAMES[Regs->int_num], Regs->err_code); + //Warning(" CS:EIP = 0x%04x:%08x", Regs->cs, Regs->eip); + __ASM__ ("xchg %bx, %bx"); + if(Regs->cs == 0x08) + Warning(" SS:ESP = 0x0010:%08x", (Uint)Regs+sizeof(tRegs)); + else + Warning(" SS:ESP = 0x%04x:%08x", Regs->ss, Regs->esp); + Warning(" EFLAGS = 0x%08x", Regs->eflags); + Warning(" EAX %08x ECX %08x EDX %08x EBX %08x", + Regs->eax, Regs->ecx, Regs->edx, Regs->ebx); + Warning(" ESP %08x EBP %08x ESI %08x EDI %08x", + Regs->esp, Regs->ebp, Regs->esi, Regs->edi); + Warning(" DS %04x ES %04x FS %04x GS %04x", + Regs->ds, Regs->es, Regs->fs, Regs->gs); + + // Control Registers + __asm__ __volatile__ ("mov %%cr0, %0":"=r"(cr)); + Warning(" CR0 0x%08x", cr); + __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr)); + Warning(" CR2 0x%08x", cr); + __asm__ __volatile__ ("mov %%cr3, %0":"=r"(cr)); + Warning(" CR3 0x%08x", cr); + + switch( Regs->int_num ) + { + case 6: // #UD + Warning(" Offending bytes: %02x %02x %02x %02x", + *(Uint8*)(Regs->eip+0), *(Uint8*)(Regs->eip+1), + *(Uint8*)(Regs->eip+2), *(Uint8*)(Regs->eip+3)); + break; + } + + // Print Stack Backtrace + Error_Backtrace(Regs->eip, Regs->ebp); + + // Dump running threads + Threads_Dump(); + + for(;;) __asm__ __volatile__ ("hlt"); +} + +void Proc_PrintBacktrace(void) +{ + Uint32 ebp; + __asm__ __volatile__ ("mov %%ebp, %0" : "=r" (ebp)); + Error_Backtrace( *(Uint32*)(ebp+4), *(Uint32*)ebp ); +} + +/** + * \fn void Error_Backtrace(Uint eip, Uint ebp) + * \brief Unrolls the stack to trace execution + * \param eip Current Instruction Pointer + * \param ebp Current Base Pointer (Stack Frame) + */ +void Error_Backtrace(Uint eip, Uint ebp) +{ + int i = 0; +// Uint delta = 0; +// char *str = NULL; + + //if(eip < 0xC0000000 && eip > 0x1000) + //{ + // LogF("Backtrace: User - 0x%x\n", eip); + // return; + //} + + #if 0 + if(eip > 0xE0000000) + { + LogF("Backtrace: Data Area - 0x%x\n", eip); + return; + } + + if(eip > 0xC8000000) + { + LogF("Backtrace: Kernel Module - 0x%x\n", eip); + return; + } + #endif + + //str = Debug_GetSymbol(eip, &delta); +// if(str == NULL) + LogF("Backtrace: 0x%x", eip); +// else +// LogF("Backtrace: %s+0x%x", str, delta); + if(!MM_GetPhysAddr(ebp)) + { + LogF("\nBacktrace: Invalid EBP, stopping\n"); + return; + } + + + while( MM_GetPhysAddr(ebp) && i < MAX_BACKTRACE ) + { + if( ebp >= MM_KERNEL_STACKS_END ) break; + //str = Debug_GetSymbol(*(Uint*)(ebp+4), &delta); +// if(str == NULL) + LogF(" >> 0x%x", *(Uint*)(ebp+4)); +// else +// LogF(" >> %s+0x%x", str, delta); + ebp = *(Uint*)ebp; + i++; + } + LogF("\n"); +} + +/** + * \fn void StartupPrint(char *Str) + * \brief Str String to print + * \note WHY IS THIS HERE?!?! + */ +void StartupPrint(char *Str) +{ + Uint16 *buf = (void*)0xC00B8000; + int i = 0; + static int line = 0; + while(*Str) + { + buf[line*80 + i++] = *Str | 0x0700; + Str ++; + } + + // Clear the rest of the line + while(i < 80) + buf[line*80 + i++] = 0x0720; + + line ++; + if(line == 25) + { + line --; + memcpy(buf, &buf[80], 80*24*2); + memset(&buf[80*24], 0, 80*2); + } +} + +// === EXPORTS === +EXPORT(__stack_chk_fail); diff --git a/KernelLand/Kernel/arch/x86/include/arch.h b/KernelLand/Kernel/arch/x86/include/arch.h new file mode 100644 index 00000000..8a987058 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/include/arch.h @@ -0,0 +1,127 @@ +/* + * Acess2 + * - x86 Architecture + * arch/x86/include/arch.h + */ +#ifndef _ARCH_H_ +#define _ARCH_H_ + +// - Base Defintions +#define KERNEL_BASE 0xC0000000 +#define BITS 32 +#define PAGE_SIZE 0x1000 + +#define INVLPTR ((void*)-1) + +// Allow nested spinlocks? +#define LOCK_DISABLE_INTS 1 + +// - Processor/Machine Specific Features +#if ARCH != x86 && ARCH != x86_smp +# error "Unknown architecture '" #ARCH "'" +#endif + +#if USE_MP +# define MAX_CPUS 8 +#else +# define MAX_CPUS 1 +#endif + +#if USE_PAE +# define PHYS_BITS 48 +#else +# define PHYS_BITS 32 +#endif + +#define __ASM__ __asm__ __volatile__ + +// === Spinlocks === +/** + * \brief Short Spinlock structure + */ +struct sShortSpinlock { + volatile int Lock; //!< Lock value + + #if LOCK_DISABLE_INTS + int IF; //!< Interrupt state on call to SHORTLOCK + #endif +}; + +// === MACROS === +/** + * \brief Halt the CPU (shorter version of yield) + */ +#if 1 +#define HALT() do { \ + Uint32 flags; \ + __asm__ __volatile__ ("pushf;pop %0" : "=a"(flags)); \ + if( !(flags & 0x200) ) Panic("HALT called with interrupts disabled"); \ + __asm__ __volatile__ ("hlt"); \ +} while(0) +#else +#define HALT() __asm__ __volatile__ ("hlt") +#endif +/** + * \brief Fire a magic breakpoint (bochs) + */ +#define MAGIC_BREAK() __asm__ __volatile__ ("xchg %bx, %bx") + +// === TYPES === +typedef unsigned int Uint; // Unsigned machine native integer +typedef unsigned char Uint8; +typedef unsigned short Uint16; +typedef unsigned long Uint32; +typedef unsigned long long Uint64; +typedef signed int Sint; // Signed Machine Native integer +typedef signed char Sint8; +typedef signed short Sint16; +typedef signed long Sint32; +typedef signed long long Sint64; +typedef Uint size_t; +typedef char BOOL; + +typedef Uint32 tPAddr; +typedef Uint32 tVAddr; + +typedef struct { + Uint32 gs, fs, es, ds; + Uint32 edi, esi, ebp, kesp; + Uint32 ebx, edx, ecx, eax; + Uint32 int_num, err_code; + Uint32 eip, cs; + Uint32 eflags, esp, ss; +} tRegs; + +typedef struct { + Uint Resvd1[4]; // GS, FS, ES, DS + Uint Arg4, Arg5; // EDI, ESI + Uint Arg6; // EBP + Uint Resvd2[1]; // Kernel ESP + union { + Uint Arg1; + Uint Error; + }; // EBX + union { + Uint Arg3; + Uint RetHi; // High 32 bits of ret + }; // EDX + Uint Arg2; // ECX + union { + Uint Num; + Uint Return; + }; // EAX + Uint Resvd3[5]; // Int, Err, Eip, CS, ... + Uint StackPointer; // ESP + Uint Resvd4[1]; // SS +} tSyscallRegs; + +// === FUNCTIONS === +extern void Debug_PutCharDebug(char ch); +extern void Debug_PutStringDebug(const char *String); + +extern int IS_LOCKED(struct sShortSpinlock *Lock); +extern int CPU_HAS_LOCK(struct sShortSpinlock *Lock); +extern void SHORTLOCK(struct sShortSpinlock *Lock); +extern void SHORTREL(struct sShortSpinlock *Lock); + +#endif // !defined(_ARCH_H_) diff --git a/KernelLand/Kernel/arch/x86/include/arch_int.h b/KernelLand/Kernel/arch/x86/include/arch_int.h new file mode 100644 index 00000000..81ea2d77 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/include/arch_int.h @@ -0,0 +1,22 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * x86 Arch - Internal Definitions + * - arch/x86/include/arch_int.h + */ +#ifndef _ARCH_INT_H_ +#define _ARCH_INT_H_ + +/** + * \brief Spinlock primative atomic set-if-zero loop + */ +extern void __AtomicTestSetLoop(Uint *Ptr, Uint Value); + +/** + * \brief Clear and free an address space + */ +extern void MM_ClearSpace(Uint32 CR3); + +#endif + diff --git a/KernelLand/Kernel/arch/x86/include/desctab.h b/KernelLand/Kernel/arch/x86/include/desctab.h new file mode 100644 index 00000000..47a462aa --- /dev/null +++ b/KernelLand/Kernel/arch/x86/include/desctab.h @@ -0,0 +1,25 @@ +/** + */ +#ifndef _DESCTAB_H_ +#define _DESCTAB_H_ + +typedef struct { + Uint16 LimitLow; + Uint16 BaseLow; + Uint8 BaseMid; + Uint8 Access; + struct { + unsigned LimitHi: 4; + unsigned Flags: 4; + } __attribute__ ((packed)); + Uint8 BaseHi; +} __attribute__ ((packed)) tGDT; + +typedef struct { + Uint16 OffsetLo; + Uint16 CS; + Uint16 Flags; + Uint16 OffsetHi; +} __attribute__ ((packed)) tIDT; + +#endif diff --git a/KernelLand/Kernel/arch/x86/include/mm_phys.h b/KernelLand/Kernel/arch/x86/include/mm_phys.h new file mode 100644 index 00000000..96d3e64a --- /dev/null +++ b/KernelLand/Kernel/arch/x86/include/mm_phys.h @@ -0,0 +1,15 @@ +/* + * AcessOS Microkernel Version + * mm_phys.h + */ +#ifndef _MM_PHYS_H +#define _MM_PHYS_H + +// === FUNCTIONS === +//extern tPAddr MM_AllocPhys(void); +//extern tPAddr MM_AllocPhysRange(int Pages, int MaxBits); +//extern void MM_RefPhys(tPAddr PAddr); +//extern void MM_DerefPhys(tPAddr PAddr); +//extern int MM_GetRefCount(tPAddr Addr); + +#endif diff --git a/KernelLand/Kernel/arch/x86/include/mm_virt.h b/KernelLand/Kernel/arch/x86/include/mm_virt.h new file mode 100644 index 00000000..d84c9650 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/include/mm_virt.h @@ -0,0 +1,53 @@ +/* + * Acess2 + * - Virtual Memory Manager (Header) + */ +#ifndef _MM_VIRT_H +#define _MM_VIRT_H + +// NOTES: +// - 1PD is 0x400000 + +// - Memory Layout +#define MM_USER_MIN 0x00200000 +#define USER_STACK_SZ 0x00020000 // 128 KiB +#define USER_STACK_TOP 0x00800000 +#define USER_LIB_MAX 0xBC000000 +#define MM_USER_MAX 0xBC000000 // Top load address for user libraries +#define MM_PPD_MIN 0xBC000000 // Per-Process Data base +#define MM_PPD_HANDLES 0xBC000000 // - VFS Handles (Practically unlimited) +#define MM_PPD_MMAP 0xBD000000 // - MMap Entries (24b each = 0x2AAAA max) +#define MM_PPD_UNALLOC 0xBE000000 // +#define MM_PPD_CFG 0xBFFFF000 // - Per-process config entries +#define MM_PPD_MAX 0xC0000000 // + +#define MM_KHEAP_BASE 0xC0400000 // C+4MiB +#define MM_KHEAP_MAX 0xCF000000 // +#define MM_KERNEL_VFS 0xCF000000 // +#define MM_KUSER_CODE 0xCFFF0000 // 16 Pages +#define MM_MODULE_MIN 0xD0000000 // Lowest Module Address +#define MM_MODULE_MAX 0xE0000000 // 128 MiB + +// Page Info (Which VFS node owns each physical page) +// 2^32/2^12*16 +// = 2^24 = 16 MiB = 0x4000000 +// 256 items per page +#define MM_PAGENODE_BASE 0xE0000000 + +// Needs (2^32/2^12*4 bytes) +// - 2^22 bytes max = 4 MiB = 0x1000000 +// 1024 items per page +#define MM_REFCOUNT_BASE 0xE4000000 + +#define MM_KERNEL_STACKS 0xF0000000 +#define MM_KERNEL_STACK_SIZE 0x00008000 +#define MM_KERNEL_STACKS_END 0xFC000000 + +// === FUNCTIONS === +extern void MM_FinishVirtualInit(void); +extern void MM_SetCR3(Uint CR3); +extern tPAddr MM_Clone(int bCloneUser); +extern tVAddr MM_NewKStack(void); +extern tVAddr MM_NewWorkerStack(Uint *InitialStack, size_t StackSize); + +#endif diff --git a/KernelLand/Kernel/arch/x86/include/mp.h b/KernelLand/Kernel/arch/x86/include/mp.h new file mode 100644 index 00000000..0dd5a1e7 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/include/mp.h @@ -0,0 +1,140 @@ +/* + */ +#ifndef _MP_H +#define _MP_H + +#define MPPTR_IDENT ('_'|('M'<<8)|('P'<<16)|('_'<<24)) +#define MPTABLE_IDENT ('P'|('C'<<8)|('M'<<16)|('P'<<24)) + +typedef struct sMPInfo { + Uint32 Sig; // '_MP_' + Uint32 MPConfig; + Uint8 Length; + Uint8 Version; + Uint8 Checksum; + Uint8 Features[5]; // 2-4 are unused +} tMPInfo; + +typedef union uMPTable_Ent { + Uint8 Type; + struct { + Uint8 Type; + Uint8 APICID; + Uint8 APICVer; + Uint8 CPUFlags; // bit 0: Enabled, bit 1: Boot Processor + Uint32 CPUSignature; // Stepping, Model, Family + Uint32 FeatureFlags; + Uint32 Reserved[2]; + } __attribute__((packed)) Proc; // 0x00 + struct { + Uint8 Type; + Uint8 ID; + char TypeString[6]; + } __attribute__((packed)) Bus; // 0x01 + struct { + Uint8 Type; + Uint8 ID; + Uint8 Version; + Uint8 Flags; // bit 0: Enabled + Uint32 Addr; + } __attribute__((packed)) IOAPIC; // 0x02 + struct { + Uint8 Type; + Uint8 IntType; + Uint16 Flags; // 0,1: Polarity, 2,3: Trigger Mode + Uint8 SourceBusID; + Uint8 SourceBusIRQ; + Uint8 DestAPICID; + Uint8 DestAPICIRQ; + } __attribute__((packed)) IOInt; + struct { + Uint8 Type; + Uint8 IntType; + Uint16 Flags; // 0,1: Polarity, 2,3: Trigger Mode + Uint8 SourceBusID; + Uint8 SourceBusIRQ; + Uint8 DestLocalAPICID; + Uint8 DestLocalAPICIRQ; + } __attribute__((packed)) LocalInt; +} __attribute__((packed)) tMPTable_Ent; + +typedef struct sMPTable { + Uint32 Sig; + Uint16 BaseTableLength; + Uint8 SpecRev; + Uint8 Checksum; + + char OemID[8]; + char ProductID[12]; + + Uint32 OEMTablePtr; + Uint16 OEMTableSize; + Uint16 EntryCount; + + Uint32 LocalAPICMemMap; //!< Address used to access the local APIC + Uint16 ExtendedTableLen; + Uint8 ExtendedTableChecksum; + Uint8 Reserved; + + tMPTable_Ent Entries[]; +} tMPTable; + +typedef volatile struct { + Uint32 Addr; + Uint32 Resvd1[3]; + union { + Uint8 Byte; + Uint16 Word; + Uint32 DWord; + } Value; + Uint32 Resvd2[3]; +} tIOAPIC; + +typedef struct { + Uint32 Val; + Uint32 Padding[3]; +} volatile tReg; + +typedef volatile struct { + tReg _unused1[2]; + tReg ID; + tReg Version; + tReg _unused2[4]; + tReg TPR; // Task Priority Register + tReg APR; // Arbitration Priority Register (RO) + tReg PPR; // Processor Priority Register (RO) + tReg EOI; // EOI Register (Write Only) + tReg _unused3[1]; + tReg LogDest; // Logical Destination Register + tReg DestFmt; // Destination Format Register (0-27: RO, 28-31: RW) + tReg SIV; // Spurious Interrupt Vector Register (0-8: RW, 9-31: RO) + tReg ISR[8]; // In-Service Register - Total 256 Bits (RO) + tReg TMR[8]; // Trigger Mode Register - Total 256 Bits (RO) + tReg IRR[8]; // Interrupt Request Register - Total 256 Bits (RO) + tReg ErrorStatus; // Error Status Register (RO) + tReg _unused4[6]; + tReg LVTCMI; // LVT CMI Registers + // 0x300 + tReg ICR[2]; // Interrupt Command Register (RW) + // LVT Registers (Controls Local Vector Table) + // Structure: + // 0-7: Vector - IDT Vector for the interrupt + // 12: Delivery Status (0: Idle, 1: Send Pending) + // 16: Mask (0: Enabled, 1: Disabled) + // 0x320 + tReg LVTTimer; // LVT Timer Register (RW) + tReg LVTThermalSensor; // LVT Thermal Sensor Register (RW) + tReg LVTPerfMonCounters; // LVT Performance Monitor Counters Register (RW) + tReg LVTLInt0; // LVT Local Interrupt (LINT) #0 Register (RW); + tReg LVTLInt1; // LVT Local Interrupt (LINT) #1 Register (RW); + tReg LVTError; // LVT Error Register (RW); + // 0x380 + tReg InitialCount; // Initial Count Register (Used for the timer) (RW) + tReg CurrentCount; // Current Count Register (Used for the timer) (RW) + tReg _unused5[4]; + // 0x3E0 + tReg DivideConifg; // Divide Configuration Register (RW) + tReg _unused6[1]; +} volatile tAPIC; + +#endif diff --git a/KernelLand/Kernel/arch/x86/include/multiboot2.h b/KernelLand/Kernel/arch/x86/include/multiboot2.h new file mode 100644 index 00000000..735109a2 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/include/multiboot2.h @@ -0,0 +1,19 @@ +/* + * Acess 2 + * By John Hodge (thePowersGang) + * + * multiboot2.h + * - Multiboot 2 Header + */ +#ifndef _MULTIBOOT2_H_ +#define _MULTIBOOT2_H_ + +#define MULTIBOOT2_MAGIC 0x36D76289 + +typedef struct sMultiboot2Info +{ + Uint32 TotalSize; + Uint32 Reserved; // SBZ +} tMultiboot2Info; + +#endif diff --git a/KernelLand/Kernel/arch/x86/include/proc.h b/KernelLand/Kernel/arch/x86/include/proc.h new file mode 100644 index 00000000..9ecd98b5 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/include/proc.h @@ -0,0 +1,49 @@ +/* + * AcessOS Microkernel Version + * proc.h + */ +#ifndef _PROC_H +#define _PROC_H + +// === TYPES == +typedef struct sTSS { + Uint32 Link; + Uint32 ESP0, SS0; + Uint32 ESP1, SS1; + Uint32 ESP2, SS2; + Uint32 CR3; + Uint32 EIP; + Uint32 EFLAGS; + Uint32 EAX, ECX, EDX, EBX; + Uint32 ESP, EBP, ESI, EDI; + Uint32 ES, CS, DS, SS, FS, GS; + Uint32 LDTR; + Uint16 Resvd, IOPB; // IO Permissions Bitmap +} __attribute__((packed)) tTSS; + +typedef struct { + #if USE_PAE + Uint PDPT[4]; + #else + Uint32 CR3; + #endif +} tMemoryState; + +// 512 bytes, 16 byte aligned +typedef struct sSSEState +{ + char data[512]; +} tSSEState; + +typedef struct { + Uint EIP, ESP; + Uint32 UserCS, UserEIP; + tSSEState *SSE; + int bSSEModified; +} tTaskState; + +#include + +#define USER_MAX KERNEL_BASE + +#endif diff --git a/KernelLand/Kernel/arch/x86/include/vm8086.h b/KernelLand/Kernel/arch/x86/include/vm8086.h new file mode 100644 index 00000000..7c8dd47d --- /dev/null +++ b/KernelLand/Kernel/arch/x86/include/vm8086.h @@ -0,0 +1,67 @@ +/* + * Acess2 VM8086 BIOS Interface + * - By John Hodge (thePowersGang) + * + * vm8086.h + * - Core Header + */ +#ifndef _VM80806_H_ +#define _VM80806_H_ + +// === TYPES === +/** + * \note Semi-opaque - Past \a .IP, the implementation may add any data + * it needs to the state. + */ +typedef struct sVM8086 +{ + Uint16 AX, CX, DX, BX; + Uint16 BP, SP, SI, DI; + + Uint16 SS, DS, ES; + + Uint16 CS, IP; + + struct sVM8086_InternalData *Internal; +} tVM8086; + +// === FUNCTIONS === +/** + * \brief Create an instance of the VM8086 Emulator + * \note Do not free this pointer with ::free, instead use ::VM8086_Free + * \return Pointer to a tVM8086 structure, this structure may be larger than + * tVM8086 due to internal data. + */ +extern tVM8086 *VM8086_Init(void); +/** + * \brief Free an allocated tVM8086 structure + * \param State Emulator state to free + */ +extern void VM8086_Free(tVM8086 *State); +/** + * \brief Allocate a piece of memory in the emulated address space and + * return a host and emulated pointer to it. + * \param State Emulator state + * \param Size Size of memory block + * \param Segment Pointer to location to store the allocated memory's segment + * \param Offset Pointet to location to store the allocated memory's offset + * \return Host pointer to the allocated memory + */ +extern void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset); +/** + * \brief Gets a pointer to a piece of emulated memory + * \todo Only 1 machine page is garenteed to be contiguous + * \param State Emulator State + * \param Segment Source Segment + * \param Offset Source Offset + * \return Host pointer to the emulated memory + */ +extern void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset); +/** + * \brief Calls a real-mode interrupt described by the current state of the IVT. + * \param State Emulator State + * \param Interrupt BIOS Interrupt to call + */ +extern void VM8086_Int(tVM8086 *State, Uint8 Interrupt); + +#endif diff --git a/KernelLand/Kernel/arch/x86/irq.c b/KernelLand/Kernel/arch/x86/irq.c new file mode 100644 index 00000000..e2dcf0e3 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/irq.c @@ -0,0 +1,68 @@ +/* + * AcessOS Microkernel Version + * irq.c + */ +#include + +// === CONSTANTS === +#define MAX_CALLBACKS_PER_IRQ 4 +#define TRACE_IRQS 0 + +// === TYPES === +typedef void (*tIRQ_Callback)(int, void *); + +// === PROTOTYPES === +void IRQ_Handler(tRegs *Regs); + +// === GLOBALS === +tIRQ_Callback gIRQ_Handlers[16][MAX_CALLBACKS_PER_IRQ]; +void *gaIRQ_DataPointers[16][MAX_CALLBACKS_PER_IRQ]; + +// === CODE === +/** + * \fn void IRQ_Handler(tRegs *Regs) + * \brief Handle an IRQ + */ +void IRQ_Handler(tRegs *Regs) +{ + int i, irq = Regs->int_num - 0xF0; + + //Log("IRQ_Handler: (Regs={int_num:%i})", Regs->int_num); + + for( i = 0; i < MAX_CALLBACKS_PER_IRQ; i++ ) + { + if( gIRQ_Handlers[irq][i] ) { + gIRQ_Handlers[irq][i](irq, gaIRQ_DataPointers[irq][i]); + #if TRACE_IRQS + if( irq != 8 ) + Log("IRQ %i: Call %p", Regs->int_num, gIRQ_Handlers[Regs->int_num][i]); + #endif + } + } + + //Log(" IRQ_Handler: Resetting"); + if(irq >= 8) + outb(0xA0, 0x20); // ACK IRQ (Secondary PIC) + outb(0x20, 0x20); // ACK IRQ + //Log("IRQ_Handler: RETURN"); +} + +/** + * \fn int IRQ_AddHandler( int Num, void (*Callback)(int) ) + */ +int IRQ_AddHandler( int Num, void (*Callback)(int, void*), void *Ptr ) +{ + int i; + for( i = 0; i < MAX_CALLBACKS_PER_IRQ; i++ ) + { + if( gIRQ_Handlers[Num][i] == NULL ) { + Log_Log("IRQ", "Added IRQ%i Cb#%i %p", Num, i, Callback); + gIRQ_Handlers[Num][i] = Callback; + gaIRQ_DataPointers[Num][i] = Ptr; + return 1; + } + } + + Log_Warning("IRQ", "No free callbacks on IRQ%i", Num); + return 0; +} diff --git a/KernelLand/Kernel/arch/x86/kpanic.c b/KernelLand/Kernel/arch/x86/kpanic.c new file mode 100644 index 00000000..9300c69e --- /dev/null +++ b/KernelLand/Kernel/arch/x86/kpanic.c @@ -0,0 +1,154 @@ +/* + * Acess 2 Kernel + * - By John Hodge (thePowersGang) + * + * kpanic.c + * - x86 Kernel Panic Handler + */ + +#include +#include + +// === CONSTANTS === +#define FB ((Uint16 *)(KERNEL_BASE|0xB8000)) +#define BGC 0x4F00 // White on Red +//#define BGC 0xC000 // Black on Bright Red +//#define BGC 0x1F00 // White on Blue (BSOD!) + +// === IMPORTS === +extern Uint32 GetEIP(void); +extern void Error_Backtrace(Uint32 eip, Uint32 ebp); +#if USE_MP +extern void MP_SendIPIVector(int CPU, Uint8 Vector); +extern int giNumCPUs; +extern int GetCPUNum(void); +#endif + +// === PROTOTYPES === +void KernelPanic_SetMode(void); +void KernelPanic_PutChar(char Ch); + +// === CONSTANTS === +const struct { + Uint16 IdxPort; + Uint16 DatPort; + Uint8 Index; + Uint8 Value; +} caRegValues[] = { + //{0x3C0, 0x3C0, 0x10, 0x0C}, // Mode Control (Blink Enabled) + {0x3C0, 0x3C0, 0x10, 0x04}, // Mode Control (Blink Disabled) + {0x3C0, 0x3C0, 0x11, 0x00}, // Overscan Register + {0x3C0, 0x3C0, 0x12, 0x0F}, // Color Plane Enable + {0x3C0, 0x3C0, 0x13, 0x08}, // Horizontal Panning + {0x3C0, 0x3C0, 0x14, 0x00}, // Color Select + {0 , 0x3C2, 0 , 0x67}, // Miscellaneous Output Register + {0x3C4, 0x3C5, 0x01, 0x00}, // Clock Mode Register + {0x3C4, 0x3C5, 0x03, 0x00}, // Character select + {0x3C4, 0x3C5, 0x04, 0x07}, // Memory Mode Register + {0x3CE, 0x3CF, 0x05, 0x10}, // Mode Register + {0x3CE, 0x3CF, 0x06, 0x0E}, // Miscellaneous Register + {0x3D4, 0x3D5, 0x00, 0x5F}, // Horizontal Total + {0x3D4, 0x3D5, 0x01, 0x4F}, // Horizontal Display Enable End + {0x3D4, 0x3D5, 0x02, 0x50}, // Horizontal Blank Start + {0x3D4, 0x3D5, 0x03, 0x82}, // Horizontal Blank End + {0x3D4, 0x3D5, 0x04, 0x55}, // Horizontal Retrace Start + {0x3D4, 0x3D5, 0x05, 0x81}, // Horizontal Retrace End + {0x3D4, 0x3D5, 0x06, 0xBF}, // Vertical Total + {0x3D4, 0x3D5, 0x07, 0x1F}, // Overflow Register + {0x3D4, 0x3D5, 0x08, 0x00}, // Preset row scan + {0x3D4, 0x3D5, 0x09, 0x4F}, // Maximum Scan Line + {0x3D4, 0x3D5, 0x10, 0x9C}, // Vertical Retrace Start + {0x3D4, 0x3D5, 0x11, 0x8E}, // Vertical Retrace End + {0x3D4, 0x3D5, 0x12, 0x8F}, // Vertical Display Enable End + {0x3D4, 0x3D5, 0x13, 0x28}, // Logical Width + {0x3D4, 0x3D5, 0x14, 0x1F}, // Underline Location + {0x3D4, 0x3D5, 0x15, 0x96}, // Vertical Blank Start + {0x3D4, 0x3D5, 0x16, 0xB9}, // Vertical Blank End + {0x3D4, 0x3D5, 0x17, 0xA3} // CRTC Mode Control +}; +#define NUM_REGVALUES (sizeof(caRegValues)/sizeof(caRegValues[0])) + +// === GLOBALS === + int giKP_Pos = 0; + +// === CODE === +/** + * \brief Sets the screen mode for a kernel panic + */ +void KernelPanic_SetMode(void) +{ + int i; + + // This function is called by Panic(), but MM_PageFault and the + // CPU exception handers also call it, so let's not clear the screen + // twice + if( giKP_Pos ) return ; + + // Restore VGA 0xB8000 text mode + #if 1 + for( i = 0; i < NUM_REGVALUES; i++ ) + { + // Reset Flip-Flop + if( caRegValues[i].IdxPort == 0x3C0 ) inb(0x3DA); + + if( caRegValues[i].IdxPort ) + outb(caRegValues[i].IdxPort, caRegValues[i].Index); + outb(caRegValues[i].DatPort, caRegValues[i].Value); + } + + inb(0x3DA); + outb(0x3C0, 0x20); + #endif + + #if USE_MP + // Send halt to all processors + for( i = 0; i < giNumCPUs; i ++ ) + { + if(i == GetCPUNum()) continue ; + FB[i] = BGC|('A'+i); + MP_SendIPIVector(i, 0xED); + } + #endif + + // Clear Screen + for( i = 0; i < 80*25; i++ ) + { + FB[i] = BGC; + } + + { + Uint32 eip = GetEIP(); + Uint32 ebp; + __asm__ __volatile__ ("mov %%ebp, %0" : "=r" (ebp)); + Error_Backtrace(eip, ebp); + } +} + +void KernelPanic_PutChar(char Ch) +{ + if( giKP_Pos > 80*25 ) return ; + switch(Ch) + { + case '\t': + do { + FB[giKP_Pos] &= 0xFF00; + FB[giKP_Pos++] |= ' '; + } while(giKP_Pos & 7); + break; + + case '\n': + giKP_Pos += 80; + case '\r': + giKP_Pos -= giKP_Pos % 80; + break; + + default: + if(' ' <= Ch && Ch < 0x7F) + { + FB[giKP_Pos] &= 0xFF00; + FB[giKP_Pos] |= Ch; + } + giKP_Pos ++; + break; + } +} diff --git a/KernelLand/Kernel/arch/x86/lib.c b/KernelLand/Kernel/arch/x86/lib.c new file mode 100644 index 00000000..175f9a55 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/lib.c @@ -0,0 +1,430 @@ +/* + * Acess2 + * + * arch/x86/lib.c + * - General arch-specific stuff + */ +#include +#include +#include +#include // GetCPUNum + +#define TRACE_LOCKS 0 + +#define DEBUG_TO_E9 1 +#define DEBUG_TO_SERIAL 1 +#define SERIAL_PORT 0x3F8 +#define GDB_SERIAL_PORT 0x2F8 + +// === IMPRORTS === +#if TRACE_LOCKS +extern struct sShortSpinlock glDebug_Lock; +extern tMutex glPhysAlloc; +#define TRACE_LOCK_COND (Lock != &glDebug_Lock && Lock != &glThreadListLock && Lock != &glPhysAlloc.Protector) +//#define TRACE_LOCK_COND (Lock != &glDebug_Lock && Lock != &glPhysAlloc.Protector) +#endif + +// === PROTOTYPES == +Uint64 __divmod64(Uint64 Num, Uint64 Den, Uint64 *Rem); +Uint64 __udivdi3(Uint64 Num, Uint64 Den); +Uint64 __umoddi3(Uint64 Num, Uint64 Den); + +// === GLOBALS === + int gbDebug_SerialSetup = 0; + int gbGDB_SerialSetup = 0; + +// === CODE === +/** + * \brief Determine if a short spinlock is locked + * \param Lock Lock pointer + */ +int IS_LOCKED(struct sShortSpinlock *Lock) +{ + return !!Lock->Lock; +} + +/** + * \brief Check if the current CPU has the lock + * \param Lock Lock pointer + */ +int CPU_HAS_LOCK(struct sShortSpinlock *Lock) +{ + return Lock->Lock == GetCPUNum() + 1; +} + +void __AtomicTestSetLoop(Uint *Ptr, Uint Value) +{ + __ASM__( + "1:\n\t" + "xor %%eax, %%eax;\n\t" + "lock cmpxchgl %0, (%1);\n\t" + "jnz 1b;\n\t" + :: "r"(Value), "r"(Ptr) + : "eax" // EAX clobbered + ); +} +/** + * \brief Acquire a Short Spinlock + * \param Lock Lock pointer + * + * This type of mutex should only be used for very short sections of code, + * or in places where a Mutex_* would be overkill, such as appending + * an element to linked list (usually two assignement lines in C) + * + * \note This type of lock halts interrupts, so ensure that no timing + * functions are called while it is held. As a matter of fact, spend as + * little time as possible with this lock held + * \note If \a STACKED_LOCKS is set, this type of spinlock can be nested + */ +void SHORTLOCK(struct sShortSpinlock *Lock) +{ + int IF; + int cpu = GetCPUNum() + 1; + + // Save interrupt state + __ASM__ ("pushf;\n\tpop %0" : "=r"(IF)); + IF &= 0x200; // AND out all but the interrupt flag + + #if TRACE_LOCKS + if( TRACE_LOCK_COND ) + { + //Log_Log("LOCK", "%p locked by %p", Lock, __builtin_return_address(0)); + Debug("%i %p obtaining %p (Called by %p)", cpu-1, __builtin_return_address(0), Lock, __builtin_return_address(1)); + } + #endif + + __ASM__("cli"); + + // Wait for another CPU to release + __AtomicTestSetLoop( (Uint*)&Lock->Lock, cpu ); + Lock->IF = IF; + + #if TRACE_LOCKS + if( TRACE_LOCK_COND ) + { + //Log_Log("LOCK", "%p locked by %p", Lock, __builtin_return_address(0)); + Debug("%i %p locked by %p\t%p", cpu-1, Lock, __builtin_return_address(0), __builtin_return_address(1)); +// Debug("got it"); + } + #endif +} +/** + * \brief Release a short lock + * \param Lock Lock pointer + */ +void SHORTREL(struct sShortSpinlock *Lock) +{ + #if TRACE_LOCKS + if( TRACE_LOCK_COND ) + { + //Log_Log("LOCK", "%p released by %p", Lock, __builtin_return_address(0)); + Debug("Lock %p released by %p\t%p", Lock, __builtin_return_address(0), __builtin_return_address(1)); + } + #endif + + // Lock->IF can change anytime once Lock->Lock is zeroed + if(Lock->IF) { + Lock->Lock = 0; + __ASM__ ("sti"); + } + else { + Lock->Lock = 0; + } +} + +// === DEBUG IO === +#if USE_GDB_STUB +int putDebugChar(char ch) +{ + if(!gbGDB_SerialSetup) { + outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts + outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) + outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud + outb(GDB_SERIAL_PORT + 1, 0x00); // (base is (hi byte) + outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit (8N1) + outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it + outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set + gbGDB_SerialSetup = 1; + } + while( (inb(GDB_SERIAL_PORT + 5) & 0x20) == 0 ); + outb(GDB_SERIAL_PORT, ch); + return 0; +} +int getDebugChar(void) +{ + if(!gbGDB_SerialSetup) { + outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts + outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) + outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud + outb(GDB_SERIAL_PORT + 1, 0x00); // (hi byte) + outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit + outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it + outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set + gbGDB_SerialSetup = 1; + } + while( (inb(GDB_SERIAL_PORT + 5) & 1) == 0) ; + return inb(GDB_SERIAL_PORT); +} +#endif /* USE_GDB_STUB */ + +void Debug_PutCharDebug(char ch) +{ + #if DEBUG_TO_E9 + __asm__ __volatile__ ( "outb %%al, $0xe9" :: "a"(((Uint8)ch)) ); + #endif + + #if DEBUG_TO_SERIAL + if(!gbDebug_SerialSetup) { + outb(SERIAL_PORT + 1, 0x00); // Disable all interrupts + outb(SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) + outb(SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud + outb(SERIAL_PORT + 1, 0x00); // (hi byte) + outb(SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit + outb(SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it + outb(SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set + gbDebug_SerialSetup = 1; + } + while( (inb(SERIAL_PORT + 5) & 0x20) == 0 ); + outb(SERIAL_PORT, ch); + #endif +} + +void Debug_PutStringDebug(const char *String) +{ + while(*String) + Debug_PutCharDebug(*String++); +} + +// === IO Commands === +void outb(Uint16 Port, Uint8 Data) +{ + __asm__ __volatile__ ("outb %%al, %%dx"::"d"(Port),"a"(Data)); +} +void outw(Uint16 Port, Uint16 Data) +{ + __asm__ __volatile__ ("outw %%ax, %%dx"::"d"(Port),"a"(Data)); +} +void outd(Uint16 Port, Uint32 Data) +{ + __asm__ __volatile__ ("outl %%eax, %%dx"::"d"(Port),"a"(Data)); +} +Uint8 inb(Uint16 Port) +{ + Uint8 ret; + __asm__ __volatile__ ("inb %%dx, %%al":"=a"(ret):"d"(Port)); + return ret; +} +Uint16 inw(Uint16 Port) +{ + Uint16 ret; + __asm__ __volatile__ ("inw %%dx, %%ax":"=a"(ret):"d"(Port)); + return ret; +} +Uint32 ind(Uint16 Port) +{ + Uint32 ret; + __asm__ __volatile__ ("inl %%dx, %%eax":"=a"(ret):"d"(Port)); + return ret; +} + +/** + * \fn void *memset(void *Dest, int Val, size_t Num) + * \brief Do a byte granuality set of Dest + */ +void *memset(void *Dest, int Val, size_t Num) +{ + Uint32 val = Val&0xFF; + val |= val << 8; + val |= val << 16; + __asm__ __volatile__ ( + "rep stosl;\n\t" + "mov %3, %%ecx;\n\t" + "rep stosb" + :: "D" (Dest), "a" (val), "c" (Num/4), "r" (Num&3)); + return Dest; +} +/** + * \brief Set double words + */ +void *memsetd(void *Dest, Uint32 Val, size_t Num) +{ + __asm__ __volatile__ ("rep stosl" :: "D" (Dest), "a" (Val), "c" (Num)); + return Dest; +} + +/** + * \fn int memcmp(const void *m1, const void *m2, size_t Num) + * \brief Compare two pieces of memory + */ +int memcmp(const void *m1, const void *m2, size_t Num) +{ + const Uint8 *d1 = m1; + const Uint8 *d2 = m2; + if( Num == 0 ) return 0; // No bytes are always identical + + while(Num--) + { + if(*d1 != *d2) + return *d1 - *d2; + d1 ++; + d2 ++; + } + return 0; +} + +/** + * \fn void *memcpy(void *Dest, const void *Src, size_t Num) + * \brief Copy \a Num bytes from \a Src to \a Dest + */ +void *memcpy(void *Dest, const void *Src, size_t Num) +{ + tVAddr dst = (tVAddr)Dest; + tVAddr src = (tVAddr)Src; + if( (dst & 3) != (src & 3) ) + { + __asm__ __volatile__ ("rep movsb" :: "D" (dst), "S" (src), "c" (Num)); +// Debug("\nmemcpy:Num=0x%x by %p (UA)", Num, __builtin_return_address(0)); + } + #if 1 + else if( Num > 128 && (dst & 15) == (src & 15) ) + { + char tmp[16+15]; // Note, this is a hack to save/restor xmm0 + int count = 16 - (dst & 15); +// Debug("\nmemcpy:Num=0x%x by %p (SSE)", Num, __builtin_return_address(0)); + if( count < 16 ) + { + Num -= count; + __asm__ __volatile__ ("rep movsb" : "=D"(dst),"=S"(src): "0"(dst), "1"(src), "c"(count)); + } + + count = Num / 16; + __asm__ __volatile__ ( + "movdqa 0(%5), %%xmm0;\n\t" + "1:\n\t" + "movdqa 0(%1), %%xmm0;\n\t" + "movdqa %%xmm0, 0(%0);\n\t" + "add $16,%0;\n\t" + "add $16,%1;\n\t" + "loop 1b;\n\t" + "movdqa %%xmm0, 0(%5);\n\t" + : "=r"(dst),"=r"(src) + : "0"(dst), "1"(src), "c"(count), "r" (((tVAddr)tmp+15)&~15) + ); + + count = Num & 15; + if(count) + __asm__ __volatile__ ("rep movsb" :: "D"(dst), "S"(src), "c"(count)); + } + #endif + else + { +// Debug("\nmemcpy:Num=0x%x by %p", Num, __builtin_return_address(0)); + __asm__ __volatile__ ( + "rep movsl;\n\t" + "mov %3, %%ecx;\n\t" + "rep movsb" + :: "D" (Dest), "S" (Src), "c" (Num/4), "r" (Num&3)); + } + return Dest; +} + +/** + * \fn void *memcpyd(void *Dest, const void *Src, size_t Num) + * \brief Copy \a Num DWORDs from \a Src to \a Dest + */ +void *memcpyd(void *Dest, const void *Src, size_t Num) +{ + __asm__ __volatile__ ("rep movsl" :: "D" (Dest), "S" (Src), "c" (Num)); + return Dest; +} + +#include "../helpers.h" + +DEF_DIVMOD(64); + +Uint64 DivMod64U(Uint64 Num, Uint64 Div, Uint64 *Rem) +{ + if( Div < 0x100000000ULL && Num < 0xFFFFFFFF * Div ) { + Uint32 rem, ret_32; + __asm__ __volatile__( + "div %4" + : "=a" (ret_32), "=d" (rem) + : "a" ( (Uint32)(Num & 0xFFFFFFFF) ), "d" ((Uint32)(Num >> 32)), "r" (Div) + ); + if(Rem) *Rem = rem; + return ret_32; + } + + return __divmod64(Num, Div, Rem); +} + +/** + * \fn Uint64 __udivdi3(Uint64 Num, Uint64 Den) + * \brief Divide two 64-bit integers + */ +Uint64 __udivdi3(Uint64 Num, Uint64 Den) +{ + if(Den == 0) { + __asm__ __volatile__ ("int $0x0"); + return -1; + } + // Common speedups + if(Num <= 0xFFFFFFFF && Den <= 0xFFFFFFFF) + return (Uint32)Num / (Uint32)Den; + if(Den == 1) return Num; + if(Den == 2) return Num >> 1; // Speed Hacks + if(Den == 4) return Num >> 2; // Speed Hacks + if(Den == 8) return Num >> 3; // Speed Hacks + if(Den == 16) return Num >> 4; // Speed Hacks + if(Den == 32) return Num >> 5; // Speed Hacks + if(Den == 1024) return Num >> 10; // Speed Hacks + if(Den == 2048) return Num >> 11; // Speed Hacks + if(Den == 4096) return Num >> 12; + if(Num < Den) return 0; + if(Num < Den*2) return 1; + if(Num == Den*2) return 2; + + return __divmod64(Num, Den, NULL); +} + +/** + * \fn Uint64 __umoddi3(Uint64 Num, Uint64 Den) + * \brief Get the modulus of two 64-bit integers + */ +Uint64 __umoddi3(Uint64 Num, Uint64 Den) +{ + Uint64 ret = 0; + if(Den == 0) { + __asm__ __volatile__ ("int $0x0"); // Call Div by Zero Error + return -1; + } + if(Den == 1) return 0; // Speed Hacks + if(Den == 2) return Num & 1; // Speed Hacks + if(Den == 4) return Num & 3; // Speed Hacks + if(Den == 8) return Num & 7; // Speed Hacks + if(Den == 16) return Num & 15; // Speed Hacks + if(Den == 32) return Num & 31; // Speed Hacks + if(Den == 1024) return Num & 1023; // Speed Hacks + if(Den == 2048) return Num & 2047; // Speed Hacks + if(Den == 4096) return Num & 4095; // Speed Hacks + + if(Num >> 32 == 0 && Den >> 32 == 0) + return (Uint32)Num % (Uint32)Den; + + __divmod64(Num, Den, &ret); + return ret; +} + + +// --- EXPORTS --- +EXPORT(memcpy); EXPORT(memset); +EXPORT(memcmp); +//EXPORT(memcpyw); EXPORT(memsetw); +EXPORT(memcpyd); EXPORT(memsetd); +EXPORT(inb); EXPORT(inw); EXPORT(ind); +EXPORT(outb); EXPORT(outw); EXPORT(outd); +EXPORT(__udivdi3); EXPORT(__umoddi3); + +EXPORT(SHORTLOCK); +EXPORT(SHORTREL); +EXPORT(IS_LOCKED); diff --git a/KernelLand/Kernel/arch/x86/link.ld b/KernelLand/Kernel/arch/x86/link.ld new file mode 100644 index 00000000..a09b29af --- /dev/null +++ b/KernelLand/Kernel/arch/x86/link.ld @@ -0,0 +1,71 @@ +/* + * AcessMicro Kernel + * Linker Script + */ + +lowStart = start - 0xC0000000; +ENTRY(lowStart) +OUTPUT_FORMAT(elf32-i386) + +SECTIONS { + . = 0x100000; + __load_addr = .; + .multiboot : AT(ADDR(.multiboot)) { + *(.multiboot) + } + + . += 0xC0000000; + + .text ALIGN(0x1000): AT(ADDR(.text) - 0xC0000000) { + *(.text) + } + + .usertext ALIGN(0x1000): AT(ADDR(.usertext) - 0xC0000000) { + _UsertextBase = .; + *(.usertext) + } + _UsertextEnd = .; + + .rodata ALIGN(0x1000): AT(ADDR(.rodata) - 0xC0000000) { + *(.initpd) + *(.rodata) + *(.rdata) + gKernelModules = .; + *(KMODULES) + gKernelModulesEnd = .; + . = ALIGN(4); + gKernelSymbols = .; + *(KEXPORT) + gKernelSymbolsEnd = .; + + + } + /* + .debug_abbrev : { *(.debug_abbrev) } + .debug_info : { *(.debug_info) } + .debug_line : { *(.debug_line) } + .debug_loc : { *(.debug_loc) } + .debug_pubnames : { *(.debug_pubnames) } + .debug_aranges : { *(.debug_aranges) } + .debug_ranges : { *(.debug_ranges) } + .debug_str : { *(.debug_str) } + .debug_frame : { *(.debug_frame) } + */ + + .padata ALIGN (0x1000) : AT(ADDR(.padata) - 0xC0000000) { + *(.padata) + } + + .data ALIGN (0x1000) : AT(ADDR(.data) - 0xC0000000) { + *(.data) + } + + __bss_start = .; + .bss : AT(ADDR(.bss) - 0xC0000000) { + _sbss = .; + *(COMMON) + *(.bss) + _ebss = .; + } + gKernelEnd = (. + 0xFFF)&0xFFFFF000; +} diff --git a/KernelLand/Kernel/arch/x86/main.c b/KernelLand/Kernel/arch/x86/main.c new file mode 100644 index 00000000..2106b6af --- /dev/null +++ b/KernelLand/Kernel/arch/x86/main.c @@ -0,0 +1,160 @@ +/* + * Acess 2 + * x86 Kernel Main + * arch/x86/main.c + */ +#include +#include +#include +#include +#include +#include + +#define VGA_ERRORS 0 + +#define MAX_ARGSTR_POS (0x400000-0x2000) + +// === IMPORTS === +extern void Heap_Install(void); +extern void Desctab_Install(void); +extern void MM_PreinitVirtual(void); +extern void MM_Install(tMBoot_Info *MBoot); +extern void MM_InstallVirtual(void); +extern void Threads_Init(void); +extern int Time_Setup(void); +// --- Core --- +extern void System_Init(char *Commandline); + +// === PROTOTYPES === + int kmain(Uint MbMagic, void *MbInfoPtr); + +// === GLOBALS === +char *gsBootCmdLine = NULL; +struct { + Uint32 PBase; + void *Base; + Uint Size; + char *ArgString; +} *gaArch_BootModules; + int giArch_NumBootModules = 0; + +// === CODE === +int kmain(Uint MbMagic, void *MbInfoPtr) +{ + int i; + tMBoot_Module *mods; + tMBoot_Info *mbInfo; + + LogF("Acess2 x86-"PLATFORM" v"EXPAND_STR(KERNEL_VERSION)"\n"); + LogF(" Build %i, Git Hash %s\n", BUILD_NUM, gsGitHash); + + Log("MbMagic = %08x, MbInfoPtr = %p", MbMagic, MbInfoPtr); + + // Set up non-boot info dependent stuff + Desctab_Install(); // Set up GDT and IDT + MM_PreinitVirtual(); // Initialise virtual mappings + + switch(MbMagic) + { + // Multiboot 1 + case MULTIBOOT_MAGIC: + // Adjust Multiboot structure address + mbInfo = (void*)( (Uint)MbInfoPtr + KERNEL_BASE ); + gsBootCmdLine = (char*)(mbInfo->CommandLine + KERNEL_BASE); + + MM_Install( mbInfo ); // Set up physical memory manager + break; + + // Multiboot 2 + case MULTIBOOT2_MAGIC: + Panic("Multiboot 2 not yet supported"); + //MM_InstallMBoot2( MbInfo ); // Set up physical memory manager + return 0; + break; + + default: + Panic("Multiboot magic invalid %08x, expected %08x or %08x\n", + MbMagic, MULTIBOOT_MAGIC, MULTIBOOT2_MAGIC); + return 0; + } + + MM_InstallVirtual(); // Clean up virtual address space + Heap_Install(); // Create initial heap + + // Start Multitasking + Threads_Init(); + + // Start Timers + Time_Setup(); + + Log_Log("Arch", "Starting VFS..."); + // Load Virtual Filesystem + VFS_Init(); + + // Load initial modules + mods = (void*)( mbInfo->Modules + KERNEL_BASE ); + giArch_NumBootModules = mbInfo->ModuleCount; + gaArch_BootModules = malloc( giArch_NumBootModules * sizeof(*gaArch_BootModules) ); + for( i = 0; i < mbInfo->ModuleCount; i ++ ) + { + int ofs; + + Log_Log("Arch", "Multiboot Module at 0x%08x, 0x%08x bytes (String at 0x%08x)", + mods[i].Start, mods[i].End-mods[i].Start, mods[i].String); + + gaArch_BootModules[i].PBase = mods[i].Start; + gaArch_BootModules[i].Size = mods[i].End - mods[i].Start; + + // Always HW map the module data + ofs = mods[i].Start&0xFFF; + gaArch_BootModules[i].Base = (void*)( MM_MapHWPages(mods[i].Start, + (gaArch_BootModules[i].Size+ofs+0xFFF) / 0x1000 + ) + ofs ); + + // Only map the string if needed + if( (tVAddr)mods[i].String > MAX_ARGSTR_POS ) + { + // Assumes the string is < 4096 bytes long) + gaArch_BootModules[i].ArgString = (void*)( + MM_MapHWPages(mods[i].String, 2) + (mods[i].String&0xFFF) + ); + } + else + gaArch_BootModules[i].ArgString = (char *)mods[i].String + KERNEL_BASE; + } + + // Pass on to Independent Loader + Log_Log("Arch", "Starting system"); + System_Init(gsBootCmdLine); + + // Sleep forever (sleeping beauty) + for(;;) + Threads_Sleep(); + return 0; +} + +void Arch_LoadBootModules(void) +{ + int i, j, numPages; + for( i = 0; i < giArch_NumBootModules; i ++ ) + { + Log_Log("Arch", "Loading '%s'", gaArch_BootModules[i].ArgString); + + if( !Module_LoadMem( gaArch_BootModules[i].Base, gaArch_BootModules[i].Size, gaArch_BootModules[i].ArgString ) ) + { + Log_Warning("Arch", "Unable to load module"); + } + + // Unmap and free + numPages = (gaArch_BootModules[i].Size + ((Uint)gaArch_BootModules[i].Base&0xFFF) + 0xFFF) >> 12; + MM_UnmapHWPages( (tVAddr)gaArch_BootModules[i].Base, numPages ); + + for( j = 0; j < numPages; j++ ) + MM_DerefPhys( gaArch_BootModules[i].PBase + (j << 12) ); + + if( (tVAddr) gaArch_BootModules[i].ArgString > MAX_ARGSTR_POS ) + MM_UnmapHWPages( (tVAddr)gaArch_BootModules[i].ArgString, 2 ); + } + Log_Log("Arch", "Boot modules loaded"); + free( gaArch_BootModules ); +} diff --git a/KernelLand/Kernel/arch/x86/mm_phys.c b/KernelLand/Kernel/arch/x86/mm_phys.c new file mode 100644 index 00000000..aa1b2603 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/mm_phys.c @@ -0,0 +1,552 @@ +/* + * Acess2 + * - Physical memory manager + */ +#define DEBUG 0 +#include +#include +#include + +//#define USE_STACK 1 +#define TRACE_ALLOCS 0 // Print trace messages on AllocPhys/DerefPhys + + +// === IMPORTS === +extern char gKernelEnd[]; +extern void Proc_PrintBacktrace(void); + +// === PROTOTYPES === +void MM_Install(tMBoot_Info *MBoot); +//tPAddr MM_AllocPhys(void); +//tPAddr MM_AllocPhysRange(int Pages, int MaxBits); +//void MM_RefPhys(tPAddr PAddr); +//void MM_DerefPhys(tPAddr PAddr); +// int MM_GetRefCount(tPAddr PAddr); + +// === GLOBALS === +tMutex glPhysAlloc; +Uint64 giPhysAlloc = 0; // Number of allocated pages +Uint64 giPageCount = 0; // Total number of pages +Uint64 giLastPossibleFree = 0; // Last possible free page (before all pages are used) + +Uint32 gaSuperBitmap[1024]; // Blocks of 1024 Pages +Uint32 gaPageBitmap[1024*1024/32]; // Individual pages + int *gaPageReferences; +void **gaPageNodes = (void*)MM_PAGENODE_BASE; +#define REFENT_PER_PAGE (0x1000/sizeof(gaPageReferences[0])) + +// === CODE === +void MM_Install(tMBoot_Info *MBoot) +{ + Uint kernelPages, num; + Uint i; + Uint64 maxAddr = 0; + tMBoot_Module *mods; + tMBoot_MMapEnt *ent; + + // --- Find largest address + MBoot->MMapAddr |= KERNEL_BASE; + ent = (void *)( MBoot->MMapAddr ); + while( (Uint)ent < MBoot->MMapAddr + MBoot->MMapLength ) + { + // Adjust for size + ent->Size += 4; + + // If entry is RAM and is above `maxAddr`, change `maxAddr` + if(ent->Type == 1 && ent->Base + ent->Length > maxAddr) + maxAddr = ent->Base + ent->Length; + // Go to next entry + ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size ); + } + + if(maxAddr == 0) { + giPageCount = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value + } + else { + giPageCount = maxAddr >> 12; + } + giLastPossibleFree = giPageCount - 1; + + memsetd(gaPageBitmap, 0xFFFFFFFF, giPageCount/32); + + // Set up allocateable space + ent = (void *)( MBoot->MMapAddr ); + while( (Uint)ent < MBoot->MMapAddr + MBoot->MMapLength ) + { + memsetd( &gaPageBitmap[ent->Base/(4096*32)], 0, ent->Length/(4096*32) ); + ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size ); + } + + // Get used page count (Kernel) + kernelPages = (Uint)&gKernelEnd - KERNEL_BASE - 0x100000; + kernelPages += 0xFFF; // Page Align + kernelPages >>= 12; + + // Fill page bitmap + num = kernelPages/32; + memsetd( &gaPageBitmap[0x100000/(4096*32)], -1, num ); + gaPageBitmap[ 0x100000/(4096*32) + num ] = (1 << (kernelPages & 31)) - 1; + + // Fill Superpage bitmap + num = kernelPages/(32*32); + memsetd( &gaSuperBitmap[0x100000/(4096*32*32)], -1, num ); + gaSuperBitmap[ 0x100000/(4096*32*32) + num ] = (1 << ((kernelPages / 32) & 31)) - 1; + + // Mark Multiboot's pages as taken + // - Structure + MM_RefPhys( (Uint)MBoot - KERNEL_BASE ); + // - Module List + for(i = (MBoot->ModuleCount*sizeof(tMBoot_Module)+0xFFF)>12; i--; ) + MM_RefPhys( MBoot->Modules + (i << 12) ); + // - Modules + mods = (void*)(MBoot->Modules + KERNEL_BASE); + for(i = 0; i < MBoot->ModuleCount; i++) + { + num = (mods[i].End - mods[i].Start + 0xFFF) >> 12; + while(num--) + MM_RefPhys( (mods[i].Start & ~0xFFF) + (num<<12) ); + } + + gaPageReferences = (void*)MM_REFCOUNT_BASE; + + Log_Log("PMem", "Physical memory set up"); +} + +/** + * \fn tPAddr MM_AllocPhys(void) + * \brief Allocates a physical page from the general pool + */ +tPAddr MM_AllocPhys(void) +{ + // int a, b, c; + int indx = -1; + tPAddr ret; + + ENTER(""); + + Mutex_Acquire( &glPhysAlloc ); + + // Classful scan + #if 1 + { + const int addrClasses[] = {0,16,20,24,32,64}; + const int numAddrClasses = sizeof(addrClasses)/sizeof(addrClasses[0]); + int i; + int first, last; + for( i = numAddrClasses; i -- > 1; ) + { + first = 1UL << (addrClasses[i-1] - 12); + last = (1UL << (addrClasses[i] - 12)) - 1; + // Range is above the last free page + if( first > giLastPossibleFree ) + continue; + // Last possible free page is in the range + if( last > giLastPossibleFree ) + last = giLastPossibleFree; + + // Scan the range + for( indx = first; indx < last; ) + { + if( gaSuperBitmap[indx>>10] == -1 ) { + indx += 1024; + continue; + } + + if( gaPageBitmap[indx>>5] == -1 ) { + indx += 32; + continue; + } + + if( gaPageBitmap[indx>>5] & (1 << (indx&31)) ) { + indx ++; + continue; + } + break; + } + if( indx < last ) break; + + giLastPossibleFree = first; // Well, we couldn't find any in this range + } + // Out of memory? + if( i <= 1 ) indx = -1; + } + #elif 0 + // Find free page + // Scan downwards + LOG("giLastPossibleFree = %i", giLastPossibleFree); + for( indx = giLastPossibleFree; indx >= 0; ) + { + if( gaSuperBitmap[indx>>10] == -1 ) { + indx -= 1024; + continue; + } + + if( gaPageBitmap[indx>>5] == -1 ) { + indx -= 32; + continue; + } + + if( gaPageBitmap[indx>>5] & (1 << (indx&31)) ) { + indx --; + continue; + } + break; + } + if( indx >= 0 ) + giLastPossibleFree = indx; + LOG("indx = %i", indx); + #else + c = giLastPossibleFree % 32; + b = (giLastPossibleFree / 32) % 32; + a = giLastPossibleFree / 1024; + + LOG("a=%i,b=%i,c=%i", a, b, c); + for( ; gaSuperBitmap[a] == -1 && a >= 0; a-- ); + if(a < 0) { + Mutex_Release( &glPhysAlloc ); + Warning("MM_AllocPhys - OUT OF MEMORY (Called by %p) - %lli/%lli used", + __builtin_return_address(0), giPhysAlloc, giPageCount); + LEAVE('i', 0); + return 0; + } + for( ; gaSuperBitmap[a] & (1<= 0 ) + giLastPossibleFree = indx; + #endif + + if( indx < 0 ) { + Mutex_Release( &glPhysAlloc ); + Warning("MM_AllocPhys - OUT OF MEMORY (Called by %p) - %lli/%lli used (indx = %x)", + __builtin_return_address(0), giPhysAlloc, giPageCount, indx); + Log_Debug("PMem", "giLastPossibleFree = %lli", giLastPossibleFree); + LEAVE('i', 0); + return 0; + } + + if( indx > 0xFFFFF ) { + Panic("The fuck? Too many pages! (indx = 0x%x)", indx); + } + + if( indx >= giPageCount ) { + Mutex_Release( &glPhysAlloc ); + Log_Error("PMem", "MM_AllocPhys - indx(%i) > giPageCount(%i)", indx, giPageCount); + LEAVE('i', 0); + return 0; + } + + // Mark page used + if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[indx] ) ) + gaPageReferences[indx] = 1; + gaPageBitmap[ indx>>5 ] |= 1 << (indx&31); + + giPhysAlloc ++; + + // Get address + ret = indx << 12; + + // Mark used block + if(gaPageBitmap[ indx>>5 ] == -1) { + gaSuperBitmap[indx>>10] |= 1 << ((indx>>5)&31); + } + + // Release Spinlock + Mutex_Release( &glPhysAlloc ); + + LEAVE('X', ret); + #if TRACE_ALLOCS + if( now() > 4000 ) { + Log_Debug("PMem", "MM_AllocPhys: RETURN %P (%i free)", ret, giPageCount-giPhysAlloc); + Proc_PrintBacktrace(); + } + #endif + return ret; +} + +/** + * \fn tPAddr MM_AllocPhysRange(int Pages, int MaxBits) + * \brief Allocate a range of physical pages + * \param Pages Number of pages to allocate + * \param MaxBits Maximum number of address bits to use + */ +tPAddr MM_AllocPhysRange(int Pages, int MaxBits) +{ + int a, b; + int i, idx, sidx; + tPAddr ret; + + ENTER("iPages iMaxBits", Pages, MaxBits); + + // Sanity Checks + if(MaxBits < 0) { + LEAVE('i', 0); + return 0; + } + if(MaxBits > PHYS_BITS) MaxBits = PHYS_BITS; + + // Lock + Mutex_Acquire( &glPhysAlloc ); + + // Set up search state + if( giLastPossibleFree > ((tPAddr)1 << (MaxBits-12)) ) { + sidx = (tPAddr)1 << (MaxBits-12); + } + else { + sidx = giLastPossibleFree; + } + idx = sidx / 32; + sidx %= 32; + b = idx % 32; + a = idx / 32; + + #if 0 + LOG("a=%i, b=%i, idx=%i, sidx=%i", a, b, idx, sidx); + + // Find free page + for( ; gaSuperBitmap[a] == -1 && a --; ) b = 31; + if(a < 0) { + Mutex_Release( &glPhysAlloc ); + Warning("MM_AllocPhysRange - OUT OF MEMORY (Called by %p)", __builtin_return_address(0)); + LEAVE('i', 0); + return 0; + } + LOG("a = %i", a); + for( ; gaSuperBitmap[a] & (1 << b); b-- ) sidx = 31; + LOG("b = %i", b); + idx = a * 32 + b; + for( ; gaPageBitmap[idx] & (1 << sidx); sidx-- ) + LOG("gaPageBitmap[%i] = 0x%08x", idx, gaPageBitmap[idx]); + + LOG("idx = %i, sidx = %i", idx, sidx); + #else + + #endif + + // Check if the gap is large enough + while( idx >= 0 ) + { + // Find a free page + for( ; ; ) + { + // Bulk Skip + if( gaPageBitmap[idx] == -1 ) { + idx --; + sidx = 31; + continue; + } + + if( gaPageBitmap[idx] & (1 << sidx) ) { + sidx --; + if(sidx < 0) { sidx = 31; idx --; } + if(idx < 0) break; + continue; + } + break; + } + if( idx < 0 ) break; + + // Check if it is a free range + for( i = 0; i < Pages; i++ ) + { + // Used page? break + if( gaPageBitmap[idx] & (1 << sidx) ) + break; + + sidx --; + if(sidx < 0) { sidx = 31; idx --; } + if(idx < 0) break; + } + + if( i == Pages ) + break; + } + + // Check if an address was found + if( idx < 0 ) { + Mutex_Release( &glPhysAlloc ); + Warning("MM_AllocPhysRange - OUT OF MEMORY (Called by %p)", __builtin_return_address(0)); + LEAVE('i', 0); + return 0; + } + + // Mark pages used + for( i = 0; i < Pages; i++ ) + { + if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[idx*32+sidx] ) ) + gaPageReferences[idx*32+sidx] = 1; + gaPageBitmap[ idx ] |= 1 << sidx; + sidx ++; + giPhysAlloc ++; + if(sidx == 32) { sidx = 0; idx ++; } + } + + // Get address + ret = (idx << 17) | (sidx << 12); + + // Mark used block + if(gaPageBitmap[ idx ] == -1) gaSuperBitmap[idx/32] |= 1 << (idx%32); + + // Release Spinlock + Mutex_Release( &glPhysAlloc ); + + LEAVE('X', ret); + #if TRACE_ALLOCS + Log_Debug("PMem", "MM_AllocPhysRange: RETURN 0x%llx-0x%llx (%i free)", + ret, ret + (1<>= 12; + + // We don't care about non-ram pages + if(PAddr >= giPageCount) return; + + // Lock Structures + Mutex_Acquire( &glPhysAlloc ); + + // Reference the page + if( gaPageReferences ) + { + if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) == 0 ) + { + int i, base; + tVAddr addr = ((tVAddr)&gaPageReferences[PAddr]) & ~0xFFF; +// Log_Debug("PMem", "MM_RefPhys: Allocating info for %X", PAddr); + Mutex_Release( &glPhysAlloc ); + if( MM_Allocate( addr ) == 0 ) { + Log_KernelPanic("PMem", "MM_RefPhys: Out of physical memory allocating info for %X", PAddr*PAGE_SIZE); + } + Mutex_Acquire( &glPhysAlloc ); + + base = PAddr & ~(1024-1); + for( i = 0; i < 1024; i ++ ) { + gaPageReferences[base + i] = (gaPageBitmap[(base+i)/32] & (1 << (base+i)%32)) ? 1 : 0; + } + } + gaPageReferences[ PAddr ] ++; + } + + // Mark as used + gaPageBitmap[ PAddr / 32 ] |= 1 << (PAddr&31); + + // Mark used block + if(gaPageBitmap[ PAddr / 32 ] == -1) + gaSuperBitmap[PAddr/1024] |= 1 << ((PAddr/32)&31); + + // Release Spinlock + Mutex_Release( &glPhysAlloc ); +} + +/** + * \fn void MM_DerefPhys(tPAddr PAddr) + * \brief Dereferences a physical page + */ +void MM_DerefPhys(tPAddr PAddr) +{ + // Get page number + PAddr >>= 12; + + // We don't care about non-ram pages + if(PAddr >= giPageCount) return; + + // Check if it is freed + if( !(gaPageBitmap[PAddr / 32] & (1 << PAddr%32)) ) { + Log_Warning("MMVirt", "MM_DerefPhys - Non-referenced memory dereferenced"); + return; + } + + // Lock Structures + Mutex_Acquire( &glPhysAlloc ); + + if( giLastPossibleFree < PAddr ) + giLastPossibleFree = PAddr; + + // Dereference + if( !MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) || (-- gaPageReferences[PAddr]) == 0 ) + { + #if TRACE_ALLOCS + Log_Debug("PMem", "MM_DerefPhys: Free'd %P (%i free)", PAddr<<12, giPageCount-giPhysAlloc); + Proc_PrintBacktrace(); + #endif + //LOG("Freed 0x%x by %p\n", PAddr<<12, __builtin_return_address(0)); + giPhysAlloc --; + gaPageBitmap[ PAddr / 32 ] &= ~(1 << (PAddr&31)); + if(gaPageBitmap[ PAddr / 32 ] == 0) + gaSuperBitmap[ PAddr >> 10 ] &= ~(1 << ((PAddr >> 5)&31)); + + if( MM_GetPhysAddr( (tVAddr) &gaPageNodes[PAddr] ) ) + { + gaPageNodes[PAddr] = NULL; + // TODO: Free Node Page when fully unused + } + } + + // Release spinlock + Mutex_Release( &glPhysAlloc ); +} + +/** + * \fn int MM_GetRefCount(tPAddr Addr) + */ +int MM_GetRefCount(tPAddr PAddr) +{ + // Get page number + PAddr >>= 12; + + // We don't care about non-ram pages + if(PAddr >= giPageCount) return -1; + + if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) == 0 ) + return (gaPageBitmap[PAddr / 32] & (1 << PAddr%32)) ? 1 : 0; + + // Check if it is freed + return gaPageReferences[ PAddr ]; +} + +int MM_SetPageNode(tPAddr PAddr, void *Node) +{ + tVAddr block_addr; + + if( MM_GetRefCount(PAddr) == 0 ) return 1; + + PAddr /= PAGE_SIZE; + + block_addr = (tVAddr) &gaPageNodes[PAddr]; + block_addr &= ~(PAGE_SIZE-1); + + if( !MM_GetPhysAddr( block_addr ) ) + { + if( !MM_Allocate( block_addr ) ) { + Log_Warning("PMem", "Unable to allocate Node page"); + return -1; + } + memset( (void*)block_addr, 0, PAGE_SIZE ); + } + + gaPageNodes[PAddr] = Node; +// Log("gaPageNodes[0x%x] = %p", PAddr, Node); + return 0; +} + +int MM_GetPageNode(tPAddr PAddr, void **Node) +{ + if( MM_GetRefCount(PAddr) == 0 ) return 1; + + PAddr /= PAGE_SIZE; + if( !MM_GetPhysAddr( (tVAddr) &gaPageNodes[PAddr] ) ) { + *Node = NULL; + return 0; + } + *Node = gaPageNodes[PAddr]; + return 0; +} + diff --git a/KernelLand/Kernel/arch/x86/mm_virt.c b/KernelLand/Kernel/arch/x86/mm_virt.c new file mode 100644 index 00000000..bd7b1dff --- /dev/null +++ b/KernelLand/Kernel/arch/x86/mm_virt.c @@ -0,0 +1,1151 @@ +/* + * AcessOS Microkernel Version + * mm_virt.c + * + * Memory Map + * 0xE0 - Kernel Base + * 0xF0 - Kernel Stacks + * 0xFD - Fractals + * 0xFE - Unused + * 0xFF - System Calls / Kernel's User Code + */ +#define DEBUG 0 +#define SANITY 1 +#include +#include +#include +#include +#include +#include + +#define TAB 22 + +#define WORKER_STACKS 0x00100000 // Thread0 Only! +#define WORKER_STACK_SIZE MM_KERNEL_STACK_SIZE +#define WORKER_STACKS_END 0xB0000000 +#define NUM_WORKER_STACKS ((WORKER_STACKS_END-WORKER_STACKS)/WORKER_STACK_SIZE) + +#define PAE_PAGE_TABLE_ADDR 0xFC000000 // 16 MiB +#define PAE_PAGE_DIR_ADDR 0xFCFC0000 // 16 KiB +#define PAE_PAGE_PDPT_ADDR 0xFCFC3F00 // 32 bytes +#define PAE_TMP_PDPT_ADDR 0xFCFC3F20 // 32 bytes +#define PAE_TMP_DIR_ADDR 0xFCFE0000 // 16 KiB +#define PAE_TMP_TABLE_ADDR 0xFD000000 // 16 MiB + +#define PAGE_TABLE_ADDR 0xFC000000 +#define PAGE_DIR_ADDR 0xFC3F0000 +#define PAGE_CR3_ADDR 0xFC3F0FC0 +#define TMP_CR3_ADDR 0xFC3F0FC4 // Part of core instead of temp +#define TMP_DIR_ADDR 0xFC3F1000 // Same +#define TMP_TABLE_ADDR 0xFC400000 + +#define HW_MAP_ADDR 0xFE000000 +#define HW_MAP_MAX 0xFFEF0000 +#define NUM_HW_PAGES ((HW_MAP_MAX-HW_MAP_ADDR)/0x1000) +#define TEMP_MAP_ADDR 0xFFEF0000 // Allows 16 "temp" pages +#define NUM_TEMP_PAGES 16 +#define LAST_BLOCK_ADDR 0xFFFF0000 // Free space for kernel provided user code/ *(-1) protection + +#define PF_PRESENT 0x1 +#define PF_WRITE 0x2 +#define PF_USER 0x4 +#define PF_GLOBAL 0x80 +#define PF_COW 0x200 +#define PF_NOPAGE 0x400 + +#define INVLPG(addr) __asm__ __volatile__ ("invlpg (%0)"::"r"(addr)) + +#define GET_TEMP_MAPPING(cr3) do { \ + __ASM__("cli"); \ + __AtomicTestSetLoop( (Uint *)gpTmpCR3, cr3 | 3 ); \ +} while(0) +#define REL_TEMP_MAPPING() do { \ + *gpTmpCR3 = 0; \ + __ASM__("sti"); \ +} while(0) + +typedef Uint32 tTabEnt; + +// === IMPORTS === +extern char _UsertextEnd[], _UsertextBase[]; +extern Uint32 gaInitPageDir[1024]; +extern Uint32 gaInitPageTable[1024]; +extern void Threads_SegFault(tVAddr Addr); +extern void Error_Backtrace(Uint eip, Uint ebp); + +// === PROTOTYPES === +void MM_PreinitVirtual(void); +void MM_InstallVirtual(void); +void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs); +//void MM_DumpTables(tVAddr Start, tVAddr End); +//void MM_ClearUser(void); +tPAddr MM_DuplicatePage(tVAddr VAddr); + +// === GLOBALS === +#define gaPageTable ((tTabEnt*)PAGE_TABLE_ADDR) +#define gaPageDir ((tTabEnt*)PAGE_DIR_ADDR) +#define gaTmpTable ((tTabEnt*)TMP_TABLE_ADDR) +#define gaTmpDir ((tTabEnt*)TMP_DIR_ADDR) +#define gpPageCR3 ((tTabEnt*)PAGE_CR3_ADDR) +#define gpTmpCR3 ((tTabEnt*)TMP_CR3_ADDR) + +#define gaPAE_PageTable ((tTabEnt*)PAE_PAGE_TABLE_ADDR) +#define gaPAE_PageDir ((tTabEnt*)PAE_PAGE_DIR_ADDR) +#define gaPAE_MainPDPT ((tTabEnt*)PAE_PAGE_PDPT_ADDR) +#define gaPAE_TmpTable ((tTabEnt*)PAE_TMP_DIR_ADDR) +#define gaPAE_TmpDir ((tTabEnt*)PAE_TMP_DIR_ADDR) +#define gaPAE_TmpPDPT ((tTabEnt*)PAE_TMP_PDPT_ADDR) + int gbUsePAE = 0; +tMutex glTempMappings; +tMutex glTempFractal; +Uint32 gWorkerStacks[(NUM_WORKER_STACKS+31)/32]; + int giLastUsedWorker = 0; +struct sPageInfo { + void *Node; + tVAddr Base; + Uint64 Offset; + int Length; + int Flags; +} *gaMappedRegions; // sizeof = 24 bytes + +// === CODE === +/** + * \fn void MM_PreinitVirtual(void) + * \brief Maps the fractal mappings + */ +void MM_PreinitVirtual(void) +{ + gaInitPageDir[ PAGE_TABLE_ADDR >> 22 ] = ((tTabEnt)&gaInitPageDir - KERNEL_BASE) | 3; + INVLPG( PAGE_TABLE_ADDR ); +} + +/** + * \fn void MM_InstallVirtual(void) + * \brief Sets up the constant page mappings + */ +void MM_InstallVirtual(void) +{ + int i; + + // --- Pre-Allocate kernel tables + for( i = KERNEL_BASE>>22; i < 1024; i ++ ) + { + if( gaPageDir[ i ] ) continue; + // Skip stack tables, they are process unique + if( i > MM_KERNEL_STACKS >> 22 && i < MM_KERNEL_STACKS_END >> 22) { + gaPageDir[ i ] = 0; + continue; + } + // Preallocate table + gaPageDir[ i ] = MM_AllocPhys() | 3; + INVLPG( &gaPageTable[i*1024] ); + memset( &gaPageTable[i*1024], 0, 0x1000 ); + } + + // Unset kernel on the User Text pages + for( i = ((tVAddr)&_UsertextEnd-(tVAddr)&_UsertextBase+0xFFF)/4096; i--; ) { + MM_SetFlags( (tVAddr)&_UsertextBase + i*4096, 0, MM_PFLAG_KERNEL ); + } + + *gpTmpCR3 = 0; +} + +/** + * \brief Cleans up the SMP required mappings + */ +void MM_FinishVirtualInit(void) +{ + gaInitPageDir[ 0 ] = 0; +} + +/** + * \fn void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs) + * \brief Called on a page fault + */ +void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs) +{ + //ENTER("xAddr bErrorCode", Addr, ErrorCode); + + // -- Check for COW -- + if( gaPageDir [Addr>>22] & PF_PRESENT && gaPageTable[Addr>>12] & PF_PRESENT + && gaPageTable[Addr>>12] & PF_COW ) + { + tPAddr paddr; + if(MM_GetRefCount( gaPageTable[Addr>>12] & ~0xFFF ) == 1) + { + gaPageTable[Addr>>12] &= ~PF_COW; + gaPageTable[Addr>>12] |= PF_PRESENT|PF_WRITE; + } + else + { + //Log("MM_PageFault: COW - MM_DuplicatePage(0x%x)", Addr); + paddr = MM_DuplicatePage( Addr ); + MM_DerefPhys( gaPageTable[Addr>>12] & ~0xFFF ); + gaPageTable[Addr>>12] &= PF_USER; + gaPageTable[Addr>>12] |= paddr|PF_PRESENT|PF_WRITE; + } + +// Log_Debug("MMVirt", "COW for %p (%P)", Addr, gaPageTable[Addr>>12]); + + INVLPG( Addr & ~0xFFF ); + return; + } + + // Disable instruction tracing + __ASM__("pushf; andw $0xFEFF, 0(%esp); popf"); + Proc_GetCurThread()->bInstrTrace = 0; + + // If it was a user, tell the thread handler + if(ErrorCode & 4) { + Log_Warning("MMVirt", "User %s %s memory%s", + (ErrorCode&2?"write to":"read from"), + (ErrorCode&1?"bad/locked":"non-present"), + (ErrorCode&16?" (Instruction Fetch)":"") + ); + Log_Warning("MMVirt", "Instruction %04x:%08x accessed %p", Regs->cs, Regs->eip, Addr); + __ASM__("sti"); // Restart IRQs + #if 1 + Error_Backtrace(Regs->eip, Regs->ebp); + #endif + Threads_SegFault(Addr); + return ; + } + + Debug_KernelPanic(); + + // -- Check Error Code -- + if(ErrorCode & 8) + Warning("Reserved Bits Trashed!"); + else + { + Warning("Kernel %s %s memory%s", + (ErrorCode&2?"write to":"read from"), + (ErrorCode&1?"bad/locked":"non-present"), + (ErrorCode&16?" (Instruction Fetch)":"") + ); + } + + Log("CPU %i - Code at %p accessed %p", GetCPUNum(), Regs->eip, Addr); + // Print Stack Backtrace + Error_Backtrace(Regs->eip, Regs->ebp); + + #if 0 + Log("gaPageDir[0x%x] = 0x%x", Addr>>22, gaPageDir[Addr>>22]); + if( gaPageDir[Addr>>22] & PF_PRESENT ) + Log("gaPageTable[0x%x] = 0x%x", Addr>>12, gaPageTable[Addr>>12]); + #endif + //MM_DumpTables(0, -1); + + // Register Dump + Log("EAX %08x ECX %08x EDX %08x EBX %08x", Regs->eax, Regs->ecx, Regs->edx, Regs->ebx); + Log("ESP %08x EBP %08x ESI %08x EDI %08x", Regs->esp, Regs->ebp, Regs->esi, Regs->edi); + //Log("SS:ESP %04x:%08x", Regs->ss, Regs->esp); + Log("CS:EIP %04x:%08x", Regs->cs, Regs->eip); + Log("DS %04x ES %04x FS %04x GS %04x", Regs->ds, Regs->es, Regs->fs, Regs->gs); + { + Uint dr0, dr1; + __ASM__ ("mov %%dr0, %0":"=r"(dr0):); + __ASM__ ("mov %%dr1, %0":"=r"(dr1):); + Log("DR0 %08x DR1 %08x", dr0, dr1); + } + + Panic("Page Fault at 0x%x (Accessed 0x%x)", Regs->eip, Addr); +} + +/** + * \fn void MM_DumpTables(tVAddr Start, tVAddr End) + * \brief Dumps the layout of the page tables + */ +void MM_DumpTables(tVAddr Start, tVAddr End) +{ + tVAddr rangeStart = 0; + tPAddr expected = 0; + void *expected_node = NULL, *tmpnode = NULL; + tVAddr curPos; + Uint page; + const tPAddr MASK = ~0xF78; + + Start >>= 12; End >>= 12; + + #if 0 + Log("Directory Entries:"); + for(page = Start >> 10; + page < (End >> 10)+1; + page ++) + { + if(gaPageDir[page]) + { + Log(" 0x%08x-0x%08x :: 0x%08x", + page<<22, ((page+1)<<22)-1, + gaPageDir[page]&~0xFFF + ); + } + } + #endif + + Log("Table Entries:"); + for(page = Start, curPos = Start<<12; + page < End; + curPos += 0x1000, page++) + { + if( !(gaPageDir[curPos>>22] & PF_PRESENT) + || !(gaPageTable[page] & PF_PRESENT) + || (gaPageTable[page] & MASK) != expected + || (tmpnode=NULL,MM_GetPageNode(expected, &tmpnode), tmpnode != expected_node)) + { + if(expected) { + tPAddr orig = gaPageTable[rangeStart>>12]; + Log(" 0x%08x => 0x%08x - 0x%08x (%s%s%s%s%s) %p", + rangeStart, + orig & ~0xFFF, + curPos - rangeStart, + (orig & PF_NOPAGE ? "P" : "-"), + (orig & PF_COW ? "C" : "-"), + (orig & PF_GLOBAL ? "G" : "-"), + (orig & PF_USER ? "U" : "-"), + (orig & PF_WRITE ? "W" : "-"), + expected_node + ); + expected = 0; + } + if( !(gaPageDir[curPos>>22] & PF_PRESENT) ) continue; + if( !(gaPageTable[curPos>>12] & PF_PRESENT) ) continue; + + expected = (gaPageTable[page] & MASK); + MM_GetPageNode(expected, &expected_node); + rangeStart = curPos; + } + if(expected) expected += 0x1000; + } + + if(expected) { + tPAddr orig = gaPageTable[rangeStart>>12]; + Log("0x%08x => 0x%08x - 0x%08x (%s%s%s%s%s) %p", + rangeStart, + orig & ~0xFFF, + curPos - rangeStart, + (orig & PF_NOPAGE ? "p" : "-"), + (orig & PF_COW ? "C" : "-"), + (orig & PF_GLOBAL ? "G" : "-"), + (orig & PF_USER ? "U" : "-"), + (orig & PF_WRITE ? "W" : "-"), + expected_node + ); + expected = 0; + } +} + +/** + * \fn tPAddr MM_Allocate(tVAddr VAddr) + */ +tPAddr MM_Allocate(tVAddr VAddr) +{ + tPAddr paddr; + //ENTER("xVAddr", VAddr); + //__ASM__("xchg %bx,%bx"); + // Check if the directory is mapped + if( gaPageDir[ VAddr >> 22 ] == 0 ) + { + // Allocate directory + paddr = MM_AllocPhys(); + if( paddr == 0 ) { + Warning("MM_Allocate - Out of Memory (Called by %p)", __builtin_return_address(0)); + //LEAVE('i',0); + return 0; + } + // Map and mark as user (if needed) + gaPageDir[ VAddr >> 22 ] = paddr | 3; + if(VAddr < MM_USER_MAX) gaPageDir[ VAddr >> 22 ] |= PF_USER; + + INVLPG( &gaPageDir[ VAddr >> 22 ] ); + memsetd( &gaPageTable[ (VAddr >> 12) & ~0x3FF ], 0, 1024 ); + } + // Check if the page is already allocated + else if( gaPageTable[ VAddr >> 12 ] != 0 ) { + Warning("MM_Allocate - Allocating to used address (%p)", VAddr); + //LEAVE('X', gaPageTable[ VAddr >> 12 ] & ~0xFFF); + return gaPageTable[ VAddr >> 12 ] & ~0xFFF; + } + + // Allocate + paddr = MM_AllocPhys(); + //LOG("paddr = 0x%llx", paddr); + if( paddr == 0 ) { + Warning("MM_Allocate - Out of Memory when allocating at %p (Called by %p)", + VAddr, __builtin_return_address(0)); + //LEAVE('i',0); + return 0; + } + // Map + gaPageTable[ VAddr >> 12 ] = paddr | 3; + // Mark as user + if(VAddr < MM_USER_MAX) gaPageTable[ VAddr >> 12 ] |= PF_USER; + // Invalidate Cache for address + INVLPG( VAddr & ~0xFFF ); + + //LEAVE('X', paddr); + return paddr; +} + +/** + * \fn void MM_Deallocate(tVAddr VAddr) + */ +void MM_Deallocate(tVAddr VAddr) +{ + if( gaPageDir[ VAddr >> 22 ] == 0 ) { + Warning("MM_Deallocate - Directory not mapped"); + return; + } + + if(gaPageTable[ VAddr >> 12 ] == 0) { + Warning("MM_Deallocate - Page is not allocated"); + return; + } + + // Dereference page + MM_DerefPhys( gaPageTable[ VAddr >> 12 ] & ~0xFFF ); + // Clear page + gaPageTable[ VAddr >> 12 ] = 0; +} + +/** + * \fn tPAddr MM_GetPhysAddr(tVAddr Addr) + * \brief Checks if the passed address is accesable + */ +tPAddr MM_GetPhysAddr(tVAddr Addr) +{ + if( !(gaPageDir[Addr >> 22] & 1) ) + return 0; + if( !(gaPageTable[Addr >> 12] & 1) ) + return 0; + return (gaPageTable[Addr >> 12] & ~0xFFF) | (Addr & 0xFFF); +} + +/** + * \fn void MM_SetCR3(Uint CR3) + * \brief Sets the current process space + */ +void MM_SetCR3(Uint CR3) +{ + __ASM__("mov %0, %%cr3"::"r"(CR3)); +} + +/** + * \fn int MM_Map(tVAddr VAddr, tPAddr PAddr) + * \brief Map a physical page to a virtual one + */ +int MM_Map(tVAddr VAddr, tPAddr PAddr) +{ + //ENTER("xVAddr xPAddr", VAddr, PAddr); + // Sanity check + if( PAddr & 0xFFF || VAddr & 0xFFF ) { + Log_Warning("MM_Virt", "MM_Map - Physical or Virtual Addresses are not aligned"); + //LEAVE('i', 0); + return 0; + } + + // Align addresses + PAddr &= ~0xFFF; VAddr &= ~0xFFF; + + // Check if the directory is mapped + if( gaPageDir[ VAddr >> 22 ] == 0 ) + { + tPAddr tmp = MM_AllocPhys(); + if( tmp == 0 ) + return 0; + gaPageDir[ VAddr >> 22 ] = tmp | 3; + + // Mark as user + if(VAddr < MM_USER_MAX) gaPageDir[ VAddr >> 22 ] |= PF_USER; + + INVLPG( &gaPageTable[ (VAddr >> 12) & ~0x3FF ] ); + memsetd( &gaPageTable[ (VAddr >> 12) & ~0x3FF ], 0, 1024 ); + } + // Check if the page is already allocated + else if( gaPageTable[ VAddr >> 12 ] != 0 ) { + Warning("MM_Map - Allocating to used address"); + //LEAVE('i', 0); + return 0; + } + + // Map + gaPageTable[ VAddr >> 12 ] = PAddr | 3; + // Mark as user + if(VAddr < MM_USER_MAX) gaPageTable[ VAddr >> 12 ] |= PF_USER; + + //LOG("gaPageTable[ 0x%x ] = (Uint)%p = 0x%x", + // VAddr >> 12, &gaPageTable[ VAddr >> 12 ], gaPageTable[ VAddr >> 12 ]); + + // Reference + MM_RefPhys( PAddr ); + + //LOG("INVLPG( 0x%x )", VAddr); + INVLPG( VAddr ); + + //LEAVE('i', 1); + return 1; +} + +/** + * \brief Clear user's address space + */ +void MM_ClearUser(void) +{ + Uint i, j; + + for( i = 0; i < (MM_USER_MAX>>22); i ++ ) + { + // Check if directory is not allocated + if( !(gaPageDir[i] & PF_PRESENT) ) { + gaPageDir[i] = 0; + continue; + } + + // Deallocate tables + for( j = 0; j < 1024; j ++ ) + { + if( gaPageTable[i*1024+j] & 1 ) + MM_DerefPhys( gaPageTable[i*1024+j] & ~0xFFF ); + gaPageTable[i*1024+j] = 0; + } + + // Deallocate directory + MM_DerefPhys( gaPageDir[i] & ~0xFFF ); + gaPageDir[i] = 0; + INVLPG( &gaPageTable[i*1024] ); + } + INVLPG( gaPageDir ); +} + +/** + * \brief Deallocate an address space + */ +void MM_ClearSpace(Uint32 CR3) +{ + int i, j; + + if(CR3 == (*gpPageCR3 & ~0xFFF)) { + Log_Error("MMVirt", "Can't clear current address space"); + return ; + } + + if( MM_GetRefCount(CR3) > 1 ) { + MM_DerefPhys(CR3); + Log_Log("MMVirt", "CR3 %P is still referenced, not cleaning (but dereferenced)", CR3); + return ; + } + + Log_Debug("MMVirt", "Clearing out address space 0x%x from 0x%x", CR3, *gpPageCR3); + + GET_TEMP_MAPPING(CR3); + INVLPG( gaTmpDir ); + + for( i = 0; i < 1024; i ++ ) + { + Uint32 *table = &gaTmpTable[i*1024]; + if( !(gaTmpDir[i] & PF_PRESENT) ) + continue ; + + INVLPG( table ); + + if( i < 768 || (i > MM_KERNEL_STACKS >> 22 && i < MM_KERNEL_STACKS_END >> 22) ) + { + for( j = 0; j < 1024; j ++ ) + { + if( !(table[j] & 1) ) + continue; + MM_DerefPhys( table[j] & ~0xFFF ); + } + } + + if( i != (PAGE_TABLE_ADDR >> 22) ) + { + MM_DerefPhys( gaTmpDir[i] & ~0xFFF ); + } + } + + + MM_DerefPhys( CR3 ); + + REL_TEMP_MAPPING(); +} + +/** + * \fn tPAddr MM_Clone(void) + * \brief Clone the current address space + */ +tPAddr MM_Clone(int bNoUserCopy) +{ + Uint i, j; + tPAddr ret; + Uint page = 0; + tVAddr kStackBase = Proc_GetCurThread()->KernelStack - MM_KERNEL_STACK_SIZE; + void *tmp; + + // Create Directory Table + ret = MM_AllocPhys(); + if( ret == 0 ) { + return 0; + } + + // Map + GET_TEMP_MAPPING( ret ); + INVLPG( gaTmpDir ); + memsetd( gaTmpDir, 0, 1024 ); + + if( Threads_GetPID() != 0 && !bNoUserCopy ) + { + // Copy Tables + for( i = 0; i < 768; i ++) + { + // Check if table is allocated + if( !(gaPageDir[i] & PF_PRESENT) ) { + gaTmpDir[i] = 0; + page += 1024; + continue; + } + + // Allocate new table + gaTmpDir[i] = MM_AllocPhys() | (gaPageDir[i] & 7); + INVLPG( &gaTmpTable[page] ); + // Fill + for( j = 0; j < 1024; j ++, page++ ) + { + if( !(gaPageTable[page] & PF_PRESENT) ) { + gaTmpTable[page] = 0; + continue; + } + + // Refrence old page + MM_RefPhys( gaPageTable[page] & ~0xFFF ); + // Add to new table + if(gaPageTable[page] & PF_WRITE) { + gaTmpTable[page] = (gaPageTable[page] & ~PF_WRITE) | PF_COW; + gaPageTable[page] = (gaPageTable[page] & ~PF_WRITE) | PF_COW; + INVLPG( page << 12 ); + } + else + gaTmpTable[page] = gaPageTable[page]; + } + } + } + + // Map in kernel tables (and make fractal mapping) + for( i = 768; i < 1024; i ++ ) + { + // Fractal + if( i == (PAGE_TABLE_ADDR >> 22) ) { + gaTmpDir[ PAGE_TABLE_ADDR >> 22 ] = *gpTmpCR3; + continue; + } + if( i == (TMP_TABLE_ADDR >> 22) ) { + gaTmpDir[ TMP_TABLE_ADDR >> 22 ] = 0; + continue ; + } + + if( gaPageDir[i] == 0 ) { + gaTmpDir[i] = 0; + continue; + } + + //LOG("gaPageDir[%x/4] = 0x%x", i*4, gaPageDir[i]); + MM_RefPhys( gaPageDir[i] & ~0xFFF ); + gaTmpDir[i] = gaPageDir[i]; + } + + // Allocate kernel stack + for(i = MM_KERNEL_STACKS >> 22; i < MM_KERNEL_STACKS_END >> 22; i ++ ) + { + // Check if directory is allocated + if( (gaPageDir[i] & 1) == 0 ) { + gaTmpDir[i] = 0; + continue; + } + + // We don't care about other kernel stacks, just the current one + if( i != kStackBase >> 22 ) { + MM_DerefPhys( gaPageDir[i] & ~0xFFF ); + gaTmpDir[i] = 0; + continue; + } + + // Create a copy + gaTmpDir[i] = MM_AllocPhys() | 3; + INVLPG( &gaTmpTable[i*1024] ); + for( j = 0; j < 1024; j ++ ) + { + // Is the page allocated? If not, skip + if( !(gaPageTable[i*1024+j] & 1) ) { + gaTmpTable[i*1024+j] = 0; + continue; + } + + // We don't care about other kernel stacks + if( ((i*1024+j)*4096 & ~(MM_KERNEL_STACK_SIZE-1)) != kStackBase ) { + gaTmpTable[i*1024+j] = 0; + continue; + } + + // Allocate page + gaTmpTable[i*1024+j] = MM_AllocPhys() | 3; + + MM_RefPhys( gaTmpTable[i*1024+j] & ~0xFFF ); + + tmp = (void *) MM_MapTemp( gaTmpTable[i*1024+j] & ~0xFFF ); + memcpy( tmp, (void *)( (i*1024+j)*0x1000 ), 0x1000 ); + MM_FreeTemp( (Uint)tmp ); + } + } + + REL_TEMP_MAPPING(); + + //LEAVE('x', ret); + return ret; +} + +/** + * \fn tVAddr MM_NewKStack(void) + * \brief Create a new kernel stack + */ +tVAddr MM_NewKStack(void) +{ + tVAddr base; + Uint i; + for(base = MM_KERNEL_STACKS; base < MM_KERNEL_STACKS_END; base += MM_KERNEL_STACK_SIZE) + { + // Check if space is free + if(MM_GetPhysAddr(base) != 0) continue; + // Allocate + //for(i = MM_KERNEL_STACK_SIZE; i -= 0x1000 ; ) + for(i = 0; i < MM_KERNEL_STACK_SIZE; i += 0x1000 ) + { + if( MM_Allocate(base+i) == 0 ) + { + // On error, print a warning and return error + Warning("MM_NewKStack - Out of memory"); + // - Clean up + //for( i += 0x1000 ; i < MM_KERNEL_STACK_SIZE; i += 0x1000 ) + // MM_Deallocate(base+i); + return 0; + } + } + // Success +// Log("MM_NewKStack - Allocated %p", base + MM_KERNEL_STACK_SIZE); + return base+MM_KERNEL_STACK_SIZE; + } + // No stacks left + Log_Warning("MMVirt", "MM_NewKStack - No address space left"); + return 0; +} + +/** + * \fn tVAddr MM_NewWorkerStack() + * \brief Creates a new worker stack + */ +tVAddr MM_NewWorkerStack(Uint *StackContents, size_t ContentsSize) +{ + Uint base, addr; + tVAddr tmpPage; + tPAddr page; + + // TODO: Thread safety + // Find a free worker stack address + for(base = giLastUsedWorker; base < NUM_WORKER_STACKS; base++) + { + // Used block + if( gWorkerStacks[base/32] == -1 ) { + base += 31; base &= ~31; + base --; // Counteracted by the base++ + continue; + } + // Used stack + if( gWorkerStacks[base/32] & (1 << base) ) { + continue; + } + break; + } + if(base >= NUM_WORKER_STACKS) { + Warning("Uh-oh! Out of worker stacks"); + return 0; + } + + // It's ours now! + gWorkerStacks[base/32] |= (1 << base); + // Make life easier for later calls + giLastUsedWorker = base; + // We have one + base = WORKER_STACKS + base * WORKER_STACK_SIZE; + //Log(" MM_NewWorkerStack: base = 0x%x", base); + + // Set the temp fractals to TID0's address space + GET_TEMP_MAPPING( ((Uint)gaInitPageDir - KERNEL_BASE) ); + INVLPG( gaTmpDir ); + + // Check if the directory is mapped (we are assuming that the stacks + // will fit neatly in a directory) + //Log(" MM_NewWorkerStack: gaTmpDir[ 0x%x ] = 0x%x", base>>22, gaTmpDir[ base >> 22 ]); + if(gaTmpDir[ base >> 22 ] == 0) { + gaTmpDir[ base >> 22 ] = MM_AllocPhys() | 3; + INVLPG( &gaTmpTable[ (base>>12) & ~0x3FF ] ); + } + + // Mapping Time! + for( addr = 0; addr < WORKER_STACK_SIZE; addr += 0x1000 ) + { + page = MM_AllocPhys(); + gaTmpTable[ (base + addr) >> 12 ] = page | 3; + } + + // Release temporary fractal + REL_TEMP_MAPPING(); + + // NOTE: Max of 1 page + // `page` is the last allocated page from the previious for loop + tmpPage = MM_MapTemp( page ); + memcpy( (void*)( tmpPage + (0x1000 - ContentsSize) ), StackContents, ContentsSize); + MM_FreeTemp(tmpPage); + + //Log("MM_NewWorkerStack: RETURN 0x%x", base); + return base + WORKER_STACK_SIZE; +} + +/** + * \fn void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask) + * \brief Sets the flags on a page + */ +void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask) +{ + tTabEnt *ent; + if( !(gaPageDir[VAddr >> 22] & 1) ) return ; + if( !(gaPageTable[VAddr >> 12] & 1) ) return ; + + ent = &gaPageTable[VAddr >> 12]; + + // Read-Only + if( Mask & MM_PFLAG_RO ) + { + if( Flags & MM_PFLAG_RO ) { + *ent &= ~PF_WRITE; + } + else { + gaPageDir[VAddr >> 22] |= PF_WRITE; + *ent |= PF_WRITE; + } + } + + // Kernel + if( Mask & MM_PFLAG_KERNEL ) + { + if( Flags & MM_PFLAG_KERNEL ) { + *ent &= ~PF_USER; + } + else { + gaPageDir[VAddr >> 22] |= PF_USER; + *ent |= PF_USER; + } + } + + // Copy-On-Write + if( Mask & MM_PFLAG_COW ) + { + if( Flags & MM_PFLAG_COW ) { + *ent &= ~PF_WRITE; + *ent |= PF_COW; + } + else { + *ent &= ~PF_COW; + *ent |= PF_WRITE; + } + } + + //Log("MM_SetFlags: *ent = 0x%08x, gaPageDir[%i] = 0x%08x", + // *ent, VAddr >> 22, gaPageDir[VAddr >> 22]); +} + +/** + * \brief Get the flags on a page + */ +Uint MM_GetFlags(tVAddr VAddr) +{ + tTabEnt *ent; + Uint ret = 0; + + // Validity Check + if( !(gaPageDir[VAddr >> 22] & 1) ) return 0; + if( !(gaPageTable[VAddr >> 12] & 1) ) return 0; + + ent = &gaPageTable[VAddr >> 12]; + + // Read-Only + if( !(*ent & PF_WRITE) ) ret |= MM_PFLAG_RO; + // Kernel + if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL; + // Copy-On-Write + if( *ent & PF_COW ) ret |= MM_PFLAG_COW; + + return ret; +} + +/** + * \brief Check if the provided buffer is valid + * \return Boolean valid + */ +int MM_IsValidBuffer(tVAddr Addr, size_t Size) +{ + int bIsUser; + int dir, tab; + + Size += Addr & (PAGE_SIZE-1); + Addr &= ~(PAGE_SIZE-1); + + dir = Addr >> 22; + tab = Addr >> 12; + +// Debug("Addr = %p, Size = 0x%x, dir = %i, tab = %i", Addr, Size, dir, tab); + + if( !(gaPageDir[dir] & 1) ) return 0; + if( !(gaPageTable[tab] & 1) ) return 0; + + bIsUser = !!(gaPageTable[tab] & PF_USER); + + while( Size >= PAGE_SIZE ) + { + if( (tab & 1023) == 0 ) + { + dir ++; + if( !(gaPageDir[dir] & 1) ) return 0; + } + + if( !(gaPageTable[tab] & 1) ) return 0; + if( bIsUser && !(gaPageTable[tab] & PF_USER) ) return 0; + + tab ++; + Size -= PAGE_SIZE; + } + return 1; +} + +/** + * \fn tPAddr MM_DuplicatePage(tVAddr VAddr) + * \brief Duplicates a virtual page to a physical one + */ +tPAddr MM_DuplicatePage(tVAddr VAddr) +{ + tPAddr ret; + Uint temp; + int wasRO = 0; + + //ENTER("xVAddr", VAddr); + + // Check if mapped + if( !(gaPageDir [VAddr >> 22] & PF_PRESENT) ) return 0; + if( !(gaPageTable[VAddr >> 12] & PF_PRESENT) ) return 0; + + // Page Align + VAddr &= ~0xFFF; + + // Allocate new page + ret = MM_AllocPhys(); + if( !ret ) { + return 0; + } + + // Write-lock the page (to keep data constistent), saving its R/W state + wasRO = (gaPageTable[VAddr >> 12] & PF_WRITE ? 0 : 1); + gaPageTable[VAddr >> 12] &= ~PF_WRITE; + INVLPG( VAddr ); + + // Copy Data + temp = MM_MapTemp(ret); + memcpy( (void*)temp, (void*)VAddr, 0x1000 ); + MM_FreeTemp(temp); + + // Restore Writeable status + if(!wasRO) gaPageTable[VAddr >> 12] |= PF_WRITE; + INVLPG(VAddr); + + //LEAVE('X', ret); + return ret; +} + +/** + * \fn Uint MM_MapTemp(tPAddr PAddr) + * \brief Create a temporary memory mapping + * \todo Show Luigi Barone (C Lecturer) and see what he thinks + */ +tVAddr MM_MapTemp(tPAddr PAddr) +{ + int i; + + //ENTER("XPAddr", PAddr); + + PAddr &= ~0xFFF; + + //LOG("glTempMappings = %i", glTempMappings); + + for(;;) + { + Mutex_Acquire( &glTempMappings ); + + for( i = 0; i < NUM_TEMP_PAGES; i ++ ) + { + // Check if page used + if(gaPageTable[ (TEMP_MAP_ADDR >> 12) + i ] & 1) continue; + // Mark as used + gaPageTable[ (TEMP_MAP_ADDR >> 12) + i ] = PAddr | 3; + INVLPG( TEMP_MAP_ADDR + (i << 12) ); + //LEAVE('p', TEMP_MAP_ADDR + (i << 12)); + Mutex_Release( &glTempMappings ); + return TEMP_MAP_ADDR + (i << 12); + } + Mutex_Release( &glTempMappings ); + Threads_Yield(); // TODO: Use a sleep queue here instead + } +} + +/** + * \fn void MM_FreeTemp(tVAddr PAddr) + * \brief Free's a temp mapping + */ +void MM_FreeTemp(tVAddr VAddr) +{ + int i = VAddr >> 12; + //ENTER("xVAddr", VAddr); + + if(i >= (TEMP_MAP_ADDR >> 12)) + gaPageTable[ i ] = 0; + + //LEAVE('-'); +} + +/** + * \fn tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number) + * \brief Allocates a contigous number of pages + */ +tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number) +{ + int i, j; + + PAddr &= ~0xFFF; + + // Scan List + for( i = 0; i < NUM_HW_PAGES; i ++ ) + { + // Check if addr used + if( gaPageTable[ (HW_MAP_ADDR >> 12) + i ] & 1 ) + continue; + + // Check possible region + for( j = 0; j < Number && i + j < NUM_HW_PAGES; j ++ ) + { + // If there is an allocated page in the region we are testing, break + if( gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] & 1 ) break; + } + // Is it all free? + if( j == Number ) + { + // Allocate + for( j = 0; j < Number; j++ ) { + MM_RefPhys( PAddr + (j<<12) ); + gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] = (PAddr + (j<<12)) | 3; + } + return HW_MAP_ADDR + (i<<12); + } + } + // If we don't find any, return NULL + return 0; +} + +/** + * \fn tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr) + * \brief Allocates DMA physical memory + * \param Pages Number of pages required + * \param MaxBits Maximum number of bits the physical address can have + * \param PhysAddr Pointer to the location to place the physical address allocated + * \return Virtual address allocate + */ +tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr) +{ + tPAddr maxCheck = (1 << MaxBits); + tPAddr phys; + tVAddr ret; + + ENTER("iPages iMaxBits pPhysAddr", Pages, MaxBits, PhysAddr); + + // Sanity Check + if(MaxBits < 12 || !PhysAddr) { + LEAVE('i', 0); + return 0; + } + + // Bound + if(MaxBits >= PHYS_BITS) maxCheck = -1; + + // Fast Allocate + if(Pages == 1 && MaxBits >= PHYS_BITS) + { + phys = MM_AllocPhys(); + if( !phys ) { + *PhysAddr = 0; + LEAVE_RET('i', 0); + } + *PhysAddr = phys; + ret = MM_MapHWPages(phys, 1); + if(ret == 0) { + MM_DerefPhys(phys); + LEAVE('i', 0); + return 0; + } + LEAVE('x', ret); + return ret; + } + + // Slow Allocate + phys = MM_AllocPhysRange(Pages, MaxBits); + // - Was it allocated? + if(phys == 0) { + LEAVE('i', 0); + return 0; + } + + // Allocated successfully, now map + ret = MM_MapHWPages(phys, Pages); + if( ret == 0 ) { + // If it didn't map, free then return 0 + for(;Pages--;phys+=0x1000) + MM_DerefPhys(phys); + LEAVE('i', 0); + return 0; + } + + *PhysAddr = phys; + LEAVE('x', ret); + return ret; +} + +/** + * \fn void MM_UnmapHWPages(tVAddr VAddr, Uint Number) + * \brief Unmap a hardware page + */ +void MM_UnmapHWPages(tVAddr VAddr, Uint Number) +{ + int i, j; + + //Log_Debug("VirtMem", "MM_UnmapHWPages: (VAddr=0x%08x, Number=%i)", VAddr, Number); + + // Sanity Check + if(VAddr < HW_MAP_ADDR || VAddr+Number*0x1000 > HW_MAP_MAX) return; + + i = VAddr >> 12; + + Mutex_Acquire( &glTempMappings ); // Temp and HW share a directory, so they share a lock + + for( j = 0; j < Number; j++ ) + { + MM_DerefPhys( gaPageTable[ i + j ] & ~0xFFF ); + gaPageTable[ i + j ] = 0; + } + + Mutex_Release( &glTempMappings ); +} + diff --git a/KernelLand/Kernel/arch/x86/pci.c b/KernelLand/Kernel/arch/x86/pci.c new file mode 100644 index 00000000..7a0a2c11 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/pci.c @@ -0,0 +1,22 @@ +/* + * Acess2 + * arch/x86/pci.h - x86 PCI Bus Access + */ +#define DEBUG 0 +#include +#include + +// === CODE === +Uint32 PCI_CfgReadDWord(Uint32 Address) +{ + Address |= 0x80000000; + outd(0xCF8, Address); + return ind(0xCFC); +} + +void PCI_CfgWriteDWord(Uint32 Address, Uint32 Data) +{ + Address |= 0x80000000; + outd(0xCF8, Address); + outd(0xCFC, Data); +} diff --git a/KernelLand/Kernel/arch/x86/proc.asm b/KernelLand/Kernel/arch/x86/proc.asm new file mode 100644 index 00000000..8da79711 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/proc.asm @@ -0,0 +1,375 @@ +; AcessOS Microkernel Version +; Start.asm + +[bits 32] + +%define SAVEFLAG_FPU 0x1 + +KERNEL_BASE equ 0xC0000000 + +KSTACK_USERSTATE_SIZE equ (4+8+1+5)*4 ; SRegs, GPRegs, CPU, IRET + +[section .text] + +[global NewTaskHeader] +NewTaskHeader: + mov eax, [esp] + mov dr0, eax + + mov eax, [esp+4] + add esp, 12 ; Thread, Function, Arg Count + call eax + + push eax ; Ret val + push 0 ; 0 = This Thread + call Threads_Exit + +[extern MM_Clone] +[global Proc_CloneInt] +Proc_CloneInt: + pusha + ; Save RSP + mov eax, [esp+0x20+4] + mov [eax], esp + push DWORD [esp+0x20+12] + call MM_Clone + add esp, 4 + ; Save CR3 + mov esi, [esp+0x20+8] + mov [esi], eax + ; Undo the pusha + add esp, 0x20 + mov eax, .newTask + ret +.newTask: + popa + xor eax, eax + ret + +[global SwitchTasks] +; + 4 = New RSP +; + 8 = Old RSP save loc +; +12 = New RIP +; +16 = Old RIP save loc +; +20 = CR3 +SwitchTasks: + pusha + + ; Old IP + mov eax, [esp+0x20+16] + test eax, eax + jz .nosave + mov DWORD [eax], .restore + ; Old SP + mov eax, [esp+0x20+8] + mov [eax], esp + +.nosave: + mov ecx, [esp+0x20+12] ; New IP + mov eax, [esp+0x20+20] ; New CR3 + mov esp, [esp+0x20+ 4] ; New SP + + test eax, eax + jz .setState + mov cr3, eax + invlpg [esp] + invlpg [esp+0x1000] +.setState: + jmp ecx + +.restore: + popa + xor eax, eax + ret + +[global Proc_InitialiseSSE] +Proc_InitialiseSSE: + mov eax, cr4 + or eax, (1 << 9)|(1 << 10) ; Set OSFXSR and OSXMMEXCPT + mov cr4, eax + mov eax, cr0 + and ax, ~(1 << 2) ; Clear EM + or eax, (1 << 1) ; Set MP + mov eax, cr0 + ret +[global Proc_DisableSSE] +Proc_DisableSSE: + mov eax, cr0 + or ax, 1 << 3 ; Set TS + mov cr0, eax + ret +[global Proc_EnableSSE] +Proc_EnableSSE: + mov eax, cr0 + and ax, ~(1 << 3) ; Clear TS + mov cr0, eax + ret + +[global Proc_SaveSSE] +Proc_SaveSSE: + mov eax, [esp+4] + fxsave [eax] + ret +[global Proc_RestoreSSE] +Proc_RestoreSSE: + mov eax, [esp+4] + fxrstor [eax] + ret + +%if USE_MP +[extern giMP_TimerCount] +[extern gpMP_LocalAPIC] +[extern Isr240.jmp] +[global SetAPICTimerCount] +SetAPICTimerCount: + pusha + push ds + push es + push fs + push gs + + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + mov eax, [gpMP_LocalAPIC] + mov ecx, [eax+0x320] + test ecx, 0x00010000 + jz .setTime + mov DWORD [eax+0x380], 0xFFFFFFFF ; Set Initial Count + mov DWORD [eax+0x320], 0x000000F0 ; Enable the timer on IVT#0xEF (One Shot) + jmp .ret + +.setTime: + ; Get Timer Count + mov ecx, 0xFFFFFFFF + sub ecx, [eax+0x390] + mov DWORD [giMP_TimerCount], ecx + ; Disable APIC Timer + mov DWORD [eax+0x320], 0x000100EF + mov DWORD [eax+0x380], 0 + + ; Update Timer IRQ to the IRQ code + mov eax, SchedulerBase + sub eax, Isr240.jmp+5 + mov DWORD [Isr240.jmp+1], eax + + ;xchg bx, bx ; MAGIC BREAK +.ret: + mov dx, 0x20 + mov al, 0x20 + out dx, al ; ACK IRQ + pop gs + pop fs + pop es + pop ds + popa + add esp, 8 ; CPU ID / Error Code + iret +%endif +; -------------- +; Task Scheduler +; -------------- +[extern Proc_Scheduler] +[global SchedulerBase] +SchedulerBase: + pusha + push ds + push es + push fs + push gs + + pushf + and BYTE [esp+1], 0xFE ; Clear Trap Flag + popf + + mov eax, dr0 + push eax ; Debug Register 0, Current Thread + + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + %if USE_MP + call GetCPUNum + mov ebx, eax + push eax ; Push as argument + %else + push 0 + %endif + + call Proc_Scheduler +[global scheduler_return] +scheduler_return: ; Used by some hackery in Proc_DumpThreadCPUState + + add esp, 4 ; Remove CPU Number (thread is poped later) + + %if USE_MP + test ebx, ebx + jnz .sendEOI + %endif + + mov al, 0x20 + out 0x20, al ; ACK IRQ + %if USE_MP + jmp .ret + +.sendEOI: + mov eax, DWORD [gpMP_LocalAPIC] + mov DWORD [eax+0x0B0], 0 + %endif +.ret: + pop eax ; Debug Register 0, Current Thread + mov dr0, eax + + pop gs + pop fs + pop es + pop ds + + popa + add esp, 4*2 ; CPU ID + Dummy error code + ; No Error code / int num + iret + +[extern Proc_Clone] +[extern Threads_Exit] +[global SpawnTask] +SpawnTask: + ; Call Proc_Clone with Flags=0 + xor eax, eax +; push eax + push eax + call Proc_Clone + add esp, 8 ; Remove arguments from stack + + test eax, eax + jnz .parent + + ; In child, so now set up stack frame + mov ebx, [esp+4] ; Child Function + mov edx, [esp+8] ; Argument + ; Child Function + push edx ; Argument + call ebx ; Function + ; Kill thread once done + push eax ; Exit Code + push 0 ; Kill this thread + call Threads_Exit ; Kill Thread + +.parent: + ret + +; void Proc_ReturnToUser(void *Method, Uint Parameter, tVAddr KernelStack) +; Calls a user fault handler +; +[global Proc_ReturnToUser] +[extern Proc_GetCurThread] +Proc_ReturnToUser: + push ebp + mov ebp, esp + ; [EBP+8]: handler to use + ; [EBP+12]: parameter + ; [EBP+16]: kernel stack top + + ; Get kernel stack + mov eax, [ebp+16] + sub eax, KSTACK_USERSTATE_SIZE + + ; + ; NOTE: This can cause corruption if the signal happens while the user + ; has called a kernel operation. + ; Good thing this can only be called on a user fault. + ; + + ; Validate user ESP + ; - Page Table + mov edx, [eax+KSTACK_USERSTATE_SIZE-12] ; User ESP is at top of kstack - 3*4 + mov ecx, edx + shr ecx, 22 + test BYTE [0xFC3F0000+ecx*4], 1 + jnz .justKillIt + ; - Page + mov ecx, edx + shr ecx, 12 + test BYTE [0xFC000000+ecx*4], 1 + jnz .justKillIt + ; Adjust + sub edx, 8 + ; - Page Table + mov ecx, edx + shr ecx, 22 + test BYTE [0xFC3F0000+ecx*4], 1 + jnz .justKillIt + ; - Page + mov ecx, edx + shr ecx, 12 + test BYTE [0xFC000000+ecx*4], 1 + jnz .justKillIt + + ; Get and alter User SP + mov edi, edx + mov edx, [ebp+12] ; Get parameter + mov [edi+4], edx ; save to user stack + mov [edi], DWORD User_Syscall_RetAndExit ; Return Address + + ; Restore Segment Registers + mov ax, 0x23 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + push 0x23 ; SS + push edi ; ESP + push 0x202 ; EFLAGS (IP and Rsvd) + push 0x1B ; CS + mov eax, [ebp+8] ; Method to call + push eax ; EIP + + iret + + ; Just kill the bleeding thing + ; (I know it calls int 0xAC in kernel mode, but meh) +.justKillIt: + xor eax, eax + xor ebx, ebx + dec ebx ; EBX = -1 + int 0xAC + +[global GetCPUNum] +GetCPUNum: ; TODO: Store in debug registers + mov eax, dr1 + ret + +[extern GetEIP] +[global GetEIP_Sched] +[global GetEIP_Sched_ret] +GetEIP_Sched_ret equ GetEIP_Sched.ret +GetEIP_Sched: + call GetEIP +GetEIP_Sched.ret: + ret + +; Usermode code exported by the kernel +[section .usertext] +; Export a place for the user to jump to to call a syscall +; - Allows the kernel to change the method easily +User_Syscall: + xchg bx, bx ; MAGIC BREAKPOINT + int 0xAC + +; A place to return to and exit +User_Syscall_RetAndExit: + push eax + call User_Syscall_Exit +User_Syscall_Exit: + xor eax, eax + mov ebx, [esp+4] + int 0xAC + +; vim: ft=nasm ts=8 diff --git a/KernelLand/Kernel/arch/x86/proc.c b/KernelLand/Kernel/arch/x86/proc.c new file mode 100644 index 00000000..82a3f409 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/proc.c @@ -0,0 +1,1025 @@ +/* + * AcessOS Microkernel Version + * proc.c + */ +#include +#include +#include +#include +#include +#include +#if USE_MP +# include +#endif +#include +#include + +// === FLAGS === +#define DEBUG_TRACE_SWITCH 0 +#define DEBUG_DISABLE_DOUBLEFAULT 1 +#define DEBUG_VERY_SLOW_PERIOD 0 + +// === CONSTANTS === +// Base is 1193182 +#define TIMER_BASE 1193182 +#if DEBUG_VERY_SLOW_PERIOD +# define TIMER_DIVISOR 1193 //~10Hz switch, with 10 quantum = 1s per thread +#else +# define TIMER_DIVISOR 11932 //~100Hz +#endif + +// === TYPES === +typedef struct sCPU +{ + Uint8 APICID; + Uint8 State; // 0: Unavaliable, 1: Idle, 2: Active + Uint16 Resvd; + tThread *Current; +} tCPU; + +// === IMPORTS === +extern tGDT gGDT[]; +extern tIDT gIDT[]; +extern void APWait(void); // 16-bit AP pause code +extern void APStartup(void); // 16-bit AP startup code +extern Uint GetEIP(void); // start.asm +extern Uint GetEIP_Sched(void); // proc.asm +extern void NewTaskHeader(tThread *Thread, void *Fcn, int nArgs, ...); // Actually takes cdecl args +extern Uint Proc_CloneInt(Uint *ESP, Uint32 *CR3, int bNoUserClone); +extern Uint32 gaInitPageDir[1024]; // start.asm +extern char Kernel_Stack_Top[]; +extern int giNumCPUs; +extern int giNextTID; +extern tThread gThreadZero; +extern tProcess gProcessZero; +extern void Isr8(void); // Double Fault +extern void Proc_ReturnToUser(tVAddr Handler, Uint Argument, tVAddr KernelStack); +extern char scheduler_return[]; // Return address in SchedulerBase +extern char IRQCommon[]; // Common IRQ handler code +extern char IRQCommon_handled[]; // IRQCommon call return location +extern char GetEIP_Sched_ret[]; // GetEIP call return location +extern void SwitchTasks(Uint NewSP, Uint *OldSP, Uint NewIP, Uint *OldIO, Uint CR3); +extern void Proc_InitialiseSSE(void); +extern void Proc_SaveSSE(Uint DestPtr); +extern void Proc_DisableSSE(void); + +// === PROTOTYPES === +//void ArchThreads_Init(void); +#if USE_MP +void MP_StartAP(int CPU); +void MP_SendIPIVector(int CPU, Uint8 Vector); +void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode); +#endif +void Proc_IdleThread(void *Ptr); +//void Proc_Start(void); +//tThread *Proc_GetCurThread(void); +void Proc_ChangeStack(void); +// int Proc_NewKThread(void (*Fcn)(void*), void *Data); +// int Proc_Clone(Uint *Err, Uint Flags); +Uint Proc_MakeUserStack(void); +//void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize); +void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) NORETURN; + int Proc_Demote(Uint *Err, int Dest, tRegs *Regs); +//void Proc_CallFaultHandler(tThread *Thread); +//void Proc_DumpThreadCPUState(tThread *Thread); +void Proc_Scheduler(int CPU); + +// === GLOBALS === +// --- Multiprocessing --- +#if USE_MP +volatile int giNumInitingCPUs = 0; +tMPInfo *gMPFloatPtr = NULL; +volatile Uint32 giMP_TimerCount; // Start Count for Local APIC Timer +tAPIC *gpMP_LocalAPIC = NULL; +Uint8 gaAPIC_to_CPU[256] = {0}; + int giProc_BootProcessorID = 0; +tTSS gaTSSs[MAX_CPUS]; // TSS Array +#endif +tCPU gaCPUs[MAX_CPUS] = { + {.Current = &gThreadZero} + }; +tTSS *gTSSs = NULL; // Pointer to TSS array +tTSS gTSS0 = {0}; +// --- Error Recovery --- +char gaDoubleFaultStack[1024] __attribute__ ((section(".padata"))); +tTSS gDoubleFault_TSS = { + .ESP0 = (Uint)&gaDoubleFaultStack[1024], + .SS0 = 0x10, + .CR3 = (Uint)gaInitPageDir - KERNEL_BASE, + .EIP = (Uint)Isr8, + .ESP = (Uint)&gaDoubleFaultStack[1024], + .CS = 0x08, .SS = 0x10, + .DS = 0x10, .ES = 0x10, + .FS = 0x10, .GS = 0x10, +}; + +// === CODE === +/** + * \fn void ArchThreads_Init(void) + * \brief Starts the process scheduler + */ +void ArchThreads_Init(void) +{ + Uint pos = 0; + + #if USE_MP + tMPTable *mptable; + + // Mark BSP as active + gaCPUs[0].State = 2; + + // -- Initialise Multiprocessing + // Find MP Floating Table + // - EBDA/Last 1Kib (640KiB) + for(pos = KERNEL_BASE|0x9F000; pos < (KERNEL_BASE|0xA0000); pos += 16) { + if( *(Uint*)(pos) == MPPTR_IDENT ) { + Log("Possible %p", pos); + if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue; + gMPFloatPtr = (void*)pos; + break; + } + } + // - Last KiB (512KiB base mem) + if(!gMPFloatPtr) { + for(pos = KERNEL_BASE|0x7F000; pos < (KERNEL_BASE|0x80000); pos += 16) { + if( *(Uint*)(pos) == MPPTR_IDENT ) { + Log("Possible %p", pos); + if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue; + gMPFloatPtr = (void*)pos; + break; + } + } + } + // - BIOS ROM + if(!gMPFloatPtr) { + for(pos = KERNEL_BASE|0xE0000; pos < (KERNEL_BASE|0x100000); pos += 16) { + if( *(Uint*)(pos) == MPPTR_IDENT ) { + Log("Possible %p", pos); + if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue; + gMPFloatPtr = (void*)pos; + break; + } + } + } + + // If the MP Table Exists, parse it + if(gMPFloatPtr) + { + int i; + tMPTable_Ent *ents; + #if DUMP_MP_TABLE + Log("gMPFloatPtr = %p", gMPFloatPtr); + Log("*gMPFloatPtr = {"); + Log("\t.Sig = 0x%08x", gMPFloatPtr->Sig); + Log("\t.MPConfig = 0x%08x", gMPFloatPtr->MPConfig); + Log("\t.Length = 0x%02x", gMPFloatPtr->Length); + Log("\t.Version = 0x%02x", gMPFloatPtr->Version); + Log("\t.Checksum = 0x%02x", gMPFloatPtr->Checksum); + Log("\t.Features = [0x%02x,0x%02x,0x%02x,0x%02x,0x%02x]", + gMPFloatPtr->Features[0], gMPFloatPtr->Features[1], + gMPFloatPtr->Features[2], gMPFloatPtr->Features[3], + gMPFloatPtr->Features[4] + ); + Log("}"); + #endif + + mptable = (void*)( KERNEL_BASE|gMPFloatPtr->MPConfig ); + #if DUMP_MP_TABLE + Log("mptable = %p", mptable); + Log("*mptable = {"); + Log("\t.Sig = 0x%08x", mptable->Sig); + Log("\t.BaseTableLength = 0x%04x", mptable->BaseTableLength); + Log("\t.SpecRev = 0x%02x", mptable->SpecRev); + Log("\t.Checksum = 0x%02x", mptable->Checksum); + Log("\t.OEMID = '%8c'", mptable->OemID); + Log("\t.ProductID = '%8c'", mptable->ProductID); + Log("\t.OEMTablePtr = %p'", mptable->OEMTablePtr); + Log("\t.OEMTableSize = 0x%04x", mptable->OEMTableSize); + Log("\t.EntryCount = 0x%04x", mptable->EntryCount); + Log("\t.LocalAPICMemMap = 0x%08x", mptable->LocalAPICMemMap); + Log("\t.ExtendedTableLen = 0x%04x", mptable->ExtendedTableLen); + Log("\t.ExtendedTableChecksum = 0x%02x", mptable->ExtendedTableChecksum); + Log("}"); + #endif + + gpMP_LocalAPIC = (void*)MM_MapHWPages(mptable->LocalAPICMemMap, 1); + + ents = mptable->Entries; + giNumCPUs = 0; + + for( i = 0; i < mptable->EntryCount; i ++ ) + { + int entSize = 0; + switch( ents->Type ) + { + case 0: // Processor + entSize = 20; + #if DUMP_MP_TABLE + Log("%i: Processor", i); + Log("\t.APICID = %i", ents->Proc.APICID); + Log("\t.APICVer = 0x%02x", ents->Proc.APICVer); + Log("\t.CPUFlags = 0x%02x", ents->Proc.CPUFlags); + Log("\t.CPUSignature = 0x%08x", ents->Proc.CPUSignature); + Log("\t.FeatureFlags = 0x%08x", ents->Proc.FeatureFlags); + #endif + + if( !(ents->Proc.CPUFlags & 1) ) { + Log("DISABLED"); + break; + } + + // Check if there is too many processors + if(giNumCPUs >= MAX_CPUS) { + giNumCPUs ++; // If `giNumCPUs` > MAX_CPUS later, it will be clipped + break; + } + + // Initialise CPU Info + gaAPIC_to_CPU[ents->Proc.APICID] = giNumCPUs; + gaCPUs[giNumCPUs].APICID = ents->Proc.APICID; + gaCPUs[giNumCPUs].State = 0; + giNumCPUs ++; + + // Set BSP Variable + if( ents->Proc.CPUFlags & 2 ) { + giProc_BootProcessorID = giNumCPUs-1; + } + + break; + + #if DUMP_MP_TABLE >= 2 + case 1: // Bus + entSize = 8; + Log("%i: Bus", i); + Log("\t.ID = %i", ents->Bus.ID); + Log("\t.TypeString = '%6C'", ents->Bus.TypeString); + break; + case 2: // I/O APIC + entSize = 8; + Log("%i: I/O APIC", i); + Log("\t.ID = %i", ents->IOAPIC.ID); + Log("\t.Version = 0x%02x", ents->IOAPIC.Version); + Log("\t.Flags = 0x%02x", ents->IOAPIC.Flags); + Log("\t.Addr = 0x%08x", ents->IOAPIC.Addr); + break; + case 3: // I/O Interrupt Assignment + entSize = 8; + Log("%i: I/O Interrupt Assignment", i); + Log("\t.IntType = %i", ents->IOInt.IntType); + Log("\t.Flags = 0x%04x", ents->IOInt.Flags); + Log("\t.SourceBusID = 0x%02x", ents->IOInt.SourceBusID); + Log("\t.SourceBusIRQ = 0x%02x", ents->IOInt.SourceBusIRQ); + Log("\t.DestAPICID = 0x%02x", ents->IOInt.DestAPICID); + Log("\t.DestAPICIRQ = 0x%02x", ents->IOInt.DestAPICIRQ); + break; + case 4: // Local Interrupt Assignment + entSize = 8; + Log("%i: Local Interrupt Assignment", i); + Log("\t.IntType = %i", ents->LocalInt.IntType); + Log("\t.Flags = 0x%04x", ents->LocalInt.Flags); + Log("\t.SourceBusID = 0x%02x", ents->LocalInt.SourceBusID); + Log("\t.SourceBusIRQ = 0x%02x", ents->LocalInt.SourceBusIRQ); + Log("\t.DestLocalAPICID = 0x%02x", ents->LocalInt.DestLocalAPICID); + Log("\t.DestLocalAPICIRQ = 0x%02x", ents->LocalInt.DestLocalAPICIRQ); + break; + default: + Log("%i: Unknown (%i)", i, ents->Type); + break; + #endif + } + ents = (void*)( (Uint)ents + entSize ); + } + + if( giNumCPUs > MAX_CPUS ) { + Warning("Too many CPUs detected (%i), only using %i of them", giNumCPUs, MAX_CPUS); + giNumCPUs = MAX_CPUS; + } + gTSSs = gaTSSs; + } + else { + Log("No MP Table was found, assuming uniprocessor\n"); + giNumCPUs = 1; + gTSSs = &gTSS0; + } + #else + giNumCPUs = 1; + gTSSs = &gTSS0; + #endif + + #if !DEBUG_DISABLE_DOUBLEFAULT + // Initialise Double Fault TSS + gGDT[5].BaseLow = (Uint)&gDoubleFault_TSS & 0xFFFF; + gGDT[5].BaseMid = (Uint)&gDoubleFault_TSS >> 16; + gGDT[5].BaseHi = (Uint)&gDoubleFault_TSS >> 24; + + // Set double fault IDT to use the new TSS + gIDT[8].OffsetLo = 0; + gIDT[8].CS = 5<<3; + gIDT[8].Flags = 0x8500; + gIDT[8].OffsetHi = 0; + #endif + + // Set timer frequency + outb(0x43, 0x34); // Set Channel 0, Low/High, Rate Generator + outb(0x40, TIMER_DIVISOR&0xFF); // Low Byte of Divisor + outb(0x40, (TIMER_DIVISOR>>8)&0xFF); // High Byte + + Log_Debug("Proc", "PIT Frequency %i.%03i Hz", + TIMER_BASE/TIMER_DIVISOR, + ((Uint64)TIMER_BASE*1000/TIMER_DIVISOR)%1000 + ); + + #if USE_MP + // Get the count setting for APIC timer + Log("Determining APIC Count"); + __asm__ __volatile__ ("sti"); + while( giMP_TimerCount == 0 ) __asm__ __volatile__ ("hlt"); + __asm__ __volatile__ ("cli"); + Log("APIC Count %i", giMP_TimerCount); + { + Uint64 freq = giMP_TimerCount; + freq *= TIMER_BASE; + freq /= TIMER_DIVISOR; + if( (freq /= 1000) < 2*1000) + Log("Bus Frequency %i KHz", freq); + else if( (freq /= 1000) < 2*1000) + Log("Bus Frequency %i MHz", freq); + else if( (freq /= 1000) < 2*1000) + Log("Bus Frequency %i GHz", freq); + else + Log("Bus Frequency %i THz", freq); + } + + // Initialise Normal TSS(s) + for(pos=0;pos> 16) & 0xFFFF; + gGDT[6+pos].BaseHi = ((Uint)(&gTSSs[pos])) >> 24; + #if USE_MP + } + #endif + + // Load the BSP's TSS + __asm__ __volatile__ ("ltr %%ax"::"a"(0x30)); + // Set Current Thread and CPU Number in DR0 and DR1 + __asm__ __volatile__ ("mov %0, %%db0"::"r"(&gThreadZero)); + __asm__ __volatile__ ("mov %0, %%db1"::"r"(0)); + + gaCPUs[0].Current = &gThreadZero; + gThreadZero.CurCPU = 0; + + gProcessZero.MemState.CR3 = (Uint)gaInitPageDir - KERNEL_BASE; + + // Create Per-Process Data Block + if( !MM_Allocate(MM_PPD_CFG) ) + { + Panic("OOM - No space for initial Per-Process Config"); + } + + // Initialise SSE support + Proc_InitialiseSSE(); + + // Change Stacks + Proc_ChangeStack(); +} + +#if USE_MP +/** + * \brief Start an AP + */ +void MP_StartAP(int CPU) +{ + Log_Log("Proc", "Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID); + + // Set location of AP startup code and mark for a warm restart + *(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APWait - (KERNEL_BASE|0xFFFF0); + *(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF; + outb(0x70, 0x0F); outb(0x71, 0x0A); // Set warm reset flag + MP_SendIPI(gaCPUs[CPU].APICID, 0, 5); // Init IPI + + // Delay + inb(0x80); inb(0x80); inb(0x80); inb(0x80); + + // TODO: Use a better address, preferably registered with the MM + // - MM_AllocDMA mabye? + // Create a far jump + *(Uint8*)(KERNEL_BASE|0x11000) = 0xEA; // Far JMP + *(Uint16*)(KERNEL_BASE|0x11001) = (Uint)&APStartup - (KERNEL_BASE|0xFFFF0); // IP + *(Uint16*)(KERNEL_BASE|0x11003) = 0xFFFF; // CS + // Send a Startup-IPI to make the CPU execute at 0x11000 (which we + // just filled) + MP_SendIPI(gaCPUs[CPU].APICID, 0x11, 6); // StartupIPI + + giNumInitingCPUs ++; +} + +void MP_SendIPIVector(int CPU, Uint8 Vector) +{ + MP_SendIPI(gaCPUs[CPU].APICID, Vector, 0); +} + +/** + * \brief Send an Inter-Processor Interrupt + * \param APICID Processor's Local APIC ID + * \param Vector Argument of some kind + * \param DeliveryMode Type of signal + * \note 3A 10.5 "APIC/Handling Local Interrupts" + */ +void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode) +{ + Uint32 val; + + // Hi + val = (Uint)APICID << 24; +// Log("%p = 0x%08x", &gpMP_LocalAPIC->ICR[1], val); + gpMP_LocalAPIC->ICR[1].Val = val; + // Low (and send) + val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF); +// Log("%p = 0x%08x", &gpMP_LocalAPIC->ICR[0], val); + gpMP_LocalAPIC->ICR[0].Val = val; +} +#endif + +void Proc_IdleThread(void *Ptr) +{ + tCPU *cpu = &gaCPUs[GetCPUNum()]; + cpu->Current->ThreadName = strdup("Idle Thread"); + Threads_SetPriority( cpu->Current, -1 ); // Never called randomly + cpu->Current->Quantum = 1; // 1 slice quantum + for(;;) { + __asm__ __volatile__ ("sti"); // Make sure interrupts are enabled + __asm__ __volatile__ ("hlt"); // Make sure interrupts are enabled + Proc_Reschedule(); + } +} + +/** + * \fn void Proc_Start(void) + * \brief Start process scheduler + */ +void Proc_Start(void) +{ + int tid; + #if USE_MP + int i; + #endif + + #if USE_MP + // Start APs + for( i = 0; i < giNumCPUs; i ++ ) + { + if(i) gaCPUs[i].Current = NULL; + + // Create Idle Task + tid = Proc_NewKThread(Proc_IdleThread, &gaCPUs[i]); + + // Start the AP + if( i != giProc_BootProcessorID ) { + MP_StartAP( i ); + } + } + + // BSP still should run the current task + gaCPUs[0].Current = &gThreadZero; + + // Start interrupts and wait for APs to come up + Log_Debug("Proc", "Waiting for APs to come up"); + __asm__ __volatile__ ("sti"); + while( giNumInitingCPUs ) __asm__ __volatile__ ("hlt"); + #else + // Create Idle Task + tid = Proc_NewKThread(Proc_IdleThread, &gaCPUs[0]); +// gaCPUs[0].IdleThread = Threads_GetThread(tid); + + // Set current task + gaCPUs[0].Current = &gThreadZero; + + // Start Interrupts (and hence scheduler) + __asm__ __volatile__("sti"); + #endif + MM_FinishVirtualInit(); +} + +/** + * \fn tThread *Proc_GetCurThread(void) + * \brief Gets the current thread + */ +tThread *Proc_GetCurThread(void) +{ + #if USE_MP + return gaCPUs[ GetCPUNum() ].Current; + #else + return gaCPUs[ 0 ].Current; + #endif +} + +/** + * \fn void Proc_ChangeStack(void) + * \brief Swaps the current stack for a new one (in the proper stack reigon) + */ +void Proc_ChangeStack(void) +{ + Uint esp, ebp; + Uint tmpEbp, oldEsp; + Uint curBase, newBase; + + __asm__ __volatile__ ("mov %%esp, %0":"=r"(esp)); + __asm__ __volatile__ ("mov %%ebp, %0":"=r"(ebp)); + + oldEsp = esp; + + // Create new KStack + newBase = MM_NewKStack(); + // Check for errors + if(newBase == 0) { + Panic("What the?? Unable to allocate space for initial kernel stack"); + return; + } + + curBase = (Uint)&Kernel_Stack_Top; + + LOG("curBase = 0x%x, newBase = 0x%x", curBase, newBase); + + // Get ESP as a used size + esp = curBase - esp; + LOG("memcpy( %p, %p, 0x%x )", (void*)(newBase - esp), (void*)(curBase - esp), esp ); + // Copy used stack + memcpy( (void*)(newBase - esp), (void*)(curBase - esp), esp ); + // Get ESP as an offset in the new stack + esp = newBase - esp; + // Adjust EBP + ebp = newBase - (curBase - ebp); + + // Repair EBPs & Stack Addresses + // Catches arguments also, but may trash stack-address-like values + for(tmpEbp = esp; tmpEbp < newBase; tmpEbp += 4) + { + if(oldEsp < *(Uint*)tmpEbp && *(Uint*)tmpEbp < curBase) + *(Uint*)tmpEbp += newBase - curBase; + } + + Proc_GetCurThread()->KernelStack = newBase; + + __asm__ __volatile__ ("mov %0, %%esp"::"r"(esp)); + __asm__ __volatile__ ("mov %0, %%ebp"::"r"(ebp)); +} + +void Proc_ClearProcess(tProcess *Process) +{ + MM_ClearSpace(Process->MemState.CR3); +} + +void Proc_ClearThread(tThread *Thread) +{ + if(Thread->SavedState.SSE) { + free(Thread->SavedState.SSE); + Thread->SavedState.SSE = NULL; + } +} + +tTID Proc_NewKThread(void (*Fcn)(void*), void *Data) +{ + Uint esp; + tThread *newThread, *cur; + + cur = Proc_GetCurThread(); + newThread = Threads_CloneTCB(0); + if(!newThread) return -1; + + // Create new KStack + newThread->KernelStack = MM_NewKStack(); + // Check for errors + if(newThread->KernelStack == 0) { + free(newThread); + return -1; + } + + esp = newThread->KernelStack; + *(Uint*)(esp-=4) = (Uint)Data; // Data (shadowed) + *(Uint*)(esp-=4) = 1; // Number of params + *(Uint*)(esp-=4) = (Uint)Fcn; // Function to call + *(Uint*)(esp-=4) = (Uint)newThread; // Thread ID + + newThread->SavedState.ESP = esp; + newThread->SavedState.EIP = (Uint)&NewTaskHeader; + newThread->SavedState.SSE = NULL; +// Log("New (KThread) %p, esp = %p", newThread->SavedState.EIP, newThread->SavedState.ESP); + +// MAGIC_BREAK(); + Threads_AddActive(newThread); + + return newThread->TID; +} + +/** + * \fn int Proc_Clone(Uint *Err, Uint Flags) + * \brief Clone the current process + */ +tPID Proc_Clone(Uint Flags) +{ + tThread *newThread; + tThread *cur = Proc_GetCurThread(); + Uint eip; + + // Sanity, please + if( !(Flags & CLONE_VM) ) { + Log_Error("Proc", "Proc_Clone: Don't leave CLONE_VM unset, use Proc_NewKThread instead"); + return -1; + } + + // New thread + newThread = Threads_CloneTCB(Flags); + if(!newThread) return -1; + + newThread->KernelStack = cur->KernelStack; + + // Clone state + eip = Proc_CloneInt(&newThread->SavedState.ESP, &newThread->Process->MemState.CR3, Flags & CLONE_NOUSER); + if( eip == 0 ) { + return 0; + } + newThread->SavedState.EIP = eip; + newThread->SavedState.SSE = NULL; + newThread->SavedState.bSSEModified = 0; + + // Check for errors + if( newThread->Process->MemState.CR3 == 0 ) { + Log_Error("Proc", "Proc_Clone: MM_Clone failed"); + Threads_Delete(newThread); + return -1; + } + + // Add the new thread to the run queue + Threads_AddActive(newThread); + return newThread->TID; +} + +/** + * \fn int Proc_SpawnWorker(void) + * \brief Spawns a new worker thread + */ +int Proc_SpawnWorker(void (*Fcn)(void*), void *Data) +{ + tThread *new; + Uint stack_contents[4]; + + // Create new thread + new = Threads_CloneThreadZero(); + if(!new) { + Warning("Proc_SpawnWorker - Out of heap space!\n"); + return -1; + } + + // Create the stack contents + stack_contents[3] = (Uint)Data; + stack_contents[2] = 1; + stack_contents[1] = (Uint)Fcn; + stack_contents[0] = (Uint)new; + + // Create a new worker stack (in PID0's address space) + new->KernelStack = MM_NewWorkerStack(stack_contents, sizeof(stack_contents)); + + // Save core machine state + new->SavedState.ESP = new->KernelStack - sizeof(stack_contents); + new->SavedState.EIP = (Uint)NewTaskHeader; + new->SavedState.SSE = NULL; + new->SavedState.bSSEModified = 0; + + // Mark as active + new->Status = THREAD_STAT_PREINIT; + Threads_AddActive( new ); + + return new->TID; +} + +/** + * \fn Uint Proc_MakeUserStack(void) + * \brief Creates a new user stack + */ +Uint Proc_MakeUserStack(void) +{ + int i; + Uint base = USER_STACK_TOP - USER_STACK_SZ; + + // Check Prospective Space + for( i = USER_STACK_SZ >> 12; i--; ) + if( MM_GetPhysAddr( base + (i<<12) ) != 0 ) + break; + + if(i != -1) return 0; + + // Allocate Stack - Allocate incrementally to clean up MM_Dump output + for( i = 0; i < USER_STACK_SZ/0x1000; i++ ) + { + if( !MM_Allocate( base + (i<<12) ) ) + { + Warning("OOM: Proc_MakeUserStack"); + return 0; + } + } + + return base + USER_STACK_SZ; +} + +void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) +{ + Uint *stack; + int i; + const char **envp = NULL; + Uint16 ss, cs; + + // Copy data to the user stack and free original buffer + stack = (void*)Proc_MakeUserStack(); + stack -= (DataSize+sizeof(*stack)-1)/sizeof(*stack); + memcpy( stack, ArgV, DataSize ); + free(ArgV); + + // Adjust Arguments and environment + if( DataSize ) + { + Uint delta = (Uint)stack - (Uint)ArgV; + ArgV = (const char**)stack; + for( i = 0; ArgV[i]; i++ ) ArgV[i] += delta; + envp = &ArgV[i+1]; + for( i = 0; envp[i]; i++ ) envp[i] += delta; + } + + // User Mode Segments + ss = 0x23; cs = 0x1B; + + // Arguments + *--stack = (Uint)envp; + *--stack = (Uint)ArgV; + *--stack = (Uint)ArgC; + *--stack = Base; + + Proc_StartProcess(ss, (Uint)stack, 0x202, cs, Entrypoint); +} + +void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) +{ + Uint *stack = (void*)Stack; + *--stack = SS; //Stack Segment + *--stack = Stack; //Stack Pointer + *--stack = Flags; //EFLAGS (Resvd (0x2) and IF (0x20)) + *--stack = CS; //Code Segment + *--stack = IP; //EIP + //PUSHAD + *--stack = 0xAAAAAAAA; // eax + *--stack = 0xCCCCCCCC; // ecx + *--stack = 0xDDDDDDDD; // edx + *--stack = 0xBBBBBBBB; // ebx + *--stack = 0xD1D1D1D1; // edi + *--stack = 0x54545454; // esp - NOT POPED + *--stack = 0x51515151; // esi + *--stack = 0xB4B4B4B4; // ebp + //Individual PUSHs + *--stack = SS; // ds + *--stack = SS; // es + *--stack = SS; // fs + *--stack = SS; // gs + + __asm__ __volatile__ ( + "mov %%eax,%%esp;\n\t" // Set stack pointer + "pop %%gs;\n\t" + "pop %%fs;\n\t" + "pop %%es;\n\t" + "pop %%ds;\n\t" + "popa;\n\t" + "iret;\n\t" : : "a" (stack)); + for(;;); +} + +/** + * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs) + * \brief Demotes a process to a lower permission level + * \param Err Pointer to user's errno + * \param Dest New Permission Level + * \param Regs Pointer to user's register structure + */ +int Proc_Demote(Uint *Err, int Dest, tRegs *Regs) +{ + int cpl = Regs->cs & 3; + // Sanity Check + if(Dest > 3 || Dest < 0) { + *Err = -EINVAL; + return -1; + } + + // Permission Check + if(cpl > Dest) { + *Err = -EACCES; + return -1; + } + + // Change the Segment Registers + Regs->cs = (((Dest+1)<<4) | Dest) - 8; + Regs->ss = ((Dest+1)<<4) | Dest; + // Check if the GP Segs are GDT, then change them + if(!(Regs->ds & 4)) Regs->ds = ((Dest+1)<<4) | Dest; + if(!(Regs->es & 4)) Regs->es = ((Dest+1)<<4) | Dest; + if(!(Regs->fs & 4)) Regs->fs = ((Dest+1)<<4) | Dest; + if(!(Regs->gs & 4)) Regs->gs = ((Dest+1)<<4) | Dest; + + return 0; +} + +/** + * \brief Calls a signal handler in user mode + * \note Used for signals + */ +void Proc_CallFaultHandler(tThread *Thread) +{ + // Rewinds the stack and calls the user function + // Never returns + Proc_ReturnToUser( Thread->FaultHandler, Thread->CurFaultNum, Thread->KernelStack ); + for(;;); +} + +void Proc_DumpThreadCPUState(tThread *Thread) +{ + if( Thread->CurCPU > -1 ) + { + int maxBacktraceDistance = 6; + tRegs *regs = NULL; + Uint32 *stack; + + if( Thread->CurCPU != GetCPUNum() ) { + Log(" Currently running"); + return ; + } + + // Backtrace to find the IRQ entrypoint + // - This will usually only be called by an IRQ, so this should + // work + __asm__ __volatile__ ("mov %%ebp, %0" : "=r" (stack)); + while( maxBacktraceDistance -- ) + { + // [ebp] = oldEbp + // [ebp+4] = retaddr + + if( stack[1] == (tVAddr)&IRQCommon_handled ) { + regs = (void*)stack[2]; + break; + } + + stack = (void*)stack[0]; + } + + if( !regs ) { + Log(" Unable to find IRQ Entry"); + return ; + } + + Log(" at %04x:%08x", regs->cs, regs->eip); + return ; + } + + tVAddr diffFromScheduler = Thread->SavedState.EIP - (tVAddr)SwitchTasks; + tVAddr diffFromClone = Thread->SavedState.EIP - (tVAddr)Proc_CloneInt; + tVAddr diffFromSpawn = Thread->SavedState.EIP - (tVAddr)NewTaskHeader; + + if( diffFromClone > 0 && diffFromClone < 40 ) // When I last checked, .newTask was at .+27 + { + Log(" Creating process"); + return ; + } + + if( diffFromSpawn == 0 ) + { + Log(" Creating thread"); + return ; + } + + if( diffFromScheduler > 0 && diffFromScheduler < 128 ) // When I last checked, GetEIP was at .+0x30 + { + // Scheduled out + Log(" At %04x:%08x", Thread->SavedState.UserCS, Thread->SavedState.UserEIP); + return ; + } + + Log(" Just created (unknown %p)", Thread->SavedState.EIP); +} + +void Proc_Reschedule(void) +{ + tThread *nextthread, *curthread; + int cpu = GetCPUNum(); + + // TODO: Wait for the lock? + if(IS_LOCKED(&glThreadListLock)) return; + + curthread = Proc_GetCurThread(); + + nextthread = Threads_GetNextToRun(cpu, curthread); + + if(!nextthread || nextthread == curthread) + return ; + + #if DEBUG_TRACE_SWITCH + // HACK: Ignores switches to the idle threads + if( nextthread->TID == 0 || nextthread->TID > giNumCPUs ) + { + LogF("\nSwitching CPU %i to %p (%i %s) - CR3 = 0x%x, EIP = %p, ESP = %p\n", + GetCPUNum(), + nextthread, nextthread->TID, nextthread->ThreadName, + nextthread->Process->MemState.CR3, + nextthread->SavedState.EIP, + nextthread->SavedState.ESP + ); + LogF("OldCR3 = %P\n", curthread->Process->MemState.CR3); + } + #endif + + // Update CPU state + gaCPUs[cpu].Current = nextthread; + gTSSs[cpu].ESP0 = nextthread->KernelStack-4; + __asm__ __volatile__("mov %0, %%db0\n\t" : : "r"(nextthread) ); + + // Save FPU/MMX/XMM/SSE state + if( curthread && curthread->SavedState.SSE ) + { + Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF ); + curthread->SavedState.bSSEModified = 0; + Proc_DisableSSE(); + } + + if( curthread ) + { + SwitchTasks( + nextthread->SavedState.ESP, &curthread->SavedState.ESP, + nextthread->SavedState.EIP, &curthread->SavedState.EIP, + nextthread->Process->MemState.CR3 + ); + } + else + { + SwitchTasks( + nextthread->SavedState.ESP, 0, + nextthread->SavedState.EIP, 0, + nextthread->Process->MemState.CR3 + ); + } + + return ; +} + +/** + * \fn void Proc_Scheduler(int CPU) + * \brief Swap current thread and clears dead threads + */ +void Proc_Scheduler(int CPU) +{ +#if 0 + tThread *thread; + + // If the spinlock is set, let it complete + if(IS_LOCKED(&glThreadListLock)) return; + + // Get current thread + thread = gaCPUs[CPU].Current; + + if( thread ) + { + tRegs *regs; + Uint ebp; + // Reduce remaining quantum and continue timeslice if non-zero + if( thread->Remaining-- ) + return; + // Reset quantum for next call + thread->Remaining = thread->Quantum; + + // TODO: Make this more stable somehow + __asm__ __volatile__("mov %%ebp, %0" : "=r" (ebp)); + regs = (tRegs*)(ebp+(2+2)*4); // EBP,Ret + CPU,CurThread + thread->SavedState.UserCS = regs->cs; + thread->SavedState.UserEIP = regs->eip; + + if(thread->bInstrTrace) { + regs->eflags |= 0x100; // Set TF + Log("%p De-scheduled", thread); + } + else + regs->eflags &= ~0x100; // Clear TF + } + + // TODO: Ack timer? + #if USE_MP + if( GetCPUNum() ) + gpMP_LocalAPIC->EOI.Val = 0; + else + #endif + outb(0x20, 0x20); + __asm__ __volatile__ ("sti"); + Proc_Reschedule(); +#endif +} + +// === EXPORTS === +EXPORT(Proc_SpawnWorker); diff --git a/KernelLand/Kernel/arch/x86/start.asm b/KernelLand/Kernel/arch/x86/start.asm new file mode 100644 index 00000000..b6026de6 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/start.asm @@ -0,0 +1,285 @@ +; AcessOS Microkernel Version +; Start.asm + +[bits 32] + +KERNEL_BASE equ 0xC0000000 +%define MAX_CPUS 16 + +[extern __load_addr] +[extern __bss_start] +[extern gKernelEnd] +[section .multiboot] +mboot: + ; Multiboot macros to make a few lines later more readable + MULTIBOOT_PAGE_ALIGN equ 1<<0 + MULTIBOOT_MEMORY_INFO equ 1<<1 + MULTIBOOT_HEADER_MAGIC equ 0x1BADB002 + MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO + MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) + + ; This is the GRUB Multiboot header. A boot signature + dd MULTIBOOT_HEADER_MAGIC + dd MULTIBOOT_HEADER_FLAGS + dd MULTIBOOT_CHECKSUM + dd mboot; - KERNEL_BASE ;Location of Multiboot Header + +; Multiboot 2 Header +;mboot2: +; MULTIBOOT2_HEADER_MAGIC equ 0xE85250D6 +; MULTIBOOT2_HEADER_ARCH equ 0 +; MULTIBOOT2_HEADER_LENGTH equ (mboot2_end-mboot2) +; MULTIBOOT2_CHECKSUM equ -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT2_HEADER_ARCH + MULTIBOOT2_HEADER_LENGTH) +; +; dd MULTIBOOT2_HEADER_MAGIC +; dd MULTIBOOT2_HEADER_ARCH +; dd MULTIBOOT2_HEADER_LENGTH +; dd MULTIBOOT2_CHECKSUM +; ; MBoot2 Address Header +; dw 2, 0 +; dd 8 + 16 +; dd mboot2 ; Location of Multiboot Header +; dd __load_addr - KERNEL_BASE ; Kernel Load base +; dd __bss_start - KERNEL_BASE ; Kernel Data End +; dd gKernelEnd - KERNEL_BASE ; Kernel BSS End +; ; MBoot2 Entry Point Tag +; dw 3, 0 +; dd 8 + 4 +; dd start - KERNEL_BASE +; ; MBoot2 Module Alignment Tag +; dw 6, 0 +; dd 12 ; ??? +; dd 0 ; Search me, seems it wants padding +; ; Terminator +; dw 0, 0 +; dd 8 +;mboot2_end: + +[section .text] +[extern kmain] +[global start] +start: + ; Just show we're here + mov WORD [0xB8000], 0x0741 ; 'A' + + ; Set up stack + mov esp, Kernel_Stack_Top + + ; Start Paging + mov ecx, gaInitPageDir - KERNEL_BASE + mov cr3, ecx + mov ecx, cr0 + or ecx, 0x80010000 ; PG and WP + mov cr0, ecx + + mov WORD [0xB8002], 0x0763 ; 'c' + + ; Set GDT + lgdt [gGDTPtr] + mov cx, 0x10 ; PL0 Data + mov ss, cx + mov ds, cx + mov es, cx + mov gs, cx + mov fs, cx + mov WORD [0xB8004], 0x0765 ; 'e' + jmp 0x08:.higher_half +.higher_half: + + mov WORD [0xB8006], 0x0773 ; 's' + mov WORD [0xB8008], 0x0773 ; 's' + + ; Call the kernel + push ebx ; Multiboot Info + push eax ; Multiboot Magic Value + mov WORD [0xB800A], 0x0732 ; '2' + call kmain + + ; Halt the Machine + cli +.hlt: + hlt + jmp .hlt + +; +; Multiprocessing AP Startup Code (Must be within 0 - 0x10FFF0) +; +%if USE_MP +[extern gIDTPtr] +[extern gpMP_LocalAPIC] +[extern giMP_TimerCount] +[extern gaAPIC_to_CPU] +[extern gaCPUs] +[extern giNumInitingCPUs] +[extern MM_NewKStack] +[extern Proc_InitialiseSSE] + +lGDTPtr: ; Local GDT Pointer + dw 3*8-1 + dd gGDT-KERNEL_BASE + +[bits 16] +[global APWait] +APWait: + ;xchg bx, bx +.hlt: + ;hlt + jmp .hlt +[extern Proc_Reschedule] +[global APStartup] +APStartup: + ;xchg bx, bx ; MAGIC BREAK! + ; Load initial GDT + mov ax, 0xFFFF + mov ds, ax + lgdt [DWORD ds:lGDTPtr-KERNEL_BASE-0xFFFF0] + ; Enable PMode in CR0 + mov eax, cr0 + or al, 1 + mov cr0, eax + ; Jump into PMode + jmp 08h:DWORD .ProtectedMode-KERNEL_BASE +[bits 32] +.ProtectedMode: + ; Load segment registers + mov ax, 0x10 + mov ss, ax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + ; Start Paging + mov eax, gaInitPageDir - KERNEL_BASE + mov cr3, eax + mov eax, cr0 + or eax, 0x80010000 ; PG and WP + mov cr0, eax + ; Jump to higher half + lea eax, [.higherHalf] + jmp eax +.higherHalf: + ; Load True GDT & IDT + lgdt [gGDTPtr] + lidt [gIDTPtr] + + mov ebp, [gpMP_LocalAPIC] + mov ebx, [ebp+0x20] ; Read ID + shr ebx, 24 + ;xchg bx, bx ; MAGIC BREAK + ; BL is now local APIC ID + mov cl, BYTE [gaAPIC_to_CPU+ebx] + xor ebx, ebx + mov bl, cl + ; BL is now the CPU ID + mov dr1, ebx ; Save the CPU number to a debug register + ; Mark the CPU as up + mov BYTE [gaCPUs+ebx*8+1], 1 + ; Decrement the remaining CPU count + dec DWORD [giNumInitingCPUs] + + ; Create a stack + lea edx, [ebx+1] + shl edx, 5+2 ; *32 *4 + lea esp, [gInitAPStacks+edx] + call MM_NewKStack + mov esp, eax + + ; Set TSS + lea ecx, [ebx*8+0x30] + ltr cx + + ;xchg bx, bx ; MAGIC_BREAK + ; Enable Local APIC + mov DWORD [ebp+0x0F0], 0x000001EF ; Spurious Interrupt Vector (0xEF) + mov ecx, DWORD [giMP_TimerCount] + mov DWORD [ebp+0x380], ecx ; Set Initial Count + mov DWORD [ebp+0x320], 0x000200EE ; Enable timer on IVT#0xEE + mov DWORD [ebp+0x330], 0x000100E0 ; ##Enable thermal sensor on IVT#0xE0 + mov DWORD [ebp+0x340], 0x000100D0 ; ##Enable performance counters on IVT#0xD0 + mov DWORD [ebp+0x350], 0x000100D1 ; ##Enable LINT0 on IVT#0xD1 + mov DWORD [ebp+0x360], 0x000100D2 ; ##Enable LINT1 on IVT#0xD2 + mov DWORD [ebp+0x370], 0x000100E1 ; ##Enable Error on IVT#0xE1 + mov DWORD [ebp+0x0B0], 0 ; Send an EOI (just in case) + + ; Initialise SSE support + call Proc_InitialiseSSE + + ; CPU is now marked as initialised + +.hlt: + sti + call Proc_Reschedule + hlt + jmp .hlt +%endif + +[global GetEIP] +GetEIP: + mov eax, [esp] + ret + +; int CallWithArgArray(void *Ptr, int NArgs, Uint *Args) +; Call a function passing the array as arguments +[global CallWithArgArray] +CallWithArgArray: + push ebp + mov ebp, esp + mov ecx, [ebp+12] ; Get NArgs + mov edx, [ebp+16] + +.top: + mov eax, [edx+ecx*4-4] + push eax + loop .top + + mov eax, [ebp+8] + call eax + lea esp, [ebp] + pop ebp + ret + +[section .data] +; GDT +GDT_SIZE equ (1+2*2+1+MAX_CPUS)*8 +[global gGDT] +gGDT: + ; PL0 - Kernel + ; PL3 - User + dd 0x00000000, 0x00000000 ; 00 NULL Entry + dd 0x0000FFFF, 0x00CF9A00 ; 08 PL0 Code + dd 0x0000FFFF, 0x00CF9200 ; 10 PL0 Data + dd 0x0000FFFF, 0x00CFFA00 ; 18 PL3 Code + dd 0x0000FFFF, 0x00CFF200 ; 20 PL3 Data + dd 26*4-1, 0x00408900 ; 28 Double Fault TSS + times MAX_CPUS dd 26*4-1, 0x00408900 ; 30+ TSSes +[global gGDTPtr] +gGDTPtr: + dw GDT_SIZE-1 + dd gGDT + +[section .initpd] +[global gaInitPageDir] +[global gaInitPageTable] +align 4096 +gaInitPageDir: + dd gaInitPageTable-KERNEL_BASE+3 ; 0x000 - Low kernel + times 0x300-0x000-1 dd 0 + dd gaInitPageTable-KERNEL_BASE+3 ; 0xC00 - High kernel + times 0x3F0-0x300-1 dd 0 + dd gaInitPageDir-KERNEL_BASE+3 ; 0xFC0 - Fractal + times 0x400-0x3F0-1 dd 0 +align 4096 +gaInitPageTable: + %assign i 0 + %rep 1024 + dd i*0x1000+3 + %assign i i+1 + %endrep +[global Kernel_Stack_Top] +ALIGN 4096 + times 1024 dd 0 +Kernel_Stack_Top: +gInitAPStacks: + times 32*MAX_CPUS dd 0 + +; vim: ft=nasm ts=8 diff --git a/KernelLand/Kernel/arch/x86/time.c b/KernelLand/Kernel/arch/x86/time.c new file mode 100644 index 00000000..9b59b30b --- /dev/null +++ b/KernelLand/Kernel/arch/x86/time.c @@ -0,0 +1,127 @@ +/* + * Acess2 Kernel + * Timekeeping + * arch/x86/time.c + */ +#include + +// === MACROS === +#define TIMER_QUANTUM 100 +// 2^(15-rate), 14: 2Hz, 5: 1024Hz, 2: 8192Hz +// (Max: 14, Min: 2) - 14 = 2Hz, 13 = 4Hz, 12 = 8Hz, 11 = 16Hz 10 = 32Hz, 2 = 8192Hz +//#define TIMER_RATE 10 // 32 Hz +//#define TIMER_RATE 12 // 8 Hz +#define TIMER_RATE 14 // 2 Hz - Lowest +#define TIMER_FREQ (0x8000>>TIMER_RATE) //Hz +#define MS_PER_TICK_WHOLE (1000/(TIMER_FREQ)) +#define MS_PER_TICK_FRACT ((0x80000000*(1000%TIMER_FREQ))/TIMER_FREQ) + +// === IMPORTS === +extern volatile Sint64 giTimestamp; +extern volatile Uint64 giTicks; +extern volatile Uint64 giPartMiliseconds; +extern void Timer_CallTimers(void); + +// === GLOBALS === +volatile Uint64 giTime_TSCAtLastTick = 0; +volatile Uint64 giTime_TSCPerTick = 0; + +// === PROTOTYPES === +//Sint64 now(void); + int Time_Setup(void); +void Time_Interrupt(int IRQ, void *Ptr); +Uint64 Time_ReadTSC(void); + +// === CODE === +/** + * \fn Sint64 now() + * \brief Return the current timestamp + */ +Sint64 now(void) +{ + Uint64 tsc = Time_ReadTSC(); + tsc -= giTime_TSCAtLastTick; + tsc *= MS_PER_TICK_WHOLE; + if( giTime_TSCPerTick ) { + tsc /= giTime_TSCPerTick; + } + else + tsc = 0; + return giTimestamp + tsc; +} + +/** + * \fn int Time_Setup(void) + * \brief Sets the system time from the Realtime-Clock + */ +int Time_Setup(void) +{ + Uint8 val; + + Log_Log("Timer", "RTC Timer firing at %iHz (%i divisor), %i.0x%08x", + TIMER_FREQ, TIMER_RATE, MS_PER_TICK_WHOLE, MS_PER_TICK_FRACT); + + outb(0x70, inb(0x70)&0x7F); // Disable NMIs + __asm__ __volatile__ ("cli"); // Disable normal interrupts + + // Set IRQ8 firing rate + outb(0x70, 0x0A); // Set the index to register A + val = inb(0x71); // Get the current value of register A + val &= 0xF0; + val |= TIMER_RATE+1; + outb(0x70, 0x0A); // Reset index to A + outb(0x71, val); // Update the timer rate + + // Enable IRQ8 + outb(0x70, 0x0B); // Set the index to register B + val = inb(0x71); // Read the current value of register B + outb(0x70, 0x0B); // Set the index again (a read will reset the index to register D) + outb(0x71, val | 0x40); // Write the previous value or'd with 0x40. This turns on bit 6 of register D + + __asm__ __volatile__ ("sti"); // Re-enable normal interrupts + outb(0x70, inb(0x70)|0x80); // Re-enable NMIs + + // Install IRQ Handler + IRQ_AddHandler(8, Time_Interrupt, NULL); + + // Make sure the RTC actually fires + outb(0x70, 0x0C); // Select register C + inb(0x71); // Just throw away contents. + + return 0; +} + +/** + * \brief Called on the timekeeping IRQ + */ +void Time_Interrupt(int IRQ, void *Ptr) +{ + Uint64 curTSC = Time_ReadTSC(); + + if( giTime_TSCAtLastTick ) + { + giTime_TSCPerTick = curTSC - giTime_TSCAtLastTick; + } + giTime_TSCAtLastTick = curTSC; + + giTicks ++; + giTimestamp += MS_PER_TICK_WHOLE; + giPartMiliseconds += MS_PER_TICK_FRACT; + if(giPartMiliseconds > 0x80000000) { + giTimestamp ++; + giPartMiliseconds -= 0x80000000; + } + + Timer_CallTimers(); + + // Make sure the RTC Fires again + outb(0x70, 0x0C); // Select register C + inb(0x71); // Just throw away contents. +} + +Uint64 Time_ReadTSC(void) +{ + Uint32 a, d; + __asm__ __volatile__ ("rdtsc" : "=a" (a), "=d" (d)); + return ((Uint64)d << 32) | a; +} diff --git a/KernelLand/Kernel/arch/x86/vm8086.c b/KernelLand/Kernel/arch/x86/vm8086.c new file mode 100644 index 00000000..61348622 --- /dev/null +++ b/KernelLand/Kernel/arch/x86/vm8086.c @@ -0,0 +1,438 @@ +/* + * Acess2 VM8086 Driver + * - By John Hodge (thePowersGang) + */ +#define DEBUG 0 +#include +#include +#include +#include +#include + +// === CONSTANTS === +#define VM8086_MAGIC_CS 0xFFFF +#define VM8086_MAGIC_IP 0x0010 +#define VM8086_STACK_SEG 0x9F00 +#define VM8086_STACK_OFS 0x0AFE +enum eVM8086_Opcodes +{ + VM8086_OP_PUSHF = 0x9C, + VM8086_OP_POPF = 0x9D, + VM8086_OP_INT_I = 0xCD, + VM8086_OP_IRET = 0xCF, + VM8086_OP_IN_AD = 0xEC, + VM8086_OP_IN_ADX = 0xED, + VM8086_OP_OUT_AD = 0xEE, + VM8086_OP_OUT_ADX = 0xEF +}; +#define VM8086_PAGES_PER_INST 4 + +#define VM8086_BLOCKSIZE 128 +#define VM8086_BLOCKCOUNT ((0x9F000-0x10000)/VM8086_BLOCKSIZE) + +// === TYPES === +struct sVM8086_InternalData +{ + struct { + Uint32 Bitmap; // 32 sections = 128 byte blocks + tVAddr VirtBase; + tPAddr PhysAddr; + } AllocatedPages[VM8086_PAGES_PER_INST]; +}; + +// === PROTOTYPES === + int VM8086_Install(char **Arguments); +void VM8086_GPF(tRegs *Regs); +//tVM8086 *VM8086_Init(void); + +// === GLOBALS === +MODULE_DEFINE(0, 0x100, VM8086, VM8086_Install, NULL, NULL); +tMutex glVM8086_Process; +tSemaphore gVM8086_TaskComplete; +tSemaphore gVM8086_TasksToDo; +tPID gVM8086_WorkerPID; +tTID gVM8086_CallingThread; +tVM8086 volatile * volatile gpVM8086_State = (void*)-1; // Set to -1 to avoid race conditions +Uint32 gaVM8086_MemBitmap[VM8086_BLOCKCOUNT/32]; + +// === FUNCTIONS === +int VM8086_Install(char **Arguments) +{ + tPID pid; + + Semaphore_Init(&gVM8086_TasksToDo, 0, 10, "VM8086", "TasksToDo"); + + // Lock to avoid race conditions + Mutex_Acquire( &glVM8086_Process ); + + // Create BIOS Call process + pid = Proc_Clone(CLONE_VM); + Log_Debug("VM8086", "pid = %i", pid); + if(pid == -1) + { + Log_Error("VM8086", "Unable to clone kernel into VM8086 worker"); + return MODULE_ERR_MISC; + } + if(pid == 0) + { + Uint * volatile stacksetup; // Initialising Stack + Uint16 * volatile rmstack; // Real Mode Stack + int i; + + Log_Debug("VM8086", "Initialising worker"); + + // Set Image Name + Threads_SetName("VM8086"); + + // Map ROM Area + for(i=0xA0;i<0x100;i++) { + MM_Map( i * 0x1000, i * 0x1000 ); + } + MM_Map( 0, 0 ); // IVT / BDA + // Map (but allow allocation) of 0x1000 - 0x9F000 + // - So much hack, it isn't funny + for(i=1;i<0x9F;i++) { + MM_Map( i * 0x1000, i * 0x1000 ); + MM_DerefPhys( i * 0x1000 ); // Above + while(MM_GetRefCount(i*0x1000)) + MM_DerefPhys( i * 0x1000 ); // Phys setup + } + MM_Map( 0x9F000, 0x9F000 ); // Stack / EBDA + // System Stack / Stub + if( MM_Allocate( 0x100000 ) == 0 ) { + Log_Error("VM8086", "Unable to allocate memory for stack/stub"); + gVM8086_WorkerPID = 0; + Threads_Exit(0, 1); + } + + *(Uint8*)(0x100000) = VM8086_OP_IRET; + *(Uint8*)(0x100001) = 0x07; // POP ES + *(Uint8*)(0x100002) = 0x1F; // POP DS + *(Uint8*)(0x100003) = 0xCB; // RET FAR + + rmstack = (Uint16*)(VM8086_STACK_SEG*16 + VM8086_STACK_OFS); + rmstack--; *rmstack = 0xFFFF; //CS + rmstack--; *rmstack = 0x0010; //IP + + // Setup Stack + stacksetup = (Uint*)0x101000; + stacksetup--; *stacksetup = VM8086_STACK_SEG; // GS + stacksetup--; *stacksetup = VM8086_STACK_SEG; // FS + stacksetup--; *stacksetup = VM8086_STACK_SEG; // DS + stacksetup--; *stacksetup = VM8086_STACK_SEG; // ES + stacksetup--; *stacksetup = VM8086_STACK_SEG; // SS + stacksetup--; *stacksetup = VM8086_STACK_OFS-2; // SP + stacksetup--; *stacksetup = 0x20202; // FLAGS + stacksetup--; *stacksetup = 0xFFFF; // CS + stacksetup--; *stacksetup = 0x10; // IP + stacksetup--; *stacksetup = 0xAAAA; // AX + stacksetup--; *stacksetup = 0xCCCC; // CX + stacksetup--; *stacksetup = 0xDDDD; // DX + stacksetup--; *stacksetup = 0xBBBB; // BX + stacksetup--; *stacksetup = 0x5454; // SP + stacksetup--; *stacksetup = 0xB4B4; // BP + stacksetup--; *stacksetup = 0x5151; // SI + stacksetup--; *stacksetup = 0xD1D1; // DI + stacksetup--; *stacksetup = 0x20|3; // DS - Kernel + stacksetup--; *stacksetup = 0x20|3; // ES - Kernel + stacksetup--; *stacksetup = 0x20|3; // FS + stacksetup--; *stacksetup = 0x20|3; // GS + __asm__ __volatile__ ( + "mov %%eax,%%esp;\n\t" // Set stack pointer + "pop %%gs;\n\t" + "pop %%fs;\n\t" + "pop %%es;\n\t" + "pop %%ds;\n\t" + "popa;\n\t" + "iret;\n\t" : : "a" (stacksetup)); + for(;;); // Shouldn't be reached + } + + gVM8086_WorkerPID = pid; + + // It's released when the GPF fires + Mutex_Acquire( &glVM8086_Process ); + Mutex_Release( &glVM8086_Process ); + + // Worker killed itself + if( gVM8086_WorkerPID != pid ) { + return MODULE_ERR_MISC; + } + + return MODULE_ERR_OK; +} + +void VM8086_GPF(tRegs *Regs) +{ + Uint8 opcode; + +// Log_Log("VM8086", "GPF - %04x:%04x", Regs->cs, Regs->eip); + + if(Regs->eip == VM8086_MAGIC_IP && Regs->cs == VM8086_MAGIC_CS + && Threads_GetPID() == gVM8086_WorkerPID) + { + if( gpVM8086_State == (void*)-1 ) { + Log_Log("VM8086", "Worker thread ready and waiting"); + gpVM8086_State = NULL; + Mutex_Release( &glVM8086_Process ); // Release lock obtained in VM8086_Install + } +// Log_Log("VM8086", "gpVM8086_State = %p, gVM8086_CallingThread = %i", +// gpVM8086_State, gVM8086_CallingThread); + if( gpVM8086_State ) { + gpVM8086_State->AX = Regs->eax; gpVM8086_State->CX = Regs->ecx; + gpVM8086_State->DX = Regs->edx; gpVM8086_State->BX = Regs->ebx; + gpVM8086_State->BP = Regs->ebp; + gpVM8086_State->SI = Regs->esi; gpVM8086_State->DI = Regs->edi; + gpVM8086_State->DS = Regs->ds; gpVM8086_State->ES = Regs->es; + gpVM8086_State = NULL; + // Wake the caller + Semaphore_Signal(&gVM8086_TaskComplete, 1); + } + + //Log_Log("VM8086", "Waiting for something to do"); + __asm__ __volatile__ ("sti"); + Semaphore_Wait(&gVM8086_TasksToDo, 1); + + //Log_Log("VM8086", "We have a task (%p)", gpVM8086_State); + Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = VM8086_MAGIC_CS; + Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = VM8086_MAGIC_IP; + Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->CS; + Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->IP; + Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->DS; + Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->ES; + + // Set Registers + Regs->eip = 0x11; Regs->cs = 0xFFFF; + Regs->eax = gpVM8086_State->AX; Regs->ecx = gpVM8086_State->CX; + Regs->edx = gpVM8086_State->DX; Regs->ebx = gpVM8086_State->BX; + Regs->esi = gpVM8086_State->SI; Regs->edi = gpVM8086_State->DI; + Regs->ebp = gpVM8086_State->BP; + Regs->ds = 0x23; Regs->es = 0x23; + Regs->fs = 0x23; Regs->gs = 0x23; + return ; + } + + opcode = *(Uint8*)( (Regs->cs*16) + (Regs->eip) ); + Regs->eip ++; + switch(opcode) + { + case VM8086_OP_PUSHF: //PUSHF + Regs->esp -= 2; + *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->eflags & 0xFFFF; + #if TRACE_EMU + Log_Debug("VM8086", "Emulated PUSHF"); + #endif + break; + case VM8086_OP_POPF: //POPF + // Changing IF is not allowed + Regs->eflags &= 0xFFFF0202; + Regs->eflags |= *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ); + Regs->esp += 2; + #if TRACE_EMU + Log_Debug("VM8086", "Emulated POPF"); + #endif + break; + + case VM8086_OP_INT_I: //INT imm8 + { + int id; + id = *(Uint8*)( Regs->cs*16 +(Regs->eip&0xFFFF)); + Regs->eip ++; + + Regs->esp -= 2; *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->cs; + Regs->esp -= 2; *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->eip; + + Regs->cs = *(Uint16*)(4*id + 2); + Regs->eip = *(Uint16*)(4*id); + #if TRACE_EMU + Log_Debug("VM8086", "Emulated INT 0x%x", id); + #endif + } + break; + + case VM8086_OP_IRET: //IRET + Regs->eip = *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ); Regs->esp += 2; + Regs->cs = *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ); Regs->esp += 2; + #if TRACE_EMU + Log_Debug("VM8086", "IRET to %04x:%04x", Regs->cs, Regs->eip); + #endif + break; + + + case VM8086_OP_IN_AD: //IN AL, DX + Regs->eax &= 0xFFFFFF00; + Regs->eax |= inb(Regs->edx&0xFFFF); + #if TRACE_EMU + Log_Debug("VM8086", "Emulated IN AL, DX (Port 0x%x)\n", Regs->edx&0xFFFF); + #endif + break; + case VM8086_OP_IN_ADX: //IN AX, DX + Regs->eax &= 0xFFFF0000; + Regs->eax |= inw(Regs->edx&0xFFFF); + #if TRACE_EMU + Log_Debug("VM8086", "Emulated IN AX, DX (Port 0x%x)\n", Regs->edx&0xFFFF); + #endif + break; + + case VM8086_OP_OUT_AD: //OUT DX, AL + outb(Regs->edx&0xFFFF, Regs->eax&0xFF); + #if TRACE_EMU + Log_Debug("VM8086", "Emulated OUT DX, AL (*0x%04x = 0x%02x)\n", Regs->edx&0xFFFF, Regs->eax&0xFF); + #endif + break; + case VM8086_OP_OUT_ADX: //OUT DX, AX + outw(Regs->edx&0xFFFF, Regs->eax&0xFFFF); + #if TRACE_EMU + Log_Debug("VM8086", "Emulated OUT DX, AX (*0x%04x = 0x%04x)\n", Regs->edx&0xFFFF, Regs->eax&0xFFFF); + #endif + break; + + // TODO: Decide on allowing VM8086 Apps to enable/disable interrupts + case 0xFA: //CLI + break; + case 0xFB: //STI + break; + + case 0x66: + opcode = *(Uint8*)( (Regs->cs*16) + (Regs->eip&0xFFFF)); + switch( opcode ) + { + case VM8086_OP_IN_ADX: //IN AX, DX + Regs->eax = ind(Regs->edx&0xFFFF); + #if TRACE_EMU + Log_Debug("VM8086", "Emulated IN EAX, DX (Port 0x%x)\n", Regs->edx&0xFFFF); + #endif + break; + case VM8086_OP_OUT_ADX: //OUT DX, AX + outd(Regs->edx&0xFFFF, Regs->eax); + #if TRACE_EMU + Log_Debug("VM8086", "Emulated OUT DX, EAX (*0x%04x = 0x%08x)\n", Regs->edx&0xFFFF, Regs->eax); + #endif + break; + default: + Log_Error("VM8086", "Error - Unknown opcode 66 %02x caused a GPF at %04x:%04x", + Regs->cs, Regs->eip, + opcode + ); + // Force an end to the call + Regs->cs = VM8086_MAGIC_CS; + Regs->eip = VM8086_MAGIC_IP; + break; + } + break; + + default: + Log_Error("VM8086", "Error - Unknown opcode %02x caused a GPF at %04x:%04x", + opcode, Regs->cs, Regs->eip); + // Force an end to the call + Regs->cs = VM8086_MAGIC_CS; + Regs->eip = VM8086_MAGIC_IP; + break; + } +} + +/** + * \brief Create an instance of the VM8086 Emulator + */ +tVM8086 *VM8086_Init(void) +{ + tVM8086 *ret; + ret = calloc( 1, sizeof(tVM8086) + sizeof(struct sVM8086_InternalData) ); + ret->Internal = (void*)((tVAddr)ret + sizeof(tVM8086)); + return ret; +} + +void VM8086_Free(tVM8086 *State) +{ + int i; + for( i = VM8086_PAGES_PER_INST; i --; ) + MM_UnmapHWPages( State->Internal->AllocatedPages[i].VirtBase, 1); + free(State); +} + +void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset) +{ + int i, j, base = 0; + int nBlocks, rem; + + Size = (Size + 127) & ~127; + nBlocks = Size / 128; + + if(Size > 4096) return NULL; + + for( i = 0; i < VM8086_PAGES_PER_INST; i++ ) + { + if( State->Internal->AllocatedPages[i].VirtBase == 0 ) continue; + + + //Log_Debug("VM8086", "AllocatedPages[%i].Bitmap = 0b%b", i, State->Internal->AllocatedPages[i].Bitmap); + + rem = nBlocks; + base = 0; + // Scan the bitmap for a free block + for( j = 0; j < 32; j++ ) { + if( State->Internal->AllocatedPages[i].Bitmap & (1 << j) ) { + base = j+1; + rem = nBlocks; + } + + rem --; + if(rem == 0) // Goodie, there's a gap + { + for( j = 0; j < nBlocks; j++ ) + State->Internal->AllocatedPages[i].Bitmap |= 1 << (base + j); + *Segment = State->Internal->AllocatedPages[i].PhysAddr / 16 + base * 8; + *Offset = 0; + LOG("Allocated at #%i,%04x", i, base*128); + LOG(" - %x:%x", *Segment, *Offset); + return (void*)( State->Internal->AllocatedPages[i].VirtBase + base * 128 ); + } + } + } + + // No pages with free space?, allocate a new one + for( i = 0; i < VM8086_PAGES_PER_INST; i++ ) + { + if( State->Internal->AllocatedPages[i].VirtBase == 0 ) break; + } + // Darn, we can't allocate any more + if( i == VM8086_PAGES_PER_INST ) { + Log_Warning("VM8086", "Out of pages in %p", State); + return NULL; + } + + State->Internal->AllocatedPages[i].VirtBase = MM_AllocDMA( + 1, 20, &State->Internal->AllocatedPages[i].PhysAddr); + State->Internal->AllocatedPages[i].Bitmap = 0; + + for( j = 0; j < nBlocks; j++ ) + State->Internal->AllocatedPages[i].Bitmap |= 1 << j; + LOG("AllocatedPages[%i].Bitmap = 0b%b", i, State->Internal->AllocatedPages[i].Bitmap); + *Segment = State->Internal->AllocatedPages[i].PhysAddr / 16; + *Offset = 0; + LOG(" - %x:%x", *Segment, *Offset); + return (void*) State->Internal->AllocatedPages[i].VirtBase; +} + +void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset) +{ + return (void*)( KERNEL_BASE + Segment*16 + Offset ); +} + +void VM8086_Int(tVM8086 *State, Uint8 Interrupt) +{ + State->IP = *(Uint16*)(KERNEL_BASE+4*Interrupt); + State->CS = *(Uint16*)(KERNEL_BASE+4*Interrupt+2); + +// Log_Debug("VM8086", "Software interrupt %i to %04x:%04x", Interrupt, State->CS, State->IP); + + Mutex_Acquire( &glVM8086_Process ); + + gpVM8086_State = State; + gVM8086_CallingThread = Threads_GetTID(); + Semaphore_Signal(&gVM8086_TasksToDo, 1); + + Semaphore_Wait(&gVM8086_TaskComplete, 1); + + Mutex_Release( &glVM8086_Process ); +} diff --git a/KernelLand/Kernel/arch/x86_64/Makefile b/KernelLand/Kernel/arch/x86_64/Makefile new file mode 100644 index 00000000..a72216a3 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/Makefile @@ -0,0 +1,32 @@ +# +# Acess2 Kernel +# i386 Architecture Makefile +# arch/i386/Makefile + +MAX_CPUS := 4 + +AS_SUFFIX = asm + +CPPFLAGS := -DMAX_CPUS=$(MAX_CPUS) -D USE_MP=0 +CFLAGS := $(KERNEL_CFLAGS) -mno-sse -mno-mmx +ASFLAGS := -f elf64 -D MAX_CPUS=$(MAX_CPUS) -D USE_MP=0 +LDFLAGS := -nostdlib -nodefaultlibs + +ifeq ($(ARCH),amd64) + ASFLAGS += -D AMD64=1 + CPPFLAGS += -DAMD64=1 +else + ifeq ($(ARCH),x86_64) + ASFLAGS += -D AMD64=0 -D X86_64=1 + CPPFLAGS += -DAMD64=0 -DX86_64=1 + endif +endif + + +A_OBJ := start32.ao start64.ao desctab.ao proc.ao +A_OBJ += main.o lib.o proc.o mm_virt.o mm_phys.o +A_OBJ += kernelpanic.o errors.o time.o pci.o +A_OBJ += vm8086.o +# rme.o + +POSTBUILD = objcopy $(BIN) -F elf32-i386 $(BIN) diff --git a/KernelLand/Kernel/arch/x86_64/desctab.asm b/KernelLand/Kernel/arch/x86_64/desctab.asm new file mode 100644 index 00000000..6e8aa639 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/desctab.asm @@ -0,0 +1,438 @@ +; +; +; +%include "arch/x86_64/include/common.inc.asm" +[BITS 64] + +[extern Log] +[extern gGDTPtr] +[extern gGDT] + +%define NUM_IRQ_CALLBACKS 4 + +MM_LOCALAPIC equ 0xFFFFFD0000000000 + +[section .text] +[global Desctab_Init] +Desctab_Init: + ; Save to make following instructions smaller + mov rdi, gIDT + + ; Set an IDT entry to a callback + %macro SETIDT 2 + mov rax, %2 + mov WORD [rdi + %1*16], ax + shr rax, 16 + mov WORD [rdi + %1*16 + 6], ax + shr rax, 16 + mov DWORD [rdi + %1*16 + 8], eax + ; Enable + mov ax, WORD [rdi + %1*16 + 4] + or ax, 0x8000 + mov WORD [rdi + %1*16 + 4], ax + %endmacro + + ; Install error handlers + %macro SETISR 1 + SETIDT %1, Isr%1 + %endmacro + + %assign i 0 + %rep 32 + SETISR i + %assign i i+1 + %endrep + + ; Install IRQs + SETIDT 0xF0, PIT_IRQ + SETIDT 0xF1, Irq1 + SETIDT 0xF2, Irq2 + SETIDT 0xF3, Irq3 + SETIDT 0xF4, Irq4 + SETIDT 0xF5, Irq5 + SETIDT 0xF6, Irq6 + SETIDT 0xF7, Irq7 + SETIDT 0xF8, Irq8 + SETIDT 0xF9, Irq9 + SETIDT 0xFA, Irq10 + SETIDT 0xFB, Irq11 + SETIDT 0xFC, Irq12 + SETIDT 0xFD, Irq13 + SETIDT 0xFE, Irq14 + SETIDT 0xFF, Irq15 + + ; Remap PIC + push rdx ; Save RDX + mov dx, 0x20 + mov al, 0x11 + out dx, al ; Init Command + mov dx, 0x21 + mov al, 0xF0 + out dx, al ; Offset (Start of IDT Range) + mov al, 0x04 + out dx, al ; IRQ connected to Slave (00000100b) = IRQ2 + mov al, 0x01 + out dx, al ; Set Mode + mov al, 0x00 + out dx, al ; Set Mode + + mov dx, 0xA0 + mov al, 0x11 + out dx, al ; Init Command + mov dx, 0xA1 + mov al, 0xF8 + out dx, al ; Offset (Start of IDT Range) + mov al, 0x02 + out dx, al ; IRQ Line connected to master + mov al, 0x01 + out dx, al ; Set Mode + mov dl, 0x00 + out dx, al ; Set Mode + pop rdx + + + ; Install IDT + mov rax, gIDTPtr + lidt [rax] + + ; Re-install GDT (in higher address space) + mov rax, gGDTPtr + mov rcx, gGDT + mov QWORD [rax+2], rcx + lgdt [rax] + + ; Start interrupts + sti + + ; Set IA32_LSTAR (RIP of handler) + mov ecx, 0xC0000082 ; IA32_LSTAR + mov eax, SyscallStub - 0xFFFFFFFF00000000 + mov edx, 0xFFFFFFFF + wrmsr + ; Set IA32_FMASK (flags mask) + mov ecx, 0xC0000084 + rdmsr + mov eax, ~0x202 + wrmsr + ; Set IA32_STAR (Kernel/User CS) + mov ecx, 0xC0000081 + rdmsr + mov edx, 0x8 | (0x1B << 16) ; Kernel CS (and Kernel DS/SS - 8), User CS + wrmsr + + ret + +; int IRQ_AddHandler(int IRQ, void (*Handler)(int IRQ), void *Ptr) +; Return Values: +; 0 on Success +; -1 on an invalid IRQ Number +; -2 when no slots are avaliable +[global IRQ_AddHandler] +IRQ_AddHandler: + ; RDI - IRQ Number + ; RSI - Callback + ; RDX - Ptr + + ; Check for RDI >= 16 + cmp rdi, 16 + jb .numOK + xor rax, rax + dec rax + jmp .ret +.numOK: + + ; Get handler base into RAX + lea rax, [rdi*4] + mov rcx, gaIRQ_Handlers + lea rax, [rcx+rax*8] + + ; Find a free callback slot + %rep NUM_IRQ_CALLBACKS + mov rcx, [rax] + test rcx, rcx + jz .assign + add rax, 8 + %endrep + ; None found, return -2 + xor rax, rax + dec rax + dec rax + jmp .ret + + ; Assign the IRQ Callback +.assign: + ; A little bit of debug + push rdi + push rsi + push rax + push rdx + sub rsp, 8 + mov rcx, rdi ; IRQ Number + mov rdx, rsi ; Callback + mov rsi, rax ; Pointer + mov rdi, csIRQ_Assigned + call Log + add rsp, 8 + pop rdx + pop rax + pop rsi + pop rdi + + ; Assign and return + mov [rax], rsi + add rax, gaIRQ_DataPtrs - gaIRQ_Handlers + mov [rax], rdx + xor rax, rax + +.ret: + ret + +[section .rodata] +csIRQ_Assigned: + db "IRQ %p := %p (IRQ %i)",0 +csIRQ_Fired: + db "IRQ %i fired",0 +[section .text] + +%macro ISR_NOERRNO 1 +Isr%1: + push QWORD 0 + push QWORD %1 + jmp ErrorCommon +%endmacro +%macro ISR_ERRNO 1 +Isr%1: + push QWORD %1 + jmp ErrorCommon +%endmacro + +ISR_NOERRNO 0; 0: Divide By Zero Exception +ISR_NOERRNO 1; 1: Debug Exception +ISR_NOERRNO 2; 2: Non Maskable Interrupt Exception +ISR_NOERRNO 3; 3: Int 3 Exception +ISR_NOERRNO 4; 4: INTO Exception +ISR_NOERRNO 5; 5: Out of Bounds Exception +ISR_NOERRNO 6; 6: Invalid Opcode Exception +ISR_NOERRNO 7; 7: Coprocessor Not Available Exception +ISR_ERRNO 8; 8: Double Fault Exception (With Error Code!) +ISR_NOERRNO 9; 9: Coprocessor Segment Overrun Exception +ISR_ERRNO 10; 10: Bad TSS Exception (With Error Code!) +ISR_ERRNO 11; 11: Segment Not Present Exception (With Error Code!) +ISR_ERRNO 12; 12: Stack Fault Exception (With Error Code!) +ISR_ERRNO 13; 13: General Protection Fault Exception (With Error Code!) +ISR_ERRNO 14; 14: Page Fault Exception (With Error Code!) +ISR_NOERRNO 15; 15: Reserved Exception +ISR_NOERRNO 16; 16: Floating Point Exception +ISR_NOERRNO 17; 17: Alignment Check Exception +ISR_NOERRNO 18; 18: Machine Check Exception +ISR_NOERRNO 19; 19: Reserved +ISR_NOERRNO 20; 20: Reserved +ISR_NOERRNO 21; 21: Reserved +ISR_NOERRNO 22; 22: Reserved +ISR_NOERRNO 23; 23: Reserved +ISR_NOERRNO 24; 24: Reserved +ISR_NOERRNO 25; 25: Reserved +ISR_NOERRNO 26; 26: Reserved +ISR_NOERRNO 27; 27: Reserved +ISR_NOERRNO 28; 28: Reserved +ISR_NOERRNO 29; 29: Reserved +ISR_NOERRNO 30; 30: Reserved +ISR_NOERRNO 31; 31: Reserved + +[extern Error_Handler] +[global ErrorCommon] +ErrorCommon: + PUSH_GPR + push gs + push fs + ;PUSH_FPU + ;PUSH_XMM + + mov rdi, rsp +; xchg bx, bx + call Error_Handler + + ;POP_XMM + ;POP_FPU + pop fs + pop gs + POP_GPR + add rsp, 2*8 + iretq + +%macro DEFIRQ 1 +Irq%1: + push 0 + push %1 + jmp IrqCommon +%endmacro + +%assign i 0 +%rep 16 +DEFIRQ i +%assign i i+1 +%endrep + +[global IrqCommon] +IrqCommon: + PUSH_GPR + push gs + push fs + +; mov rdi, csIRQ_Fired +; mov rsi, [rsp+(16+2)*8] +; call Log + + mov ebx, [rsp+(16+2)*8] ; Get interrupt number (16 GPRS + 2 SRs) + shl ebx, 2 ; *4 + mov rax, gaIRQ_Handlers + lea rbx, [rax+rbx*8] + + ; Check all callbacks + sub rsp, 8 ; Shadow of argument + %assign i 0 + %rep NUM_IRQ_CALLBACKS + ; Get callback address + mov rax, [rbx] + test rax, rax ; Check if it exists + jz .skip.%[i] + ; Set RDI to IRQ number + mov rdi, [rsp+(16+2+1)*8] ; Get IRQ number + mov rsi, [rbx-gaIRQ_Handlers+gaIRQ_DataPtrs] + call rax ; Call +.skip.%[i]: + add rbx, 8 ; Next! + %assign i i+1 + %endrep + add rsp, 8 + + ; ACK + mov al, 0x20 + mov rdi, [rsp+(16+2)*8] ; Get IRQ number + cmp rdi, 8 + jb .skipAckSecondary + out 0xA0, al +.skipAckSecondary: + out 0x20, al + + pop fs + pop gs + POP_GPR + add rsp, 8*2 + iretq + +[extern Time_UpdateTimestamp] + +%if USE_MP +[global APIC_Timer_IRQ] +APIC_Timer_IRQ: + PUSH_GPR + push gs + push fs + + ; TODO: What to do? + + mov eax, DWORD [gpMP_LocalAPIC] + mov DWORD [eax+0x0B0], 0 + + pop fs + pop gs + POP_GPR + iretq +%endif + +[global PIT_IRQ] +PIT_IRQ: + PUSH_GPR + ;PUSH_FPU + ;PUSH_XMM + + call Time_UpdateTimestamp + + %if 0 +[section .rodata] +csUserSS: db "User SS: 0x%x",0 +[section .text] + mov rdi, csUserSS + mov rsi, [rsp+0x80+0x20] + call Log + %endif + + ; Send EOI + mov al, 0x20 + out 0x20, al ; ACK IRQ + + ;POP_XMM + ;POP_FPU + POP_GPR + iretq + +[extern ci_offsetof_tThread_KernelStack] +[extern SyscallHandler] +[global SyscallStub] +SyscallStub: + mov rbp, dr0 + mov ebx, [rel ci_offsetof_tThread_KernelStack] + mov rbp, [rbp+rbx] ; Get kernel stack + xchg rbp, rsp ; Swap stacks + + push rbp ; Save User RSP + push rcx ; RIP + push r11 ; RFLAGS + + ; RDI + ; RSI + ; RDX + ; R10 (RCX for non syscall) + ; R8 + ; R9 + sub rsp, (6+2)*8 + mov [rsp+0x00], rax ; Number +; mov [rsp+0x08], rax ; Errno (output only) + mov [rsp+0x10], rdi ; Arg1 + mov [rsp+0x18], rsi ; Arg2 + mov [rsp+0x20], rdx ; Arg3 + mov [rsp+0x28], r10 ; Arg4 + mov [rsp+0x30], r8 ; Arg5 + mov [rsp+0x38], r9 ; Arg6 + + mov rdi, rsp + sub rsp, 8 + call SyscallHandler + + %if 0 +[section .rodata] +csSyscallReturn: db "Syscall Return: 0x%x",0 +[section .text] + mov rdi, csSyscallReturn + mov rsi, [rsp+0+8] + call Log + %endif + + add rsp, 8 + mov ebx, [rsp+8] ; Get errno + mov rax, [rsp+0] ; Get return + add rsp, (6+2)*8 + + pop r11 + pop rcx + pop rsp ; Change back to user stack + ; TODO: Determine if user is 64 or 32 bit + + db 0x48 ; REX, nasm doesn't have a sysretq opcode + sysret + +[section .data] +gIDT: + ; 64-bit Interrupt Gate, CS = 0x8, IST0 (Disabled) + times 256 dd 0x00080000, 0x00000E00, 0, 0 +gIDTPtr: + dw 256*16-1 + dq gIDT + +gaIRQ_Handlers: + times 16*NUM_IRQ_CALLBACKS dq 0 +gaIRQ_DataPtrs: + times 16*NUM_IRQ_CALLBACKS dq 0 + +; vim: ft=nasm diff --git a/KernelLand/Kernel/arch/x86_64/errors.c b/KernelLand/Kernel/arch/x86_64/errors.c new file mode 100644 index 00000000..f7da7f9f --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/errors.c @@ -0,0 +1,155 @@ +/* + * Acess2 x86_64 Project + * - Error Handling + */ +#include +#include +#include +#include // Needed for SSE handling + +#define MAX_BACKTRACE 6 + +// === IMPORTS === +extern int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs); +extern void Error_Backtrace(Uint IP, Uint BP); +extern void Proc_EnableSSE(void); +extern void Proc_RestoreSSE(Uint32 Data); + +// === PROTOTYPES === +void Error_Handler(tRegs *Regs); + +// === GLOBALS == +const char * const csaERROR_NAMES[] = { + "Divide By Zero", "Debug", "NMI Exception", "INT3", + "INTO", "Out of Bounds", "Invalid Opcode", "Coprocessor not avaliable", + "Double Fault", "Coprocessor Segment Overrun", "Bad TSS", "Segment Not Present", + "Stack Fault Exception", "GPF", "#PF", "Reserved", + "Floating Point Exception", "Alignment Check Exception", "Machine Check Exception", "Reserved", + "Reserved", "Reserved", "Reserved", "Reserved", + "Reserved", "Reserved", "Reserved", "Reserved", + "Reserved", "Reserved", "Reserved", "Reserved" + }; + +// === CODE === +void Error_Handler(tRegs *Regs) +{ + Uint cr; + + if( Regs->IntNum == 7 ) + { + tThread *thread = Proc_GetCurThread(); + if(!thread->SavedState.bSSEModified) + { + Proc_EnableSSE(); + if(!thread->SavedState.SSE) + thread->SavedState.SSE = malloc(sizeof(tSSEState) + 0xF); + else + Proc_RestoreSSE( ((Uint)thread->SavedState.SSE + 0xF) & ~0xF ); + thread->SavedState.bSSEModified = 1; +// __asm__ __volatile__ ("sti"); + return ; + } + // oops, SSE enabled but a #NM, bad news + } + + if( Regs->IntNum == 14 ) { + __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr)); + if( MM_PageFault(cr, Regs->ErrorCode, Regs) == 0 ) + return ; + } + else { + Debug_KernelPanic(); + + Error_Backtrace(Regs->RIP, Regs->RBP); + } + + Log("CPU Error %x, Code: 0x%x", Regs->IntNum, Regs->ErrorCode); + Log(" - %s", csaERROR_NAMES[Regs->IntNum]); + Log(" CS:RIP = 0x%04x:%016llx", Regs->CS, Regs->RIP); + Log(" SS:RSP = 0x%04x:%016llx", Regs->SS, Regs->RSP); + Log(" RFLAGS = 0x%016llx", Regs->RFlags); + + Log(" RAX %016llx RCX %016llx RDX %016llx RBX %016llx", + Regs->RAX, Regs->RCX, Regs->RDX, Regs->RBX); + Log(" RSP %016llx RBP %016llx RSI %016llx RDI %016llx", + Regs->RSP, Regs->RBP, Regs->RSP, Regs->RDI); + Log(" R8 %016llx R9 %016llx R10 %016llx R11 %016llx", + Regs->R8, Regs->R9, Regs->R10, Regs->R11); + Log(" R12 %016llx R13 %016llx R14 %016llx R15 %016llx", + Regs->R12, Regs->R13, Regs->R14, Regs->R15); + Log(" FS %04x GS %04x", Regs->FS, Regs->GS); + + + // Control Registers + __asm__ __volatile__ ("mov %%cr0, %0":"=r"(cr)); + Warning(" CR0 0x%08x", cr); + __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr)); + Warning(" CR2 0x%016llx", cr); + __asm__ __volatile__ ("mov %%cr3, %0":"=r"(cr)); + Warning(" CR3 0x%016llx", cr); + __asm__ __volatile__ ("mov %%cr4, %0":"=r"(cr)); + Warning(" CR4 0x%08x", cr); + + switch( Regs->IntNum ) + { + case 6: // #UD + Warning(" Offending bytes: %02x %02x %02x %02x", + *(Uint8*)(Regs->RIP+0), *(Uint8*)(Regs->RIP+1), + *(Uint8*)(Regs->RIP+2), *(Uint8*)(Regs->RIP+3) + ); + break; + } + + __asm__ __volatile__ ("cli"); + for(;;) + __asm__ __volatile__ ("hlt"); +} + +/** + * \fn void Error_Backtrace(Uint eip, Uint ebp) + * \brief Unrolls the stack to trace execution + * \param eip Current Instruction Pointer + * \param ebp Current Base Pointer (Stack Frame) + */ +void Error_Backtrace(Uint IP, Uint BP) +{ + int i = 0; + + //if(eip < 0xC0000000 && eip > 0x1000) + //{ + // LogF("Backtrace: User - 0x%x\n", eip); + // return; + //} + + if( IP > USER_MAX && IP < MM_KERNEL_CODE + && (MM_MODULE_MIN > IP || IP > MM_MODULE_MAX) + ) + { + LogF("Backtrace: Data Area - %p\n", IP); + return; + } + + //str = Debug_GetSymbol(eip, &delta); + //if(str == NULL) + LogF("Backtrace: %p", IP); + //else + // LogF("Backtrace: %s+0x%x", str, delta); + if( !MM_GetPhysAddr(BP) ) + { + LogF("\nBacktrace: Invalid BP, stopping\n"); + return; + } + + + while( MM_GetPhysAddr(BP) && MM_GetPhysAddr(BP+8+7) && i < MAX_BACKTRACE ) + { + //str = Debug_GetSymbol(*(Uint*)(ebp+4), &delta); + //if(str == NULL) + LogF(" >> 0x%llx", ((Uint*)BP)[1]); + //else + // LogF(" >> %s+0x%x", str, delta); + BP = ((Uint*)BP)[0]; + i++; + } + LogF("\n"); +} diff --git a/KernelLand/Kernel/arch/x86_64/include/arch.h b/KernelLand/Kernel/arch/x86_64/include/arch.h new file mode 100644 index 00000000..bde7abda --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/include/arch.h @@ -0,0 +1,109 @@ +/* + * Acess2 x86-64 Architecure Module + * - By John Hodge (thePowersGang) + */ +#ifndef _ARCH_H_ +#define _ARCH_H_ + +//#include +#define USER_MAX 0x00007FFF##FFFFF000 +#define KERNEL_BASE 0xFFFFFFFF##80000000 +#define BITS 64 +#define PAGE_SIZE 0x1000 + +#define STACKED_LOCKS 2 // 0: No, 1: Per-CPU, 2: Per-Thread +#define LOCK_DISABLE_INTS 0 + +#define INVLPTR ((void*)0x0FFFFFFFFFFFFFFFULL) + +//#define INT_MAX 0x7FFFFFFF +//#define UINT_MAX 0xFFFFFFFF + +// === Core Types === +typedef signed char Sint8; +typedef unsigned char Uint8; +typedef signed short Sint16; +typedef unsigned short Uint16; +typedef signed int Sint32; +typedef unsigned int Uint32; +#if __WORDSIZE == 64 +typedef signed long int Sint64; +typedef unsigned long int Uint64; +#else +typedef signed long long int Sint64; +typedef unsigned long long int Uint64; +#endif + +typedef Sint64 Sint; +typedef Uint64 Uint; +typedef Uint64 tPAddr; +typedef Uint64 tVAddr; + +typedef Uint64 size_t; +typedef char BOOL; + +#define __ASM__ __asm__ __volatile__ + +// === MACROS === +/** + * \brief Halt the CPU + */ +#define HALT() __asm__ __volatile__ ("sti;\n\thlt") +/** + * \brief Fire a magic breakpoint (bochs) + */ +#define MAGIC_BREAK() __asm__ __volatile__ ("xchg %bx, %bx") + +// Systemcall Registers +// TODO: Fix this structure +typedef struct sSyscallRegs +{ + union { + Uint Num; + Uint Return; + }; // RAX + Uint Error; // RBX + Uint Arg1; // RDI + Uint Arg2; // RSI + Uint Arg3; // RDX + Uint Arg4; // RCX + Uint Arg5; // R8 + Uint Arg6; // R9 + Uint _Flags; + Uint _IP; + Uint StackPointer; // RSP + +} tSyscallRegs; + +/** + * \brief Short Spinlock structure + */ +struct sShortSpinlock { + #if STACKED_LOCKS == 2 + volatile void *Lock; //!< Lock value + #else + volatile int Lock; //!< Lock value + #endif + + #if LOCK_DISABLE_INTS + int IF; //!< Interrupt state on call to SHORTLOCK + #endif + #if STACKED_LOCKS + int Depth; + #endif +}; + +// === FUNCTIONS === +extern int IS_LOCKED(struct sShortSpinlock *Lock); +extern int CPU_HAS_LOCK(struct sShortSpinlock *Lock); +extern void SHORTLOCK(struct sShortSpinlock *Lock); +extern void SHORTREL(struct sShortSpinlock *Lock); + +extern void Debug_PutCharDebug(char ch); +extern void Debug_PutStringDebug(const char *Str); + +// TODO: Move this to acess.h +extern tPAddr MM_AllocateZero(tVAddr VAddr); + +#endif + diff --git a/KernelLand/Kernel/arch/x86_64/include/arch_config.h b/KernelLand/Kernel/arch/x86_64/include/arch_config.h new file mode 100644 index 00000000..c9f47b0d --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/include/arch_config.h @@ -0,0 +1,13 @@ +/* + */ +#ifndef _ARCH_CONFIG_H_ +#define _ARCH_CONFIG_H_ + + +#define PIT_TIMER_BASE_N 3579545 +#define PIT_TIMER_BASE_D 3 +// PIT Ticks at 1.1931816666666 MHz +// Base is 1193182 HZ +#define PIT_TIMER_DIVISOR 11931 //~100Hz + +#endif diff --git a/KernelLand/Kernel/arch/x86_64/include/common.inc.asm b/KernelLand/Kernel/arch/x86_64/include/common.inc.asm new file mode 100644 index 00000000..d2301777 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/include/common.inc.asm @@ -0,0 +1,50 @@ + +%define INITIAL_KSTACK_SIZE 8 + +%macro SAVE_GPR 1 + mov [%1-0x08], r15 + mov [%1-0x10], r14 + mov [%1-0x18], r13 + mov [%1-0x20], r12 + mov [%1-0x28], r11 + mov [%1-0x30], r10 + mov [%1-0x38], r9 + mov [%1-0x40], r8 + mov [%1-0x48], rdi + mov [%1-0x50], rsi + mov [%1-0x58], rbp + mov [%1-0x60], rsp + mov [%1-0x68], rbx + mov [%1-0x70], rdx + mov [%1-0x78], rcx + mov [%1-0x80], rax +%endmacro + +%macro PUSH_GPR 0 + SAVE_GPR rsp + sub rsp, 0x80 +%endmacro + +%macro RESTORE_GPR 1 + mov r15, [%1-0x08] + mov r14, [%1-0x10] + mov r13, [%1-0x18] + mov r12, [%1-0x20] + mov r11, [%1-0x28] + mov r10, [%1-0x30] + mov r9, [%1-0x38] + mov r8, [%1-0x40] + mov rdi, [%1-0x48] + mov rsi, [%1-0x50] + mov rbp, [%1-0x58] + ;mov rsp, [%1-0x60] + mov rbx, [%1-0x68] + mov rdx, [%1-0x70] + mov rcx, [%1-0x78] + mov rax, [%1-0x80] +%endmacro + +%macro POP_GPR 0 + add rsp, 0x80 + RESTORE_GPR rsp +%endmacro diff --git a/KernelLand/Kernel/arch/x86_64/include/desctab.h b/KernelLand/Kernel/arch/x86_64/include/desctab.h new file mode 100644 index 00000000..de3996c6 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/include/desctab.h @@ -0,0 +1,57 @@ +/** + */ +#ifndef _DESCTAB_H_ +#define _DESCTAB_H_ + +typedef struct { + union + { + struct + { + Uint16 LimitLow; + Uint16 BaseLow; + Uint8 BaseMid; + Uint8 Access; + struct { + unsigned LimitHi: 4; + unsigned Flags: 4; + } __attribute__ ((packed)); + Uint8 BaseHi; + }; + Uint32 DWord[2]; + }; +} __attribute__ ((packed)) tGDT; + +typedef struct { + Uint16 OffsetLo; + Uint16 CS; + Uint16 Flags; // 0-2: IST, 3-7: 0, 8-11: Type, 12: 0, 13-14: DPL, 15: Present + Uint16 OffsetMid; + Uint32 OffsetHi; + Uint32 Reserved; +} __attribute__ ((packed)) tIDT; + +typedef struct { + Uint32 Rsvd1; + + Uint64 RSP0; + Uint64 RSP1; + Uint64 RSP2; + + Uint32 Rsvd2[2]; + + // Interrupt Stack Table Pointers + Uint64 IST1; + Uint64 IST2; + Uint64 IST3; + Uint64 IST4; + Uint64 IST5; + Uint64 IST6; + Uint64 IST7; + + Uint32 Rsvd3[2]; + Uint16 Rsvd4; + Uint16 IOMapBase; +} __attribute__ ((packed)) tTSS; + +#endif diff --git a/KernelLand/Kernel/arch/x86_64/include/mm_virt.h b/KernelLand/Kernel/arch/x86_64/include/mm_virt.h new file mode 100644 index 00000000..6cf25f02 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/include/mm_virt.h @@ -0,0 +1,90 @@ +/* + * Acess2 x86_64 Architecture Code + * + * This file is published under the terms of the Acess Licence. + * See the file COPYING for more details + * + * vmem.h - Virtual Memory Functions & Definitions + */ +#ifndef _VMEM_H_ +#define _VMEM_H_ + +#include + +#define PAGE_SIZE 0x1000 + +// === Memory Location Definitions === +/* + * Userland - Lower Half + * Kernel land - Upper Half + * + * START ADDRESS END ADDRESS BITS SIZE NAME + * 0x00000000 00000000 - 0x00007FFF FFFFFFFF 47 128 TiB User Space + * 0x00008000 00000000 - 0xFFFF7FFF FFFFFFFF --- SIGN EXTENSION NULL ZONE + * 0xFFFF8000 00000000 - 0xFFFFFFFF FFFFFFFF 47 128 TiB Kernel Range + * 8000 00000000 - 9000 00000000 42 16 TiB Kernel Heap + * 9000 00000000 - 9800 00000000 43 8 TiB Module Space + * 9800 00000000 - 9A00 00000000 41 2 TiB Kernel VFS + * ---- GAP ---- 6 TiB + * A000 00000000 - B000 00000000 44 16 TiB Kernel Stacks + * C000 00000000 - D000 00000000 44 16 TiB Hardware Mappings + * D000 00000000 - D080 00000000 39 512 GiB Per-Process Data + * D080 00000000 - D100 00000000 39 512 GiB Kernel Supplied User Code + * ---- GAP ---- 15 TiB + * E000 00000000 - E800 00000000 43 8 TiB Physical Page Nodes (2**40 pages * 8 bytes) + * E800 00000000 - EC00 00000000 42 4 TiB Physical Page Reference Counts (2**40 pg * 4 bytes) + * EC00 00000000 - EC80 00000000 39 512 GiB Physical Page Bitmap (1 page per bit) + * EC80 00000000 - ED00 00000000 39 512 GiB Physical Page DblAlloc Bitmap (1 page per bit) + * ED00 00000000 - ED00 80000000 31 2 GiB Physical Page Super Bitmap (64 pages per bit) + * ---- GAP ---- 9 TiB + * FE00 00000000 - FE80 00000000 39 512 GiB Fractal Mapping (PML4 508) + * FE80 00000000 - FF00 00000000 39 512 GiB Temp Fractal Mapping + * FF00 00000000 - FF80 00000000 39 512 GiB Temporary page mappings + * FF80 00000000 - FF80 80000000 31 2 GiB Local APIC + * ---- GAP ---- 506 GiB + * FFFF 00000000 - FFFF 80000000 31 2 GiB User Code + * FFFF 80000000 - FFFF FFFFFFFF 31 2 GiB Kernel code / data + */ + +#define MM_USER_MIN 0x00000000##00010000 +#define USER_LIB_MAX 0x00007000##00000000 +#define USER_STACK_PREALLOC 0x00000000##00002000 // 8 KiB +#define USER_STACK_SZ 0x00000000##00020000 // 64 KiB +#define USER_STACK_TOP 0x00008000##00000000 +#define MM_KERNEL_RANGE 0xFFFF8000##00000000 +#define MM_KHEAP_BASE (MM_KERNEL_RANGE|(0x8000##00000000)) +#define MM_KHEAP_MAX (MM_KERNEL_RANGE|(0x9000##00000000)) +#define MM_MODULE_MIN (MM_KERNEL_RANGE|(0x9000##00000000)) +#define MM_MODULE_MAX (MM_KERNEL_RANGE|(0x9800##00000000)) +#define MM_KERNEL_VFS (MM_KERNEL_RANGE|(0x9800##00000000)) +#define MM_KSTACK_BASE (MM_KERNEL_RANGE|(0xA000##00000000)) +#define MM_KSTACK_TOP (MM_KERNEL_RANGE|(0xB000##00000000)) + +#define MM_HWMAP_BASE (MM_KERNEL_RANGE|(0xC000##00000000)) +#define MM_HWMAP_TOP (MM_KERNEL_RANGE|(0xD000##00000000)) +#define MM_PPD_BASE (MM_KERNEL_RANGE|(0xD000##00000000)) +#define MM_PPD_CFG MM_PPD_BASE +#define MM_PPD_HANDLES (MM_KERNEL_RANGE|(0xD008##00000000)) +#define MM_USER_CODE (MM_KERNEL_RANGE|(0xD080##00000000)) + +#define MM_PAGE_NODES (MM_KERNEL_RANGE|(0xE000##00000000)) +#define MM_PAGE_COUNTS (MM_KERNEL_RANGE|(0xE800##00000000)) +#define MM_PAGE_BITMAP (MM_KERNEL_RANGE|(0xEC00##00000000)) +#define MM_PAGE_DBLBMP (MM_KERNEL_RANGE|(0xEC00##00000000)) +#define MM_PAGE_SUPBMP (MM_KERNEL_RANGE|(0xED00##00000000)) + +#define MM_FRACTAL_BASE (MM_KERNEL_RANGE|(0xFE00##00000000)) +#define MM_TMPFRAC_BASE (MM_KERNEL_RANGE|(0xFE80##00000000)) +#define MM_TMPMAP_BASE (MM_KERNEL_RANGE|(0xFF00##00000000)) +#define MM_TMPMAP_END (MM_KERNEL_RANGE|(0xFF80##00000000)) +#define MM_LOCALAPIC (MM_KERNEL_RANGE|(0xFF80##00000000)) +#define MM_KERNEL_CODE (MM_KERNEL_RANGE|(0xFFFF##80000000)) + + +// === FUNCTIONS === +void MM_FinishVirtualInit(void); +tVAddr MM_NewKStack(void); +tVAddr MM_Clone(void); +tVAddr MM_NewWorkerStack(void *StackData, size_t StackSize); + +#endif diff --git a/KernelLand/Kernel/arch/x86_64/include/proc.h b/KernelLand/Kernel/arch/x86_64/include/proc.h new file mode 100644 index 00000000..dad8f061 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/include/proc.h @@ -0,0 +1,58 @@ +/* + * Acess2 x86_64 Port + * + * proc.h - Process/Thread management code + */ +#ifndef _PROC_H_ +#define _PROC_H_ + +#include + +// Register Structure +// TODO: Rebuild once IDT code is done +typedef struct { + // MMX + // FPU + Uint FS, GS; + + Uint RAX, RCX, RDX, RBX; + Uint KernelRSP, RBP, RSI, RDI; + Uint R8, R9, R10, R11; + Uint R12, R13, R14, R15; + + Uint IntNum, ErrorCode; + Uint RIP, CS; + Uint RFlags, RSP, SS; +} tRegs; + +/** + * \brief Memory State for thread handler + */ +typedef struct sMemoryState +{ + tPAddr CR3; +} tMemoryState; + +// 512 bytes, 16 byte aligned +typedef struct sSSEState +{ + char data[512]; +} tSSEState; + +/** + * \brief Task state for thread handler + */ +typedef struct sTaskState +{ + Uint RIP, RSP; + Uint64 UserRIP, UserCS; + tSSEState *SSE; + int bSSEModified; +} tTaskState; + +// === CONSTANTS === +#define KERNEL_STACK_SIZE 0x8000 // 32 KiB +//#define KERNEL_STACK_SIZE 0x10000 // 64 KiB + +#endif + diff --git a/KernelLand/Kernel/arch/x86_64/include/vm8086.h b/KernelLand/Kernel/arch/x86_64/include/vm8086.h new file mode 100644 index 00000000..7c8dd47d --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/include/vm8086.h @@ -0,0 +1,67 @@ +/* + * Acess2 VM8086 BIOS Interface + * - By John Hodge (thePowersGang) + * + * vm8086.h + * - Core Header + */ +#ifndef _VM80806_H_ +#define _VM80806_H_ + +// === TYPES === +/** + * \note Semi-opaque - Past \a .IP, the implementation may add any data + * it needs to the state. + */ +typedef struct sVM8086 +{ + Uint16 AX, CX, DX, BX; + Uint16 BP, SP, SI, DI; + + Uint16 SS, DS, ES; + + Uint16 CS, IP; + + struct sVM8086_InternalData *Internal; +} tVM8086; + +// === FUNCTIONS === +/** + * \brief Create an instance of the VM8086 Emulator + * \note Do not free this pointer with ::free, instead use ::VM8086_Free + * \return Pointer to a tVM8086 structure, this structure may be larger than + * tVM8086 due to internal data. + */ +extern tVM8086 *VM8086_Init(void); +/** + * \brief Free an allocated tVM8086 structure + * \param State Emulator state to free + */ +extern void VM8086_Free(tVM8086 *State); +/** + * \brief Allocate a piece of memory in the emulated address space and + * return a host and emulated pointer to it. + * \param State Emulator state + * \param Size Size of memory block + * \param Segment Pointer to location to store the allocated memory's segment + * \param Offset Pointet to location to store the allocated memory's offset + * \return Host pointer to the allocated memory + */ +extern void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset); +/** + * \brief Gets a pointer to a piece of emulated memory + * \todo Only 1 machine page is garenteed to be contiguous + * \param State Emulator State + * \param Segment Source Segment + * \param Offset Source Offset + * \return Host pointer to the emulated memory + */ +extern void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset); +/** + * \brief Calls a real-mode interrupt described by the current state of the IVT. + * \param State Emulator State + * \param Interrupt BIOS Interrupt to call + */ +extern void VM8086_Int(tVM8086 *State, Uint8 Interrupt); + +#endif diff --git a/KernelLand/Kernel/arch/x86_64/kernelpanic.c b/KernelLand/Kernel/arch/x86_64/kernelpanic.c new file mode 100644 index 00000000..1ce8ae70 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/kernelpanic.c @@ -0,0 +1,38 @@ +/* + * Acess2 x86_64 port + * - Kernel Panic output + */ +#include + +// === PROTOTYPES === +void KernelPanic_SetMode(void); +void KernelPanic_PutChar(char ch); + +// === GLOBALS === +Uint16 *gpKernelPanic_Buffer = (void*)( KERNEL_BASE|0xB8000 ); + int giKernelPanic_CurPos = 0; + +// === CODE === +void KernelPanic_SetMode(void) +{ + giKernelPanic_CurPos = 0; +} + +void KernelPanic_PutChar(char ch) +{ + switch(ch) + { + case '\n': + giKernelPanic_CurPos += 80; + case '\r': + giKernelPanic_CurPos /= 80; + giKernelPanic_CurPos *= 80; + break; + + default: + if(' ' <= ch && ch <= 0x7F) + gpKernelPanic_Buffer[giKernelPanic_CurPos] = 0x4F00|ch; + giKernelPanic_CurPos ++; + break; + } +} diff --git a/KernelLand/Kernel/arch/x86_64/lib.c b/KernelLand/Kernel/arch/x86_64/lib.c new file mode 100644 index 00000000..5b9609a6 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/lib.c @@ -0,0 +1,375 @@ +/* + */ +#include +#include + +#define DEBUG_TO_E9 1 +#define DEBUG_TO_SERIAL 1 +#define SERIAL_PORT 0x3F8 +#define GDB_SERIAL_PORT 0x2F8 + + +// === IMPORTS === +extern int GetCPUNum(void); +extern void *Proc_GetCurThread(void); + +// === GLOBALS === + int gbDebug_SerialSetup = 0; + int gbGDB_SerialSetup = 0; + +// === PROTOTYPEs === + int putDebugChar(char ch); + +// === CODE === +/** + * \brief Determine if a short spinlock is locked + * \param Lock Lock pointer + */ +int IS_LOCKED(struct sShortSpinlock *Lock) +{ + return !!Lock->Lock; +} + +/** + * \brief Check if the current CPU has the lock + * \param Lock Lock pointer + */ +int CPU_HAS_LOCK(struct sShortSpinlock *Lock) +{ + #if STACKED_LOCKS == 1 + return Lock->Lock == GetCPUNum() + 1; + #elif STACKED_LOCKS == 2 + return Lock->Lock == Proc_GetCurThread(); + #else + return 0; + #endif +} + +/** + * \brief Acquire a Short Spinlock + * \param Lock Lock pointer + * + * This type of mutex should only be used for very short sections of code, + * or in places where a Mutex_* would be overkill, such as appending + * an element to linked list (usually two assignement lines in C) + * + * \note This type of lock halts interrupts, so ensure that no timing + * functions are called while it is held. As a matter of fact, spend as + * little time as possible with this lock held + * \note If \a STACKED_LOCKS is set, this type of spinlock can be nested + */ +void SHORTLOCK(struct sShortSpinlock *Lock) +{ + int v = 1; + #if LOCK_DISABLE_INTS + int IF; + #endif + #if STACKED_LOCKS == 1 + int cpu = GetCPUNum() + 1; + #elif STACKED_LOCKS == 2 + void *thread = Proc_GetCurThread(); + #endif + + #if LOCK_DISABLE_INTS + // Save interrupt state and clear interrupts + __ASM__ ("pushf;\n\tpop %0" : "=r"(IF)); + IF &= 0x200; // AND out all but the interrupt flag + #endif + + #if STACKED_LOCKS == 1 + if( Lock->Lock == cpu ) { + Lock->Depth ++; + return ; + } + #elif STACKED_LOCKS == 2 + if( Lock->Lock == thread ) { + Lock->Depth ++; + return ; + } + #endif + + // Wait for another CPU to release + while(v) { + // CMPXCHG: + // If r/m32 == EAX, set ZF and set r/m32 = r32 + // Else, clear ZF and set EAX = r/m32 + #if STACKED_LOCKS == 1 + __ASM__("lock cmpxchgl %2, (%3)" + : "=a"(v) + : "a"(0), "r"(cpu), "r"(&Lock->Lock) + ); + #elif STACKED_LOCKS == 2 + __ASM__("lock cmpxchgq %2, (%3)" + : "=a"(v) + : "a"(0), "r"(thread), "r"(&Lock->Lock) + ); + #else + __ASM__("xchgl %0, (%2)":"=a"(v):"a"(1),"D"(&Lock->Lock)); + #endif + + #if LOCK_DISABLE_INTS + if( v ) __ASM__("sti"); // Re-enable interrupts + #endif + } + + #if LOCK_DISABLE_INTS + __ASM__("cli"); + Lock->IF = IF; + #endif +} +/** + * \brief Release a short lock + * \param Lock Lock pointer + */ +void SHORTREL(struct sShortSpinlock *Lock) +{ + #if STACKED_LOCKS + if( Lock->Depth ) { + Lock->Depth --; + return ; + } + #endif + + #if LOCK_DISABLE_INTS + // Lock->IF can change anytime once Lock->Lock is zeroed + if(Lock->IF) { + Lock->Lock = 0; + __ASM__ ("sti"); + } + else { + Lock->Lock = 0; + } + #else + Lock->Lock = 0; + #endif +} + +// === DEBUG IO === +#if USE_GDB_STUB +int putDebugChar(char ch) +{ + if(!gbGDB_SerialSetup) { + outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts + outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) + outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud + outb(GDB_SERIAL_PORT + 1, 0x00); // (base is (hi byte) + outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit (8N1) + outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it + outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set + gbDebug_SerialSetup = 1; + } + while( (inb(GDB_SERIAL_PORT + 5) & 0x20) == 0 ); + outb(GDB_SERIAL_PORT, ch); + return 0; +} +int getDebugChar(void) +{ + if(!gbGDB_SerialSetup) { + outb(GDB_SERIAL_PORT + 1, 0x00); // Disable all interrupts + outb(GDB_SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) + outb(GDB_SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud + outb(GDB_SERIAL_PORT + 1, 0x00); // (hi byte) + outb(GDB_SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit + outb(GDB_SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it + outb(GDB_SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set + gbDebug_SerialSetup = 1; + } + while( (inb(GDB_SERIAL_PORT + 5) & 1) == 0) ; + return inb(GDB_SERIAL_PORT); +} +#endif + +void Debug_PutCharDebug(char ch) +{ + #if DEBUG_TO_E9 + __asm__ __volatile__ ( "outb %%al, $0xe9" :: "a"(((Uint8)ch)) ); + #endif + + #if DEBUG_TO_SERIAL + if(!gbDebug_SerialSetup) { + outb(SERIAL_PORT + 1, 0x00); // Disable all interrupts + outb(SERIAL_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) + outb(SERIAL_PORT + 0, 0x0C); // Set divisor to 12 (lo byte) 9600 baud + outb(SERIAL_PORT + 1, 0x00); // (hi byte) + outb(SERIAL_PORT + 3, 0x03); // 8 bits, no parity, one stop bit + outb(SERIAL_PORT + 2, 0xC7); // Enable FIFO with 14-byte threshold and clear it + outb(SERIAL_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set + gbDebug_SerialSetup = 1; + } + while( (inb(SERIAL_PORT + 5) & 0x20) == 0 ); + outb(SERIAL_PORT, ch); + #endif +} + +void Debug_PutStringDebug(const char *String) +{ + while(*String) + Debug_PutCharDebug(*String++); +} + +// === PORT IO === +void outb(Uint16 Port, Uint8 Data) +{ + __asm__ __volatile__ ("outb %%al, %%dx"::"d"(Port),"a"(Data)); +} +void outw(Uint16 Port, Uint16 Data) +{ + __asm__ __volatile__ ("outw %%ax, %%dx"::"d"(Port),"a"(Data)); +} +void outd(Uint16 Port, Uint32 Data) +{ + __asm__ __volatile__ ("outl %%eax, %%dx"::"d"(Port),"a"(Data)); +} +Uint8 inb(Uint16 Port) +{ + Uint8 ret; + __asm__ __volatile__ ("inb %%dx, %%al":"=a"(ret):"d"(Port)); + return ret; +} +Uint16 inw(Uint16 Port) +{ + Uint16 ret; + __asm__ __volatile__ ("inw %%dx, %%ax":"=a"(ret):"d"(Port)); + return ret; +} +Uint32 ind(Uint16 Port) +{ + Uint32 ret; + __asm__ __volatile__ ("inl %%dx, %%eax":"=a"(ret):"d"(Port)); + return ret; +} + +// === Endianness === +/* +Uint32 BigEndian32(Uint32 Value) +{ + Uint32 ret; + ret = (Value >> 24); + ret |= ((Value >> 16) & 0xFF) << 8; + ret |= ((Value >> 8) & 0xFF) << 16; + ret |= ((Value >> 0) & 0xFF) << 24; + return ret; +} + +Uint16 BigEndian16(Uint16 Value) +{ + return (Value>>8)|(Value<<8); +} +*/ + +// === Memory Manipulation === +int memcmp(const void *__dest, const void *__src, size_t __count) +{ + if( ((tVAddr)__dest & 7) != ((tVAddr)__src & 7) ) { + const Uint8 *src = __src, *dst = __dest; + while(__count) + { + if( *src != *dst ) + return *dst - *src; + src ++; dst ++; __count --; + } + return 0; + } + else { + const Uint8 *src = __src; + const Uint8 *dst = __dest; + const Uint64 *src64, *dst64; + + while( (tVAddr)src & 7 && __count ) { + if( *src != *dst ) + return *dst - *src; + dst ++; src ++; __count --; + } + + src64 = (void*)src; + dst64 = (void*)dst; + + while( __count >= 8 ) + { + if( *src64 != *dst64 ) + { + src = (void*)src64; + dst = (void*)dst64; + if(src[0] != dst[0]) return dst[0]-src[0]; + if(src[1] != dst[1]) return dst[1]-src[1]; + if(src[2] != dst[2]) return dst[2]-src[2]; + if(src[3] != dst[3]) return dst[3]-src[3]; + if(src[4] != dst[4]) return dst[4]-src[4]; + if(src[5] != dst[5]) return dst[5]-src[5]; + if(src[6] != dst[6]) return dst[6]-src[6]; + if(src[7] != dst[7]) return dst[7]-src[7]; + return -1; // This should never happen + } + __count -= 8; + src64 ++; + dst64 ++; + } + + src = (void*)src64; + dst = (void*)dst64; + while( __count-- ) + { + if(*dst != *src) return *dst - *src; + dst ++; + src ++; + } + } + return 0; +} + +void *memcpy(void *__dest, const void *__src, size_t __count) +{ + tVAddr dst = (tVAddr)__dest, src = (tVAddr)__src; + if( (dst & 7) != (src & 7) ) + { + __asm__ __volatile__ ("rep movsb" : : "D"(dst),"S"(src),"c"(__count)); + } + else + { + while( (src & 7) && __count ) { + *(char*)dst++ = *(char*)src++; + __count --; + } + + __asm__ __volatile__ ("rep movsq" : "=D"(dst),"=S"(src) : "0"(dst),"1"(src),"c"(__count/8)); + __count = __count & 7; + while( __count-- ) + *(char*)dst++ = *(char*)src++; + } + return __dest; +} + +void *memset(void *__dest, int __val, size_t __count) +{ + if( __val != 0 || ((tVAddr)__dest & 7) != 0 ) + __asm__ __volatile__ ("rep stosb" : : "D"(__dest),"a"(__val),"c"(__count)); + else { + Uint8 *dst = __dest; + + __asm__ __volatile__ ("rep stosq" : : "D"(dst),"a"(0),"c"(__count/8)); + dst += __count & ~7; + __count = __count & 7; + while( __count-- ) + *dst++ = 0; + } + return __dest; +} + +void *memsetd(void *__dest, Uint32 __val, size_t __count) +{ + __asm__ __volatile__ ("rep stosl" : : "D"(__dest),"a"(__val),"c"(__count)); + return __dest; +} + +Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem) +{ + Uint64 ret, rem; + __asm__ __volatile__( + "div %4" + : "=a" (ret), "=d" (rem) + : "a" ( Num ), "d" (0), "r" (Den) + ); + if(Rem) *Rem = rem; + return ret; +} + diff --git a/KernelLand/Kernel/arch/x86_64/link.ld b/KernelLand/Kernel/arch/x86_64/link.ld new file mode 100644 index 00000000..49fe7ae3 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/link.ld @@ -0,0 +1,64 @@ +/* + * Acess2 x86_64 Kernel + * Linker Script + */ + +/* _kernel_base = 0xFFFF800000000000; */ +/* -2 GiB */ +_kernel_base = 0xFFFFFFFF80000000; + +/* +OUTPUT_FORMAT(elf32-i386) +OUTPUT_ARCH(i386:x86-64) +*/ +OUTPUT_FORMAT(elf64-x86-64) +ENTRY(start) + +SECTIONS { + . = 0x100000; + gKernelBase = .; + . += SIZEOF_HEADERS; + __load_addr = .; + .multiboot : AT(ADDR(.multiboot)) { + *(.multiboot) + } + + . += _kernel_base; + + .text ALIGN(0x1000): AT(ADDR(.text) - _kernel_base) { + *(.text) + } + + .usertext ALIGN(0x1000): AT(ADDR(.usertext) - _kernel_base) { + _UsertextBase = .; + *(.usertext) + _UsertextEnd = .; + } + + .rodata ALIGN(0x1000): AT(ADDR(.rodata) - _kernel_base) { + *(.initpd) + *(.rodata .rodata.*) + *(.rdata) + + . = ALIGN(0x10); + gKernelModules = .; + *(KMODULES) + gKernelModulesEnd = .; + . = ALIGN(0x10); + gKernelSymbols = .; + *(KEXPORT) + gKernelSymbolsEnd = .; + } + + .data ALIGN (0x1000) : AT(ADDR(.data) - _kernel_base) { + *(.padata) + *(.data) + } + + __bss_start = .; + .bss : AT(ADDR(.bss) - _kernel_base) { + *(COMMON) + *(.bss) + } + gKernelEnd = (. + 0xFFF)&0xFFFFFFFFFFFFF000; +} diff --git a/KernelLand/Kernel/arch/x86_64/main.c b/KernelLand/Kernel/arch/x86_64/main.c new file mode 100644 index 00000000..3532c809 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/main.c @@ -0,0 +1,86 @@ +/* + * Acess2 x86_64 Project + */ +#include +#include +#include + +// === IMPORTS === +extern void Desctab_Init(void); +extern void MM_InitVirt(void); +extern void Heap_Install(void); +extern void Threads_Init(void); +extern int Time_Setup(void); +extern void System_Init(char *Commandline); + +extern void MM_InitPhys_Multiboot(tMBoot_Info *MBoot); + +// === PROTOTYPES === +void kmain(Uint MbMagic, void *MbInfoPtr); + +// === GLOBALS == +char *gsBootCmdLine = NULL; + +// === CODE === +void kmain(Uint MbMagic, void *MbInfoPtr) +{ + tMBoot_Info *mbInfo; + + LogF("Acess2 x86_64 v"EXPAND_STR(KERNEL_VERSION)"\n"); + LogF(" Build %i, Git Hash %s\n", BUILD_NUM, gsGitHash); + + Desctab_Init(); + + MM_InitVirt(); + *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'C'; + + switch(MbMagic) + { + // Multiboot 1 + case MULTIBOOT_MAGIC: + // Adjust Multiboot structure address + mbInfo = (void*)( (Uint)MbInfoPtr + KERNEL_BASE ); + gsBootCmdLine = (char*)( (Uint)mbInfo->CommandLine + KERNEL_BASE); + MM_InitPhys_Multiboot( mbInfo ); // Set up physical memory manager + break; + default: + Panic("Multiboot magic invalid %08x, expected %08x\n", + MbMagic, MULTIBOOT_MAGIC); + return ; + } + + Log("gsBootCmdLine = '%s'", gsBootCmdLine); + + *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'D'; + Heap_Install(); + + *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'E'; + Threads_Init(); + + Time_Setup(); + *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'F'; + + // Load Virtual Filesystem + Log_Log("Arch", "Starting VFS..."); + VFS_Init(); + + *(Uint16*)(KERNEL_BASE|0xB8000) = 0x1F00|'Z'; + + // Pass on to Independent Loader + Log_Log("Arch", "Starting system"); + System_Init(gsBootCmdLine); + + // Sleep forever (sleeping beauty) + for(;;) + Threads_Sleep(); +} + +void Arch_LoadBootModules(void) +{ + +} + +void StartupPrint(const char *String) +{ + +} diff --git a/KernelLand/Kernel/arch/x86_64/mm_phys.c b/KernelLand/Kernel/arch/x86_64/mm_phys.c new file mode 100644 index 00000000..c2c215b3 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/mm_phys.c @@ -0,0 +1,641 @@ +/* + * Acess2 x86_64 Port + * + * Physical Memory Manager + */ +#define DEBUG 0 +#include +#include +#include + +#define TRACE_REF 0 + +enum eMMPhys_Ranges +{ + MM_PHYS_16BIT, // Does anything need this? + MM_PHYS_20BIT, // Real-Mode + MM_PHYS_24BIT, // ISA DMA + MM_PHYS_32BIT, // x86 Hardware + MM_PHYS_MAX, // Doesn't care + NUM_MM_PHYS_RANGES +}; + +// === IMPORTS === +extern char gKernelBase[]; +extern char gKernelEnd[]; + +// === PROTOTYPES === +void MM_InitPhys_Multiboot(tMBoot_Info *MBoot); +//tPAddr MM_AllocPhysRange(int Num, int Bits); +//tPAddr MM_AllocPhys(void); +//void MM_RefPhys(tPAddr PAddr); +//void MM_DerefPhys(tPAddr PAddr); + int MM_int_GetRangeID( tPAddr Addr ); + +// === MACROS === +#define PAGE_ALLOC_TEST(__page) (gaMainBitmap[(__page)>>6] & (1ULL << ((__page)&63))) +#define PAGE_ALLOC_SET(__page) do{gaMainBitmap[(__page)>>6] |= (1ULL << ((__page)&63));}while(0) +#define PAGE_ALLOC_CLEAR(__page) do{gaMainBitmap[(__page)>>6] &= ~(1ULL << ((__page)&63));}while(0) +//#define PAGE_MULTIREF_TEST(__page) (gaMultiBitmap[(__page)>>6] & (1ULL << ((__page)&63))) +//#define PAGE_MULTIREF_SET(__page) do{gaMultiBitmap[(__page)>>6] |= 1ULL << ((__page)&63);}while(0) +//#define PAGE_MULTIREF_CLEAR(__page) do{gaMultiBitmap[(__page)>>6] &= ~(1ULL << ((__page)&63));}while(0) + +// === GLOBALS === +tMutex glPhysicalPages; +Uint64 *gaSuperBitmap = (void*)MM_PAGE_SUPBMP; // 1 bit = 64 Pages, 16 MiB per Word +Uint64 *gaMainBitmap = (void*)MM_PAGE_BITMAP; // 1 bit = 1 Page, 256 KiB per Word +Uint64 *gaMultiBitmap = (void*)MM_PAGE_DBLBMP; // Each bit means that the page is being used multiple times +Uint32 *gaiPageReferences = (void*)MM_PAGE_COUNTS; // Reference Counts +void **gapPageNodes = (void*)MM_PAGE_NODES; // Reference Counts +tPAddr giFirstFreePage; // First possibly free page +Uint64 giPhysRangeFree[NUM_MM_PHYS_RANGES]; // Number of free pages in each range +Uint64 giPhysRangeFirst[NUM_MM_PHYS_RANGES]; // First free page in each range +Uint64 giPhysRangeLast[NUM_MM_PHYS_RANGES]; // Last free page in each range +Uint64 giMaxPhysPage = 0; // Maximum Physical page +// Only used in init, allows the init code to provide pages for use by +// the allocator before the bitmaps exist. +// 3 entries because the are three calls to MM_AllocPhys in MM_Map +#define NUM_STATIC_ALLOC 3 +tPAddr gaiStaticAllocPages[NUM_STATIC_ALLOC] = {0}; + +// === CODE === +/** + * \brief Initialise the physical memory map using a Multiboot 1 map + */ +void MM_InitPhys_Multiboot(tMBoot_Info *MBoot) +{ + tMBoot_MMapEnt *mmapStart; + tMBoot_MMapEnt *ent; + Uint64 maxAddr = 0; + int numPages, superPages; + int i; + Uint64 base, size; + tVAddr vaddr; + tPAddr paddr, firstFreePage; + + ENTER("pMBoot=%p", MBoot); + + // Scan the physical memory map + // Looking for the top of physical memory + mmapStart = (void *)( KERNEL_BASE | MBoot->MMapAddr ); + LOG("mmapStart = %p", mmapStart); + ent = mmapStart; + while( (Uint)ent < (Uint)mmapStart + MBoot->MMapLength ) + { + // Adjust for the size of the entry + ent->Size += 4; + LOG("ent={Type:%i,Base:0x%x,Length:%x", + ent->Type, ent->Base, ent->Length); + + // If entry is RAM and is above `maxAddr`, change `maxAddr` + if(ent->Type == 1 && ent->Base + ent->Length > maxAddr) + maxAddr = ent->Base + ent->Length; + + // Go to next entry + ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size ); + } + + // Did we find a valid end? + if(maxAddr == 0) { + // No, darn, let's just use the HighMem hack + giMaxPhysPage = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value + } + else { + // Goodie, goodie gumdrops + giMaxPhysPage = maxAddr >> 12; + } + LOG("giMaxPhysPage = 0x%x", giMaxPhysPage); + + // Find a contigous section of memory to hold it in + // - Starting from the end of the kernel + // - We also need a region for the super bitmap + superPages = ((giMaxPhysPage+64*8-1)/(64*8) + 0xFFF) >> 12; + numPages = (giMaxPhysPage + 7) / 8; + numPages = (numPages + 0xFFF) >> 12; + LOG("numPages = %i, superPages = %i", numPages, superPages); + if(maxAddr == 0) + { + int todo = numPages*2 + superPages; + // Ok, naieve allocation, just put it after the kernel + // - Allocated Bitmap + vaddr = MM_PAGE_BITMAP; + paddr = (tPAddr)&gKernelEnd - KERNEL_BASE; + while(todo ) + { + // Allocate statics + for( i = 0; i < NUM_STATIC_ALLOC; i++) { + if(gaiStaticAllocPages[i] != 0) continue; + gaiStaticAllocPages[i] = paddr; + paddr += 0x1000; + } + + MM_Map(vaddr, paddr); + vaddr += 0x1000; + paddr += 0x1000; + + todo --; + + if( todo == numPages + superPages ) + vaddr = MM_PAGE_DBLBMP; + if( todo == superPages ) + vaddr = MM_PAGE_SUPBMP; + } + } + // Scan for a nice range + else + { + int todo = numPages*2 + superPages; + paddr = 0; + vaddr = MM_PAGE_BITMAP; + // Scan! + for( + ent = mmapStart; + (Uint)ent < (Uint)mmapStart + MBoot->MMapLength; + ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size ) + ) + { + int avail; + + // RAM only please + if( ent->Type != 1 ) + continue; + + // Let's not put it below the kernel, shall we? + if( ent->Base + ent->Size < (tPAddr)&gKernelBase ) + continue; + + LOG("%x <= %x && %x > %x", + ent->Base, (tPAddr)&gKernelBase, + ent->Base + ent->Size, (tPAddr)&gKernelEnd - KERNEL_BASE + ); + // Check if the kernel is in this range + if( ent->Base <= (tPAddr)&gKernelBase + && ent->Base + ent->Length > (tPAddr)&gKernelEnd - KERNEL_BASE ) + { + avail = ent->Length >> 12; + avail -= ((tPAddr)&gKernelEnd - KERNEL_BASE - ent->Base) >> 12; + paddr = (tPAddr)&gKernelEnd - KERNEL_BASE; + } + // No? then we can use all of the block + else + { + avail = ent->Length >> 12; + paddr = ent->Base; + } + + Log("MM_InitPhys_Multiboot: paddr=0x%x, avail=0x%x pg", paddr, avail); + + // Map + while( todo && avail --) + { + // Static Allocations + for( i = 0; i < NUM_STATIC_ALLOC && avail; i++) { + if(gaiStaticAllocPages[i] != 0) continue; + gaiStaticAllocPages[i] = paddr; + paddr += 0x1000; + avail --; + } + if(!avail) break; + + // Map + MM_Map(vaddr, paddr); + todo --; + vaddr += 0x1000; + paddr += 0x1000; + + // Alter the destination address when needed + if(todo == superPages+numPages) + vaddr = MM_PAGE_DBLBMP; + if(todo == superPages) + vaddr = MM_PAGE_SUPBMP; + } + + // Fast quit if there's nothing left to allocate + if( !todo ) break; + } + } + // Save the current value of paddr to simplify the allocation later + firstFreePage = paddr; + + LOG("Clearing multi bitmap"); + // Fill the bitmaps + memset(gaMultiBitmap, 0, (numPages<<12)/8); + // - initialise to one, then clear the avaliable areas + memset(gaMainBitmap, -1, (numPages<<12)/8); + memset(gaSuperBitmap, -1, (numPages<<12)/(8*64)); + LOG("Setting main bitmap"); + // - Clear all Type=1 areas + LOG("Clearing valid regions"); + for( + ent = mmapStart; + (Uint)ent < (Uint)mmapStart + MBoot->MMapLength; + ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size ) + ) + { + // Check if the type is RAM + if(ent->Type != 1) continue; + + // Main bitmap + base = ent->Base >> 12; + size = ent->Size >> 12; + + if(base & 63) { + Uint64 val = -1LL << (base & 63); + gaMainBitmap[base / 64] &= ~val; + size -= (base & 63); + base += 64 - (base & 63); + } + memset( &gaMainBitmap[base / 64], 0, size/8 ); + if( size & 7 ) { + Uint64 val = -1LL << (size & 7); + val <<= (size/8)&7; + gaMainBitmap[base / 64] &= ~val; + } + + // Super Bitmap + base = ent->Base >> 12; + size = ent->Size >> 12; + size = (size + (base & 63) + 63) >> 6; + base = base >> 6; + if(base & 63) { + Uint64 val = -1LL << (base & 63); + gaSuperBitmap[base / 64] &= ~val; +// size -= (base & 63); +// base += 64 - (base & 63); + } + } + + // Reference the used pages + base = (tPAddr)&gKernelBase >> 12; + size = firstFreePage >> 12; + memset( &gaMainBitmap[base / 64], -1, size/8 ); + if( size & 7 ) { + Uint64 val = -1LL << (size & 7); + val <<= (size/8)&7; + gaMainBitmap[base / 64] |= val; + } + + // Free the unused static allocs + for( i = 0; i < NUM_STATIC_ALLOC; i++) { + if(gaiStaticAllocPages[i] != 0) + continue; + gaMainBitmap[ gaiStaticAllocPages[i] >> (12+6) ] + &= ~(1LL << ((gaiStaticAllocPages[i]>>12)&63)); + } + + // Fill the super bitmap + LOG("Filling super bitmap"); + memset(gaSuperBitmap, 0, superPages<<12); + for( base = 0; base < (size+63)/64; base ++) + { + if( gaMainBitmap[ base ] + 1 == 0 ) + gaSuperBitmap[ base/64 ] |= 1LL << (base&63); + } + + // Set free page counts + for( base = 1; base < giMaxPhysPage; base ++ ) + { + int rangeID; + // Skip allocated + if( gaMainBitmap[ base >> 6 ] & (1LL << (base&63)) ) continue; + + // Get range ID + rangeID = MM_int_GetRangeID( base << 12 ); + + // Increment free page count + giPhysRangeFree[ rangeID ] ++; + + // Check for first free page in range + if(giPhysRangeFirst[ rangeID ] == 0) + giPhysRangeFirst[ rangeID ] = base; + // Set last (when the last free page is reached, this won't be + // updated anymore, hence will be correct) + giPhysRangeLast[ rangeID ] = base; + } + + LEAVE('-'); +} + +/** + * \brief Allocate a contiguous range of physical pages with a maximum + * bit size of \a MaxBits + * \param Pages Number of pages to allocate + * \param MaxBits Maximum size of the physical address + * \note If \a MaxBits is <= 0, any sized address is used (with preference + * to higher addresses) + */ +tPAddr MM_AllocPhysRange(int Pages, int MaxBits) +{ + tPAddr addr, ret; + int rangeID; + int nFree = 0, i; + + ENTER("iPages iBits", Pages, MaxBits); + + if( MaxBits <= 0 || MaxBits >= 64 ) // Speedup for the common case + rangeID = MM_PHYS_MAX; + else + rangeID = MM_int_GetRangeID( (1LL << MaxBits) - 1 ); + + LOG("rangeID = %i", rangeID); + + Mutex_Acquire(&glPhysicalPages); + + // Check if the range actually has any free pages + while(giPhysRangeFree[rangeID] == 0 && rangeID) + rangeID --; + + LOG("rangeID = %i", rangeID); + + // What the? Oh, man. No free pages + if(giPhysRangeFree[rangeID] == 0) { + Mutex_Release(&glPhysicalPages); + // TODO: Page out + // ATM. Just Warning + Warning(" MM_AllocPhysRange: Out of free pages"); + Log_Warning("Arch", + "Out of memory (unable to fulfil request for %i pages), zero remaining", + Pages + ); + LEAVE('i', 0); + return 0; + } + + // Check if there is enough in the range + if(giPhysRangeFree[rangeID] >= Pages) + { + LOG("{%i,0x%x -> 0x%x}", + giPhysRangeFree[rangeID], + giPhysRangeFirst[rangeID], giPhysRangeLast[rangeID] + ); + // Do a cheap scan, scanning upwards from the first free page in + // the range + nFree = 0; + addr = giPhysRangeFirst[ rangeID ]; + while( addr <= giPhysRangeLast[ rangeID ] ) + { + //Log(" MM_AllocPhysRange: addr = 0x%x", addr); + // Check the super bitmap + if( gaSuperBitmap[addr >> (6+6)] + 1 == 0 ) { + LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr); + nFree = 0; + addr += 1LL << (6+6); + addr &= ~0xFFF; // (1LL << 6+6) - 1 + continue; + } + // Check page block (64 pages) + if( gaMainBitmap[addr >> 6] + 1 == 0) { + LOG("nFree = %i = 0 (main) (0x%x)", nFree, addr); + nFree = 0; + addr += 1LL << (6); + addr &= ~0x3F; + continue; + } + // Check individual page + if( gaMainBitmap[addr >> 6] & (1LL << (addr & 63)) ) { + LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr); + nFree = 0; + addr ++; + continue; + } + nFree ++; + addr ++; + LOG("nFree(%i) == %i (0x%x)", nFree, Pages, addr); + if(nFree == Pages) + break; + } + LOG("nFree = %i", nFree); + // If we don't find a contiguous block, nFree will not be equal + // to Num, so we set it to zero and do the expensive lookup. + if(nFree != Pages) nFree = 0; + } + + if( !nFree ) + { + // Oops. ok, let's do an expensive check (scan down the list + // until a free range is found) +// nFree = 1; +// addr = giPhysRangeLast[ rangeID ]; + // TODO: Expensive Check + Mutex_Release(&glPhysicalPages); + // TODO: Page out + // ATM. Just Warning + Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Pages); + Log_Warning("Arch", + "Out of memory (unable to fulfil request for %i pages)", + Pages + ); + LEAVE('i', 0); + return 0; + } + LOG("nFree = %i, addr = 0x%08x", nFree, addr); + + // Mark pages as allocated + addr -= Pages; + for( i = 0; i < Pages; i++, addr++ ) + { + gaMainBitmap[addr >> 6] |= 1LL << (addr & 63); + if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[addr] ) ) + gaiPageReferences[addr] = 1; +// Log("page %P refcount = %i", MM_GetRefCount(addr<<12)); + rangeID = MM_int_GetRangeID(addr << 12); + giPhysRangeFree[ rangeID ] --; + LOG("%x == %x", addr, giPhysRangeFirst[ rangeID ]); + if(addr == giPhysRangeFirst[ rangeID ]) + giPhysRangeFirst[ rangeID ] += 1; + } + addr -= Pages; + ret = addr; // Save the return address + + // Update super bitmap + Pages += addr & (64-1); + addr &= ~(64-1); + Pages = (Pages + (64-1)) & ~(64-1); + for( i = 0; i < Pages/64; i++ ) + { + if( gaMainBitmap[ addr >> 6 ] + 1 == 0 ) + gaSuperBitmap[addr>>12] |= 1LL << ((addr >> 6) & 63); + } + + Mutex_Release(&glPhysicalPages); + #if TRACE_REF + Log("MM_AllocPhysRange: ret = %P (Ref %i)", ret << 12, MM_GetRefCount(ret<<12)); + #endif + LEAVE('x', ret << 12); + return ret << 12; +} + +/** + * \brief Allocate a single physical page, with no preference as to address + * size. + */ +tPAddr MM_AllocPhys(void) +{ + int i; + + // Hack to allow allocation during setup + for(i = 0; i < NUM_STATIC_ALLOC; i++) { + if( gaiStaticAllocPages[i] ) { + tPAddr ret = gaiStaticAllocPages[i]; + gaiStaticAllocPages[i] = 0; + Log("MM_AllocPhys: Return %P, static alloc %i", ret, i); + return ret; + } + } + + return MM_AllocPhysRange(1, -1); +} + +/** + * \brief Reference a physical page + */ +void MM_RefPhys(tPAddr PAddr) +{ + Uint64 page = PAddr >> 12; + + if( page > giMaxPhysPage ) return ; + + if( PAGE_ALLOC_TEST(page) ) + { + tVAddr ref_base = ((tVAddr)&gaiPageReferences[ page ]) & ~0xFFF; + // Allocate reference page + if( !MM_GetPhysAddr(ref_base) ) + { + const int pages_per_refpage = PAGE_SIZE/sizeof(gaiPageReferences[0]); + int i; + int page_base = page / pages_per_refpage * pages_per_refpage; + if( !MM_Allocate( ref_base ) ) { + Log_Error("Arch", "Out of memory when allocating reference count page"); + return ; + } + // Fill block + Log("Allocated references for %P-%P", page_base << 12, (page_base+pages_per_refpage)<<12); + for( i = 0; i < pages_per_refpage; i ++ ) { + int pg = page_base + i; + gaiPageReferences[pg] = !!PAGE_ALLOC_TEST(pg); + } + } + gaiPageReferences[page] ++; + } + else + { + // Allocate + PAGE_ALLOC_SET(page); + if( gaMainBitmap[page >> 6] + 1 == 0 ) + gaSuperBitmap[page>> 12] |= 1LL << ((page >> 6) & 63); + if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[page] ) ) + gaiPageReferences[page] = 1; + } + + #if TRACE_REF + Log("MM_RefPhys: %P referenced (%i)", page << 12, MM_GetRefCount(page << 12)); + #endif +} + +/** + * \brief Dereference a physical page + */ +void MM_DerefPhys(tPAddr PAddr) +{ + Uint64 page = PAddr >> 12; + + if( PAddr >> 12 > giMaxPhysPage ) return ; + + if( MM_GetPhysAddr( (tVAddr) &gaiPageReferences[page] ) ) + { + gaiPageReferences[ page ] --; + if( gaiPageReferences[ page ] == 0 ) + PAGE_ALLOC_CLEAR(page); + } + else + PAGE_ALLOC_CLEAR(page); + + // Update the free counts if the page was freed + if( !PAGE_ALLOC_TEST(page) ) + { + int rangeID; + rangeID = MM_int_GetRangeID( PAddr ); + giPhysRangeFree[ rangeID ] ++; + if( giPhysRangeFirst[rangeID] > page ) + giPhysRangeFirst[rangeID] = page; + if( giPhysRangeLast[rangeID] < page ) + giPhysRangeLast[rangeID] = page; + } + + // If the bitmap entry is not -1, unset the bit in the super bitmap + if(gaMainBitmap[ page >> 6 ] + 1 != 0 ) { + gaSuperBitmap[page >> 12] &= ~(1LL << ((page >> 6) & 63)); + } + + #if TRACE_REF + Log("Page %P dereferenced (%i)", page << 12, MM_GetRefCount(page << 12)); + #endif +} + +int MM_GetRefCount( tPAddr PAddr ) +{ + PAddr >>= 12; + + if( PAddr > giMaxPhysPage ) return 0; + + if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[PAddr] ) ) { + return gaiPageReferences[PAddr]; + } + + if( PAGE_ALLOC_TEST(PAddr) ) + { + return 1; + } + return 0; +} + +/** + * \brief Takes a physical address and returns the ID of its range + * \param Addr Physical address of page + * \return Range ID from eMMPhys_Ranges + */ +int MM_int_GetRangeID( tPAddr Addr ) +{ + if(Addr >> 32) + return MM_PHYS_MAX; + else if(Addr >> 24) + return MM_PHYS_32BIT; + else if(Addr >> 20) + return MM_PHYS_24BIT; + else if(Addr >> 16) + return MM_PHYS_20BIT; + else + return MM_PHYS_16BIT; +} + +int MM_SetPageNode(tPAddr PAddr, void *Node) +{ + tPAddr page = PAddr >> 12; + tVAddr node_page = ((tVAddr)&gapPageNodes[page]) & ~(PAGE_SIZE-1); + +// if( !MM_GetRefCount(PAddr) ) return 1; + + if( !MM_GetPhysAddr(node_page) ) { + if( !MM_Allocate(node_page) ) + return -1; + memset( (void*)node_page, 0, PAGE_SIZE ); + } + + gapPageNodes[page] = Node; + return 0; +} + +int MM_GetPageNode(tPAddr PAddr, void **Node) +{ +// if( !MM_GetRefCount(PAddr) ) return 1; + PAddr >>= 12; + + if( !MM_GetPhysAddr( (tVAddr)&gapPageNodes[PAddr] ) ) { + *Node = NULL; + return 0; + } + + *Node = gapPageNodes[PAddr]; + return 0; +} + diff --git a/KernelLand/Kernel/arch/x86_64/mm_virt.c b/KernelLand/Kernel/arch/x86_64/mm_virt.c new file mode 100644 index 00000000..89aeaa15 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/mm_virt.c @@ -0,0 +1,1110 @@ +/* + * Acess2 x86_64 Port + * + * Virtual Memory Manager + */ +#define DEBUG 0 +#include +#include +#include +#include +#include + +// === DEBUG OPTIONS === +#define TRACE_COW 0 + +// === CONSTANTS === +#define PHYS_BITS 52 // TODO: Move out +#define VIRT_BITS 48 + +#define PML4_SHIFT 39 +#define PDP_SHIFT 30 +#define PDIR_SHIFT 21 +#define PTAB_SHIFT 12 + +#define PADDR_MASK 0x7FFFFFFF##FFFFF000 +#define PAGE_MASK ((1LL << 36)-1) +#define TABLE_MASK ((1LL << 27)-1) +#define PDP_MASK ((1LL << 18)-1) +#define PML4_MASK ((1LL << 9)-1) + +#define PF_PRESENT 0x001 +#define PF_WRITE 0x002 +#define PF_USER 0x004 +#define PF_LARGE 0x080 +#define PF_GLOBAL 0x100 +#define PF_COW 0x200 +#define PF_PAGED 0x400 +#define PF_NX 0x80000000##00000000 + +// === MACROS === +#define PAGETABLE(idx) (*((Uint64*)MM_FRACTAL_BASE+((idx)&PAGE_MASK))) +#define PAGEDIR(idx) PAGETABLE((MM_FRACTAL_BASE>>12)+((idx)&TABLE_MASK)) +#define PAGEDIRPTR(idx) PAGEDIR((MM_FRACTAL_BASE>>21)+((idx)&PDP_MASK)) +#define PAGEMAPLVL4(idx) PAGEDIRPTR((MM_FRACTAL_BASE>>30)+((idx)&PML4_MASK)) + +#define TMPCR3() PAGEMAPLVL4(MM_TMPFRAC_BASE>>39) +#define TMPTABLE(idx) (*((Uint64*)MM_TMPFRAC_BASE+((idx)&PAGE_MASK))) +#define TMPDIR(idx) PAGETABLE((MM_TMPFRAC_BASE>>12)+((idx)&TABLE_MASK)) +#define TMPDIRPTR(idx) PAGEDIR((MM_TMPFRAC_BASE>>21)+((idx)&PDP_MASK)) +#define TMPMAPLVL4(idx) PAGEDIRPTR((MM_TMPFRAC_BASE>>30)+((idx)&PML4_MASK)) + +#define INVLPG(__addr) __asm__ __volatile__ ("invlpg (%0)"::"r"(__addr)) +#define INVLPG_ALL() __asm__ __volatile__ ("mov %cr3,%rax;\n\tmov %rax,%cr3;") +#define INVLPG_GLOBAL() __asm__ __volatile__ ("mov %cr4,%rax;\n\txorl $0x80, %eax;\n\tmov %rax,%cr4;\n\txorl $0x80, %eax;\n\tmov %rax,%cr4") + +// === CONSTS === +//tPAddr * const gaPageTable = MM_FRACTAL_BASE; + +// === IMPORTS === +extern void Error_Backtrace(Uint IP, Uint BP); +extern tPAddr gInitialPML4[512]; +extern void Threads_SegFault(tVAddr Addr); +extern char _UsertextBase[]; + +// === PROTOTYPES === +void MM_InitVirt(void); +//void MM_FinishVirtualInit(void); +void MM_int_ClonePageEnt( Uint64 *Ent, void *NextLevel, tVAddr Addr, int bTable ); + int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs); +void MM_int_DumpTablesEnt(tVAddr RangeStart, size_t Length, tPAddr Expected); +//void MM_DumpTables(tVAddr Start, tVAddr End); + int MM_GetPageEntryPtr(tVAddr Addr, BOOL bTemp, BOOL bAllocate, BOOL bLargePage, tPAddr **Pointer); + int MM_MapEx(tVAddr VAddr, tPAddr PAddr, BOOL bTemp, BOOL bLarge); +// int MM_Map(tVAddr VAddr, tPAddr PAddr); +void MM_Unmap(tVAddr VAddr); +void MM_int_ClearTableLevel(tVAddr VAddr, int LevelBits, int MaxEnts); +//void MM_ClearUser(void); + int MM_GetPageEntry(tVAddr Addr, tPAddr *Phys, Uint *Flags); + +// === GLOBALS === +tMutex glMM_TempFractalLock; +tPAddr gMM_ZeroPage; + +// === CODE === +void MM_InitVirt(void) +{ +// Log_Debug("MMVirt", "&PAGEMAPLVL4(0) = %p", &PAGEMAPLVL4(0)); +// MM_DumpTables(0, -1L); +} + +void MM_FinishVirtualInit(void) +{ + PAGEMAPLVL4(0) = 0; +} + +/** + * \brief Clone a page from an entry + * \param Ent Pointer to the entry in the PML4/PDP/PD/PT + * \param NextLevel Pointer to contents of the entry + * \param Addr Dest address + * \note Used in COW + */ +void MM_int_ClonePageEnt( Uint64 *Ent, void *NextLevel, tVAddr Addr, int bTable ) +{ + tPAddr curpage = *Ent & PADDR_MASK; + int bCopied = 0; + + if( MM_GetRefCount( curpage ) <= 0 ) { + Log_KernelPanic("MMVirt", "Page %P still marked COW, but unreferenced", curpage); + } + if( MM_GetRefCount( curpage ) == 1 ) + { + *Ent &= ~PF_COW; + *Ent |= PF_PRESENT|PF_WRITE; + #if TRACE_COW + Log_Debug("MMVirt", "COW ent at %p (%p) only %P", Ent, NextLevel, curpage); + #endif + } + else + { + void *tmp; + tPAddr paddr; + + if( !(paddr = MM_AllocPhys()) ) { + Threads_SegFault(Addr); + return ; + } + + ASSERT(paddr != curpage); + + tmp = (void*)MM_MapTemp(paddr); + memcpy( tmp, NextLevel, 0x1000 ); + MM_FreeTemp( (tVAddr)tmp ); + + #if TRACE_COW + Log_Debug("MMVirt", "COW ent at %p (%p) from %P to %P", Ent, NextLevel, curpage, paddr); + #endif + + MM_DerefPhys( curpage ); + *Ent &= PF_USER; + *Ent |= paddr|PF_PRESENT|PF_WRITE; + + bCopied = 1; + } + INVLPG( (tVAddr)NextLevel ); + + // Mark COW on contents if it's a PDPT, Dir or Table + if(bTable) + { + Uint64 *dp = NextLevel; + int i; + for( i = 0; i < 512; i ++ ) + { + if( !(dp[i] & PF_PRESENT) ) + continue; + + if( bCopied ) + MM_RefPhys( dp[i] & PADDR_MASK ); + if( dp[i] & PF_WRITE ) { + dp[i] &= ~PF_WRITE; + dp[i] |= PF_COW; + } + } + } +} + +/* + * \brief Called on a page fault + */ +int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs) +{ +// Log_Debug("MMVirt", "Addr = %p, ErrorCode = %x", Addr, ErrorCode); + + // Catch reserved bits first + if( ErrorCode & 0x8 ) + { + Log_Warning("MMVirt", "Reserved bits trashed!"); + Log_Warning("MMVirt", "PML4 Ent = %P", PAGEMAPLVL4(Addr>>39)); + if( !(PAGEMAPLVL4(Addr>>39) & PF_PRESENT) ) goto print_done; + Log_Warning("MMVirt", "PDP Ent = %P", PAGEDIRPTR(Addr>>30)); + if( !(PAGEDIRPTR(Addr>>30) & PF_PRESENT) ) goto print_done; + Log_Warning("MMVirt", "PDir Ent = %P", PAGEDIR(Addr>>21)); + if( !(PAGEDIR(Addr>>21) & PF_PRESENT) ) goto print_done; + Log_Warning("MMVirt", "PTable Ent = %P", PAGETABLE(Addr>>12)); + if( !(PAGETABLE(Addr>>12) & PF_PRESENT) ) goto print_done; + print_done: + + for(;;); + } + + // TODO: Implement Copy-on-Write + #if 1 + if( PAGEMAPLVL4(Addr>>39) & PF_PRESENT + && PAGEDIRPTR (Addr>>30) & PF_PRESENT + && PAGEDIR (Addr>>21) & PF_PRESENT + && PAGETABLE (Addr>>12) & PF_PRESENT ) + { + // PML4 Entry + if( PAGEMAPLVL4(Addr>>39) & PF_COW ) + { + tPAddr *dp = &PAGEDIRPTR((Addr>>39)*512); + MM_int_ClonePageEnt( &PAGEMAPLVL4(Addr>>39), dp, Addr, 1 ); +// MM_DumpTables(Addr>>39 << 39, (((Addr>>39) + 1) << 39) - 1); + } + // PDP Entry + if( PAGEDIRPTR(Addr>>30) & PF_COW ) + { + tPAddr *dp = &PAGEDIR( (Addr>>30)*512 ); + MM_int_ClonePageEnt( &PAGEDIRPTR(Addr>>30), dp, Addr, 1 ); +// MM_DumpTables(Addr>>30 << 30, (((Addr>>30) + 1) << 30) - 1); + } + // PD Entry + if( PAGEDIR(Addr>>21) & PF_COW ) + { + tPAddr *dp = &PAGETABLE( (Addr>>21)*512 ); + MM_int_ClonePageEnt( &PAGEDIR(Addr>>21), dp, Addr, 1 ); +// MM_DumpTables(Addr>>21 << 21, (((Addr>>21) + 1) << 21) - 1); + } + // PT Entry + if( PAGETABLE(Addr>>12) & PF_COW ) + { + MM_int_ClonePageEnt( &PAGETABLE(Addr>>12), (void*)(Addr & ~0xFFF), Addr, 0 ); + INVLPG( Addr & ~0xFFF ); + return 0; + } + } + #endif + + // If it was a user, tell the thread handler + if(ErrorCode & 4) { + Warning("User %s %s memory%s", + (ErrorCode&2?"write to":"read from"), + (ErrorCode&1?"bad/locked":"non-present"), + (ErrorCode&16?" (Instruction Fetch)":"") + ); + Warning("User Pagefault: Instruction at %04x:%p accessed %p", + Regs->CS, Regs->RIP, Addr); + __asm__ __volatile__ ("sti"); // Restart IRQs + Error_Backtrace(Regs->RIP, Regs->RBP); + Threads_SegFault(Addr); + return 0; + } + + // Kernel #PF + Debug_KernelPanic(); + // -- Check Error Code -- + if(ErrorCode & 8) + Warning("Reserved Bits Trashed!"); + else + { + Warning("Kernel %s %s memory%s", + (ErrorCode&2?"write to":"read from"), + (ErrorCode&1?"bad/locked":"non-present"), + (ErrorCode&16?" (Instruction Fetch)":"") + ); + } + + Log("Thread %i - Code at %p accessed %p", Threads_GetTID(), Regs->RIP, Addr); + // Print Stack Backtrace + Error_Backtrace(Regs->RIP, Regs->RBP); + + MM_DumpTables(0, -1); + + return 1; +} + +void MM_int_DumpTablesEnt(tVAddr RangeStart, size_t Length, tPAddr Expected) +{ + #define CANOICAL(addr) ((addr)&0x800000000000?(addr)|0xFFFF000000000000:(addr)) + LogF("%016llx => ", CANOICAL(RangeStart)); +// LogF("%6llx %6llx %6llx %016llx => ", +// MM_GetPhysAddr( (tVAddr)&PAGEDIRPTR(RangeStart>>30) ), +// MM_GetPhysAddr( (tVAddr)&PAGEDIR(RangeStart>>21) ), +// MM_GetPhysAddr( (tVAddr)&PAGETABLE(RangeStart>>12) ), +// CANOICAL(RangeStart) +// ); + if( gMM_ZeroPage && (PAGETABLE(RangeStart>>12) & PADDR_MASK) == gMM_ZeroPage ) + LogF("%13s", "zero" ); + else + LogF("%13llx", PAGETABLE(RangeStart>>12) & PADDR_MASK ); + LogF(" : 0x%6llx (%c%c%c%c%c%c)\r\n", + Length, + (Expected & PF_GLOBAL ? 'G' : '-'), + (Expected & PF_NX ? '-' : 'x'), + (Expected & PF_PAGED ? 'p' : '-'), + (Expected & PF_COW ? 'C' : '-'), + (Expected & PF_USER ? 'U' : '-'), + (Expected & PF_WRITE ? 'W' : '-') + ); + #undef CANOICAL +} + +/** + * \brief Dumps the layout of the page tables + */ +void MM_DumpTables(tVAddr Start, tVAddr End) +{ + const tPAddr FIXED_BITS = PF_PRESENT|PF_WRITE|PF_USER|PF_COW|PF_PAGED|PF_NX|PF_GLOBAL; + const tPAddr CHANGEABLE_BITS = ~FIXED_BITS & 0xFFF; + const tPAddr MASK = ~CHANGEABLE_BITS; // Physical address and access bits + tVAddr rangeStart = 0; + tPAddr expected = CHANGEABLE_BITS; // CHANGEABLE_BITS is used because it's not a vaild value + tVAddr curPos; + Uint page; + tPAddr expected_pml4 = PF_WRITE|PF_USER; + tPAddr expected_pdp = PF_WRITE|PF_USER; + tPAddr expected_pd = PF_WRITE|PF_USER; + + Log("Table Entries: (%p to %p)", Start, End); + + End &= (1L << 48) - 1; + + Start >>= 12; End >>= 12; + + for(page = Start, curPos = Start<<12; + page < End; + curPos += 0x1000, page++) + { + //Debug("&PAGEMAPLVL4(%i page>>27) = %p", page>>27, &PAGEMAPLVL4(page>>27)); + //Debug("&PAGEDIRPTR(%i page>>18) = %p", page>>18, &PAGEDIRPTR(page>>18)); + //Debug("&PAGEDIR(%i page>>9) = %p", page>>9, &PAGEDIR(page>>9)); + //Debug("&PAGETABLE(%i page) = %p", page, &PAGETABLE(page)); + + // End of a range + if(!(PAGEMAPLVL4(page>>27) & PF_PRESENT) + || (PAGEMAPLVL4(page>>27) & FIXED_BITS) != expected_pml4 + || !(PAGEDIRPTR(page>>18) & PF_PRESENT) + || (PAGEDIRPTR(page>>18) & FIXED_BITS) != expected_pdp + || !(PAGEDIR(page>>9) & PF_PRESENT) + || (PAGEDIR(page>>9) & FIXED_BITS) != expected_pd + || !(PAGETABLE(page) & PF_PRESENT) + || (PAGETABLE(page) & MASK) != expected) + { + if(expected != CHANGEABLE_BITS) + { + // Merge + expected &= expected_pml4 | ~(PF_WRITE|PF_USER); + expected &= expected_pdp | ~(PF_WRITE|PF_USER); + expected &= expected_pd | ~(PF_WRITE|PF_USER); + expected |= expected_pml4 & PF_NX; + expected |= expected_pdp & PF_NX; + expected |= expected_pd & PF_NX; + Log("expected (pml4 = %x, pdp = %x, pd = %x)", + expected_pml4, expected_pdp, expected_pd); + // Dump + MM_int_DumpTablesEnt( rangeStart, curPos - rangeStart, expected ); + expected = CHANGEABLE_BITS; + } + + if( curPos == 0x800000000000L ) + curPos = 0xFFFF800000000000L; + + if( !(PAGEMAPLVL4(page>>27) & PF_PRESENT) ) { + page += (1 << 27) - 1; + curPos += (1L << 39) - 0x1000; + continue; + } + if( !(PAGEDIRPTR(page>>18) & PF_PRESENT) ) { + page += (1 << 18) - 1; + curPos += (1L << 30) - 0x1000; + continue; + } + if( !(PAGEDIR(page>>9) & PF_PRESENT) ) { + page += (1 << 9) - 1; + curPos += (1L << 21) - 0x1000; + continue; + } + if( !(PAGETABLE(page) & PF_PRESENT) ) continue; + + expected = (PAGETABLE(page) & MASK); + expected_pml4 = (PAGEMAPLVL4(page>>27) & FIXED_BITS); + expected_pdp = (PAGEDIRPTR (page>>18) & FIXED_BITS); + expected_pd = (PAGEDIR (page>> 9) & FIXED_BITS); + rangeStart = curPos; + } + if(gMM_ZeroPage && (expected & PADDR_MASK) == gMM_ZeroPage ) + expected = expected; + else if(expected != CHANGEABLE_BITS) + expected += 0x1000; + } + + if(expected != CHANGEABLE_BITS) { + // Merge + + // Dump + MM_int_DumpTablesEnt( rangeStart, curPos - rangeStart, expected ); + expected = 0; + } +} + +/** + * \brief Get a pointer to a page entry + * \param Addr Virtual Address + * \param bTemp Use the Temporary fractal mapping + * \param bAllocate Allocate entries + * \param bLargePage Request a large page + * \param Pointer Location to place the calculated pointer + * \return Page size, or -ve on error + */ +int MM_GetPageEntryPtr(tVAddr Addr, BOOL bTemp, BOOL bAllocate, BOOL bLargePage, tPAddr **Pointer) +{ + tPAddr *pmlevels[4]; + tPAddr tmp; + int i, size; + + #define BITMASK(bits) ( (1LL << (bits))-1 ) + + if( bTemp ) + { + pmlevels[3] = &TMPTABLE(0); // Page Table + pmlevels[2] = &TMPDIR(0); // PDIR + pmlevels[1] = &TMPDIRPTR(0); // PDPT + pmlevels[0] = &TMPMAPLVL4(0); // PML4 + } + else + { + pmlevels[3] = (void*)MM_FRACTAL_BASE; // Page Table + pmlevels[2] = &pmlevels[3][(MM_FRACTAL_BASE>>12)&BITMASK(VIRT_BITS-12)]; // PDIR + pmlevels[1] = &pmlevels[2][(MM_FRACTAL_BASE>>21)&BITMASK(VIRT_BITS-21)]; // PDPT + pmlevels[0] = &pmlevels[1][(MM_FRACTAL_BASE>>30)&BITMASK(VIRT_BITS-30)]; // PML4 + } + + // Mask address + Addr &= (1ULL << 48)-1; + + for( size = 39, i = 0; size > 12; size -= 9, i ++ ) + { + Uint64 *ent = &pmlevels[i][Addr >> size]; +// INVLPG( &pmlevels[i][ (Addr >> ADDR_SIZES[i]) & + + // Check for a free large page slot + // TODO: Better support with selectable levels + if( (Addr & ((1ULL << size)-1)) == 0 && bLargePage ) + { + if(Pointer) *Pointer = ent; + return size; + } + // Allocate an entry if required + if( !(*ent & PF_PRESENT) ) + { + if( !bAllocate ) return -4; // If allocation is not requested, error + if( !(tmp = MM_AllocPhys()) ) return -2; + *ent = tmp | 3; + if( Addr < 0x800000000000 ) + *ent |= PF_USER; + INVLPG( &pmlevels[i+1][ (Addr>>size)*512 ] ); + memset( &pmlevels[i+1][ (Addr>>size)*512 ], 0, 0x1000 ); + LOG("Init PML%i ent 0x%x %p with %P (*ent = %P)", 4 - i, + Addr>>size, (Addr>>size) << size, tmp, *ent); + } + // Catch large pages + else if( *ent & PF_LARGE ) + { + // Alignment + if( (Addr & ((1ULL << size)-1)) != 0 ) return -3; + if(Pointer) *Pointer = ent; + return size; // Large page warning + } + } + + // And, set the page table entry + if(Pointer) *Pointer = &pmlevels[i][Addr >> size]; + return size; +} + +/** + * \brief Map a physical page to a virtual one + * \param VAddr Target virtual address + * \param PAddr Physical address of page + * \param bTemp Use tempoary mappings + * \param bLarge Treat as a large page + */ +int MM_MapEx(tVAddr VAddr, tPAddr PAddr, BOOL bTemp, BOOL bLarge) +{ + tPAddr *ent; + int rv; + + ENTER("pVAddr PPAddr", VAddr, PAddr); + + // Get page pointer (Allow allocating) + rv = MM_GetPageEntryPtr(VAddr, bTemp, 1, bLarge, &ent); + if(rv < 0) LEAVE_RET('i', 0); + + if( *ent & 1 ) LEAVE_RET('i', 0); + + *ent = PAddr | 3; + + if( VAddr < 0x800000000000 ) + *ent |= PF_USER; + + INVLPG( VAddr ); + + LEAVE('i', 1); + return 1; +} + +/** + * \brief Map a physical page to a virtual one + * \param VAddr Target virtual address + * \param PAddr Physical address of page + */ +int MM_Map(tVAddr VAddr, tPAddr PAddr) +{ + return MM_MapEx(VAddr, PAddr, 0, 0); +} + +/** + * \brief Removed a mapped page + */ +void MM_Unmap(tVAddr VAddr) +{ + // Check PML4 + if( !(PAGEMAPLVL4(VAddr >> 39) & 1) ) return ; + // Check PDP + if( !(PAGEDIRPTR(VAddr >> 30) & 1) ) return ; + // Check Page Dir + if( !(PAGEDIR(VAddr >> 21) & 1) ) return ; + + PAGETABLE(VAddr >> PTAB_SHIFT) = 0; + INVLPG( VAddr ); +} + +/** + * \brief Allocate a block of memory at the specified virtual address + */ +tPAddr MM_Allocate(tVAddr VAddr) +{ + tPAddr ret; + + ENTER("xVAddr", VAddr); + + // Ensure the tables are allocated before the page (keeps things neat) + MM_GetPageEntryPtr(VAddr, 0, 1, 0, NULL); + + // Allocate the page + ret = MM_AllocPhys(); + LOG("ret = %x", ret); + if(!ret) LEAVE_RET('i', 0); + + if( !MM_Map(VAddr, ret) ) + { + Warning("MM_Allocate: Unable to map. Strange, we should have errored earlier"); + MM_DerefPhys(ret); + LEAVE('i'); + return 0; + } + + LEAVE('X', ret); + return ret; +} + +tPAddr MM_AllocateZero(tVAddr VAddr) +{ + tPAddr ret = gMM_ZeroPage; + + MM_GetPageEntryPtr(VAddr, 0, 1, 0, NULL); + + if(!gMM_ZeroPage) { + ret = gMM_ZeroPage = MM_AllocPhys(); + MM_RefPhys(ret); // Don't free this please + MM_Map(VAddr, ret); + memset((void*)VAddr, 0, 0x1000); + } + else { + MM_Map(VAddr, ret); + } + MM_RefPhys(ret); // Refernce for this map + MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW); + return ret; +} + +/** + * \brief Deallocate a page at a virtual address + */ +void MM_Deallocate(tVAddr VAddr) +{ + tPAddr phys; + + phys = MM_GetPhysAddr(VAddr); + if(!phys) return ; + + MM_Unmap(VAddr); + + MM_DerefPhys(phys); +} + +/** + * \brief Get the page table entry of a virtual address + * \param Addr Virtual Address + * \param Phys Location to put the physical address + * \param Flags Flags on the entry (set to zero if unmapped) + * \return Size of the entry (in address bits) - 12 = 4KiB page + */ +int MM_GetPageEntry(tVAddr Addr, tPAddr *Phys, Uint *Flags) +{ + tPAddr *ptr; + int ret; + + if(!Phys || !Flags) return 0; + + ret = MM_GetPageEntryPtr(Addr, 0, 0, 0, &ptr); + if( ret < 0 ) return 0; + + *Phys = *ptr & PADDR_MASK; + *Flags = *ptr & 0xFFF; + return ret; +} + +/** + * \brief Get the physical address of a virtual location + */ +tPAddr MM_GetPhysAddr(tVAddr Addr) +{ + tPAddr *ptr; + int ret; + + ret = MM_GetPageEntryPtr(Addr, 0, 0, 0, &ptr); + if( ret < 0 ) return 0; + + if( !(*ptr & 1) ) return 0; + + return (*ptr & PADDR_MASK) | (Addr & 0xFFF); +} + +/** + * \brief Sets the flags on a page + */ +void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask) +{ + tPAddr *ent; + int rv; + + // Get pointer + rv = MM_GetPageEntryPtr(VAddr, 0, 0, 0, &ent); + if(rv < 0) return ; + + // Ensure the entry is valid + if( !(*ent & 1) ) return ; + + // Read-Only + if( Mask & MM_PFLAG_RO ) + { + if( Flags & MM_PFLAG_RO ) { + *ent &= ~PF_WRITE; + } + else { + *ent |= PF_WRITE; + } + } + + // Kernel + if( Mask & MM_PFLAG_KERNEL ) + { + if( Flags & MM_PFLAG_KERNEL ) { + *ent &= ~PF_USER; + } + else { + *ent |= PF_USER; + } + } + + // Copy-On-Write + if( Mask & MM_PFLAG_COW ) + { + if( Flags & MM_PFLAG_COW ) { + *ent &= ~PF_WRITE; + *ent |= PF_COW; + INVLPG_ALL(); + } + else { + *ent &= ~PF_COW; + *ent |= PF_WRITE; + } + } + + // Execute + if( Mask & MM_PFLAG_EXEC ) + { + if( Flags & MM_PFLAG_EXEC ) { + *ent &= ~PF_NX; + } + else { + *ent |= PF_NX; + } + } +} + +/** + * \brief Get the flags applied to a page + */ +Uint MM_GetFlags(tVAddr VAddr) +{ + tPAddr *ent; + int rv, ret = 0; + + rv = MM_GetPageEntryPtr(VAddr, 0, 0, 0, &ent); + if(rv < 0) return 0; + + if( !(*ent & 1) ) return 0; + + // Read-Only + if( !(*ent & PF_WRITE) ) ret |= MM_PFLAG_RO; + // Kernel + if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL; + // Copy-On-Write + if( *ent & PF_COW ) ret |= MM_PFLAG_COW; + // Execute + if( !(*ent & PF_NX) ) ret |= MM_PFLAG_EXEC; + + return ret; +} + +/** + * \brief Check if the provided buffer is valid + * \return Boolean valid + */ +int MM_IsValidBuffer(tVAddr Addr, size_t Size) +{ + int bIsUser; + Uint64 pml4, pdp, dir, tab; + + Size += Addr & (PAGE_SIZE-1); + Addr &= ~(PAGE_SIZE-1); + Addr &= ((1UL << 48)-1); // Clap to address space + + pml4 = Addr >> 39; + pdp = Addr >> 30; + dir = Addr >> 21; + tab = Addr >> 12; + + if( !(PAGEMAPLVL4(pml4) & 1) ) return 0; + if( !(PAGEDIRPTR(pdp) & 1) ) return 0; + if( !(PAGEDIR(dir) & 1) ) return 0; + if( !(PAGETABLE(tab) & 1) ) return 0; + + bIsUser = !!(PAGETABLE(tab) & PF_USER); + + while( Size >= PAGE_SIZE ) + { + if( (tab & 511) == 0 ) + { + dir ++; + if( ((dir >> 9) & 511) == 0 ) + { + pdp ++; + if( ((pdp >> 18) & 511) == 0 ) + { + pml4 ++; + if( !(PAGEMAPLVL4(pml4) & 1) ) return 0; + } + if( !(PAGEDIRPTR(pdp) & 1) ) return 0; + } + if( !(PAGEDIR(dir) & 1) ) return 0; + } + + if( !(PAGETABLE(tab) & 1) ) return 0; + if( bIsUser && !(PAGETABLE(tab) & PF_USER) ) return 0; + + tab ++; + Size -= PAGE_SIZE; + } + return 1; +} + +// --- Hardware Mappings --- +/** + * \brief Map a range of hardware pages + */ +tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number) +{ + tVAddr ret; + int num; + + //TODO: Add speedups (memory of first possible free) + for( ret = MM_HWMAP_BASE; ret < MM_HWMAP_TOP; ret += 0x1000 ) + { + for( num = Number; num -- && ret < MM_HWMAP_TOP; ret += 0x1000 ) + { + if( MM_GetPhysAddr(ret) != 0 ) break; + } + if( num >= 0 ) continue; + +// Log_Debug("MMVirt", "Mapping %i pages to %p (base %P)", Number, ret-Number*0x1000, PAddr); + + PAddr += 0x1000 * Number; + + while( Number -- ) + { + ret -= 0x1000; + PAddr -= 0x1000; + MM_Map(ret, PAddr); + MM_RefPhys(PAddr); + } + + return ret; + } + + Log_Error("MM", "MM_MapHWPages - No space for %i pages", Number); + return 0; +} + +/** + * \brief Free a range of hardware pages + */ +void MM_UnmapHWPages(tVAddr VAddr, Uint Number) +{ +// Log_KernelPanic("MM", "TODO: Implement MM_UnmapHWPages"); + while( Number -- ) + { + MM_DerefPhys( MM_GetPhysAddr(VAddr) ); + MM_Unmap(VAddr); + VAddr += 0x1000; + } +} + + +/** + * \fn tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr) + * \brief Allocates DMA physical memory + * \param Pages Number of pages required + * \param MaxBits Maximum number of bits the physical address can have + * \param PhysAddr Pointer to the location to place the physical address allocated + * \return Virtual address allocate + */ +tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr) +{ + tPAddr phys; + tVAddr ret; + + // Sanity Check + if(MaxBits < 12 || !PhysAddr) return 0; + + // Fast Allocate + if(Pages == 1 && MaxBits >= PHYS_BITS) + { + phys = MM_AllocPhys(); + *PhysAddr = phys; + ret = MM_MapHWPages(phys, 1); + MM_DerefPhys(phys); + return ret; + } + + // Slow Allocate + phys = MM_AllocPhysRange(Pages, MaxBits); + // - Was it allocated? + if(phys == 0) return 0; + + // Allocated successfully, now map + ret = MM_MapHWPages(phys, Pages); + // MapHWPages references the pages, so deref them back down to 1 + for(;Pages--;phys+=0x1000) + MM_DerefPhys(phys); + if( ret == 0 ) { + // If it didn't map, free then return 0 + return 0; + } + + *PhysAddr = phys; + return ret; +} + +// --- Tempory Mappings --- +tVAddr MM_MapTemp(tPAddr PAddr) +{ + const int max_slots = (MM_TMPMAP_END - MM_TMPMAP_BASE) / PAGE_SIZE; + tVAddr ret = MM_TMPMAP_BASE; + int i; + + for( i = 0; i < max_slots; i ++, ret += PAGE_SIZE ) + { + tPAddr *ent; + if( MM_GetPageEntryPtr( ret, 0, 1, 0, &ent) < 0 ) { + continue ; + } + + if( *ent & 1 ) + continue ; + + *ent = PAddr | 3; + MM_RefPhys(PAddr); + INVLPG(ret); + return ret; + } + return 0; +} + +void MM_FreeTemp(tVAddr VAddr) +{ + MM_Deallocate(VAddr); + return ; +} + + +// --- Address Space Clone -- +tPAddr MM_Clone(void) +{ + tPAddr ret; + int i; + tVAddr kstackbase; + + // #1 Create a copy of the PML4 + ret = MM_AllocPhys(); + if(!ret) return 0; + + // #2 Alter the fractal pointer + Mutex_Acquire(&glMM_TempFractalLock); + TMPCR3() = ret | 3; + INVLPG_ALL(); + + // #3 Set Copy-On-Write to all user pages + if( Threads_GetPID() != 0 ) + { + for( i = 0; i < 256; i ++) + { + if( PAGEMAPLVL4(i) & PF_WRITE ) { + PAGEMAPLVL4(i) |= PF_COW; + PAGEMAPLVL4(i) &= ~PF_WRITE; + } + + TMPMAPLVL4(i) = PAGEMAPLVL4(i); +// Log_Debug("MM", "TMPMAPLVL4(%i) = 0x%016llx", i, TMPMAPLVL4(i)); + if( !(TMPMAPLVL4(i) & PF_PRESENT) ) continue ; + + MM_RefPhys( TMPMAPLVL4(i) & PADDR_MASK ); + } + } + else + { + for( i = 0; i < 256; i ++ ) + { + TMPMAPLVL4(i) = 0; + } + } + + // #4 Map in kernel pages + for( i = 256; i < 512; i ++ ) + { + // Skip addresses: + // 320 0xFFFFA.... - Kernel Stacks + if( i == MM_KSTACK_BASE>>39 ) continue; + // 509 0xFFFFFE0.. - Fractal mapping + if( i == MM_FRACTAL_BASE>>39 ) continue; + // 510 0xFFFFFE8.. - Temp fractal mapping + if( i == MM_TMPFRAC_BASE>>39 ) continue; + + TMPMAPLVL4(i) = PAGEMAPLVL4(i); + if( TMPMAPLVL4(i) & 1 ) + MM_RefPhys( TMPMAPLVL4(i) & PADDR_MASK ); + } + + // Mark Per-Process data as COW + TMPMAPLVL4(MM_PPD_BASE>>39) |= PF_COW; + TMPMAPLVL4(MM_PPD_BASE>>39) &= ~PF_WRITE; + + // #5 Set fractal mapping + TMPMAPLVL4(MM_FRACTAL_BASE>>39) = ret | 3; // Main + TMPMAPLVL4(MM_TMPFRAC_BASE>>39) = 0; // Temp + + // #6 Create kernel stack + // tThread->KernelStack is the top + // There is 1 guard page below the stack + kstackbase = Proc_GetCurThread()->KernelStack - KERNEL_STACK_SIZE; + + // Clone stack + TMPMAPLVL4(MM_KSTACK_BASE >> PML4_SHIFT) = 0; + for( i = 1; i < KERNEL_STACK_SIZE/0x1000; i ++ ) + { + tPAddr phys = MM_AllocPhys(); + tVAddr tmpmapping; + MM_MapEx(kstackbase+i*0x1000, phys, 1, 0); + + tmpmapping = MM_MapTemp(phys); + if( MM_GetPhysAddr( kstackbase+i*0x1000 ) ) + memcpy((void*)tmpmapping, (void*)(kstackbase+i*0x1000), 0x1000); + else + memset((void*)tmpmapping, 0, 0x1000); +// if( i == 0xF ) +// Debug_HexDump("MM_Clone: *tmpmapping = ", (void*)tmpmapping, 0x1000); + MM_FreeTemp(tmpmapping); + } + +// MAGIC_BREAK(); + + // #7 Return + TMPCR3() = 0; + INVLPG_ALL(); + Mutex_Release(&glMM_TempFractalLock); +// Log("MM_Clone: RETURN %P", ret); + return ret; +} + +void MM_int_ClearTableLevel(tVAddr VAddr, int LevelBits, int MaxEnts) +{ + Uint64 * const table_bases[] = {&PAGETABLE(0), &PAGEDIR(0), &PAGEDIRPTR(0), &PAGEMAPLVL4(0)}; + Uint64 *table = table_bases[(LevelBits-12)/9] + (VAddr >> LevelBits); + int i; +// Log("MM_int_ClearTableLevel: (VAddr=%p, LevelBits=%i, MaxEnts=%i)", VAddr, LevelBits, MaxEnts); + for( i = 0; i < MaxEnts; i ++ ) + { + // Skip non-present tables + if( !(table[i] & PF_PRESENT) ) { + table[i] = 0; + continue ; + } + + if( (table[i] & PF_COW) && MM_GetRefCount(table[i] & PADDR_MASK) > 1 ) { + MM_DerefPhys(table[i] & PADDR_MASK); + table[i] = 0; + continue ; + } + // Clear table contents (if it is a table) + if( LevelBits > 12 ) + MM_int_ClearTableLevel(VAddr + ((tVAddr)i << LevelBits), LevelBits-9, 512); + MM_DerefPhys(table[i] & PADDR_MASK); + table[i] = 0; + } +} + +void MM_ClearUser(void) +{ + MM_int_ClearTableLevel(0, 39, 256); +} + +tVAddr MM_NewWorkerStack(void *StackData, size_t StackSize) +{ + tVAddr ret; + tPAddr phys; + int i; + + // #1 Set temp fractal to PID0 + Mutex_Acquire(&glMM_TempFractalLock); + TMPCR3() = ((tPAddr)gInitialPML4 - KERNEL_BASE) | 3; + INVLPG_ALL(); + + // #2 Scan for a free stack addresss < 2^47 + for(ret = 0x100000; ret < (1ULL << 47); ret += KERNEL_STACK_SIZE) + { + tPAddr *ptr; + if( MM_GetPageEntryPtr(ret, 1, 0, 0, &ptr) <= 0 ) break; + if( !(*ptr & 1) ) break; + } + if( ret >= (1ULL << 47) ) { + Mutex_Release(&glMM_TempFractalLock); + return 0; + } + + // #3 Map all save the last page in the range + // - This acts as as guard page + MM_GetPageEntryPtr(ret, 1, 1, 0, NULL); // Make sure tree is allocated + for( i = 0; i < KERNEL_STACK_SIZE/0x1000 - 1; i ++ ) + { + phys = MM_AllocPhys(); + if(!phys) { + // TODO: Clean up + Log_Error("MM", "MM_NewWorkerStack - Unable to allocate page"); + return 0; + } + MM_MapEx(ret + i*0x1000, phys, 1, 0); + MM_SetFlags(ret + i*0x1000, MM_PFLAG_KERNEL|MM_PFLAG_RO, MM_PFLAG_KERNEL); + } + + // Copy data + if( StackSize > 0x1000 ) { + Log_Error("MM", "MM_NewWorkerStack: StackSize(0x%x) > 0x1000, cbf handling", StackSize); + } + else { + tVAddr tmp_addr, dest; + tmp_addr = MM_MapTemp(phys); + dest = tmp_addr + (0x1000 - StackSize); + memcpy( (void*)dest, StackData, StackSize ); + Log_Debug("MM", "MM_NewWorkerStack: %p->%p %i bytes (i=%i)", StackData, dest, StackSize, i); + Log_Debug("MM", "MM_NewWorkerStack: ret = %p", ret); + MM_FreeTemp(tmp_addr); + } + + TMPCR3() = 0; + Mutex_Release(&glMM_TempFractalLock); + + return ret + i*0x1000; +} + +/** + * \brief Allocate a new kernel stack + */ +tVAddr MM_NewKStack(void) +{ + tVAddr base = MM_KSTACK_BASE; + Uint i; + for( ; base < MM_KSTACK_TOP; base += KERNEL_STACK_SIZE ) + { + if(MM_GetPhysAddr(base+KERNEL_STACK_SIZE-0x1000) != 0) + continue; + + //Log("MM_NewKStack: Found one at %p", base + KERNEL_STACK_SIZE); + for( i = 0x1000; i < KERNEL_STACK_SIZE; i += 0x1000) + { + if( !MM_Allocate(base+i) ) + { + Log_Warning("MM", "MM_NewKStack - Allocation failed"); + for( i -= 0x1000; i; i -= 0x1000) + MM_Deallocate(base+i); + return 0; + } + } + + return base + KERNEL_STACK_SIZE; + } + Log_Warning("MM", "MM_NewKStack - No address space left\n"); + return 0; +} diff --git a/KernelLand/Kernel/arch/x86_64/pci.c b/KernelLand/Kernel/arch/x86_64/pci.c new file mode 120000 index 00000000..cac29a8f --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/pci.c @@ -0,0 +1 @@ +../x86/pci.c \ No newline at end of file diff --git a/KernelLand/Kernel/arch/x86_64/proc.asm b/KernelLand/Kernel/arch/x86_64/proc.asm new file mode 100644 index 00000000..f540b3b6 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/proc.asm @@ -0,0 +1,148 @@ +; +; +; +%include "arch/x86_64/include/common.inc.asm" +[BITS 64] +[section .text] + +[extern Threads_Exit] + +[global GetRIP] +GetRIP: + mov rax, [rsp] + ret + +[global NewTaskHeader] +NewTaskHeader: + ; [rsp+0x00]: Thread + ; [rsp+0x08]: Function + ; [rsp+0x10]: Argument + + mov rdi, [rsp+0x10] + mov rax, [rsp+0x8] + add rsp, 0x10 ; Reclaim stack space (thread/fcn) + xchg bx, bx + call rax + + ; Quit thread with RAX as the return code + xor rdi, rdi + mov rsi, rax + call Threads_Exit + +.hlt: + jmp .hlt + +[extern MM_Clone] +[extern MM_DumpTables] +[global Proc_CloneInt] +Proc_CloneInt: + PUSH_GPR + ; Save RSP + mov [rdi], rsp + call MM_Clone + ; Save CR3 + mov rsi, [rsp+0x30] ; Saved version of RSI + mov [rsi], rax + ; Undo the PUSH_GPR + add rsp, 0x80 + mov rax, .newTask + ret +.newTask: +; mov rdi, 0 +; mov rsi, 0x800000000000 +; call MM_DumpTables + POP_GPR + xor eax, eax + ret + +[global SaveState] +SaveState: + ; Save regs to RSI + add rsi, 0x80 + SAVE_GPR rsi + ; Save return addr + mov rax, [rsp] + mov [rsi], rax + ; Return RSI as the RSP value + sub rsi, 0x80 + mov [rdi], rsi + ; Check for + mov rax, .restore + ret +.restore: + ; RSP = RSI now + POP_GPR + mov rax, [rsp] + mov rsp, [rsp-0x60] ; Restore RSP from the saved value + mov [rsp], rax ; Restore return address + xor eax, eax + ret + +[global SwitchTasks] +; rdi = New RSP +; rsi = Old RSP save loc +; rdx = New RIP +; rcx = Old RIP save loc +; r8 = CR3 +SwitchTasks: + PUSH_GPR + + ; Save state RIP and RSP + lea rax, [rel .restore] + mov [rcx], rax + mov [rsi], rsp + + ; Change CR3 if requested + test r8, r8 + jz .setState + mov cr3, r8 + + ; Make sure the stack is valid before jumping + invlpg [rdi-0x1000] + invlpg [rdi] + invlpg [rdi+0x1000] + + ; Go to new state +.setState: + mov rsp, rdi + jmp rdx + + ; Restore point for saved state +.restore: + POP_GPR + xor eax, eax ; Return zero + ret + +[global Proc_InitialiseSSE] +Proc_InitialiseSSE: + mov rax, cr4 + or ax, (1 << 9)|(1 << 10) ; Set OSFXSR and OSXMMEXCPT + mov cr4, rax + mov rax, cr0 + and ax, ~(1 << 2) ; Clear EM + or rax, (1 << 1) ; Set MP + mov rax, cr0 + ret +[global Proc_DisableSSE] +Proc_DisableSSE: + mov rax, cr0 + or ax, 1 << 3 ; Set TS + mov cr0, rax + ret +[global Proc_EnableSSE] +Proc_EnableSSE: + mov rax, cr0 + and ax, ~(1 << 3) ; Clear TS + mov cr0, rax + ret + +[global Proc_SaveSSE] +Proc_SaveSSE: + fxsave [rdi] + ret +[global Proc_RestoreSSE] +Proc_RestoreSSE: + fxrstor [rdi] + ret + +; vim: ft=nasm diff --git a/KernelLand/Kernel/arch/x86_64/proc.c b/KernelLand/Kernel/arch/x86_64/proc.c new file mode 100644 index 00000000..5f71a45f --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/proc.c @@ -0,0 +1,827 @@ +/* + * Acess2 x86_64 port + * proc.c + */ +#include +#include +#include +#include +#include +#include +#include +#if USE_MP +# include +#endif +#include +#include + +// === FLAGS === +#define DEBUG_TRACE_SWITCH 0 +#define BREAK_ON_SWITCH 0 // Break into bochs debugger on a task switch + +// === CONSTANTS === + +// === TYPES === +typedef struct sCPU +{ + Uint8 APICID; + Uint8 State; // 0: Unavaliable, 1: Idle, 2: Active + Uint16 Resvd; + tThread *Current; + tThread *IdleThread; +} tCPU; + +// === IMPORTS === +extern tGDT gGDT[]; +extern void APStartup(void); // 16-bit AP startup code + +extern Uint GetRIP(void); // start.asm +extern Uint SaveState(Uint *RSP, Uint *Regs); +extern Uint Proc_CloneInt(Uint *RSP, Uint *CR3); +extern void NewTaskHeader(void); // Actually takes cdecl args +extern void Proc_InitialiseSSE(void); +extern void Proc_SaveSSE(Uint DestPtr); +extern void Proc_DisableSSE(void); + +extern Uint64 gInitialPML4[512]; // start.asm +extern int giNumCPUs; +extern int giNextTID; +extern int giTotalTickets; +extern int giNumActiveThreads; +extern tThread gThreadZero; +extern tProcess gProcessZero; +extern void Threads_Dump(void); +extern void Proc_ReturnToUser(tVAddr Handler, tVAddr KStackTop, int Argument); +extern void Time_UpdateTimestamp(void); +extern void SwitchTasks(Uint NewSP, Uint *OldSP, Uint NewIP, Uint *OldIO, Uint CR3); + +// === PROTOTYPES === +//void ArchThreads_Init(void); +#if USE_MP +void MP_StartAP(int CPU); +void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode); +#endif +void Proc_IdleTask(void *unused); +//void Proc_Start(void); +//tThread *Proc_GetCurThread(void); +// int Proc_NewKThread(void (*Fcn)(void*), void *Data); +// int Proc_Clone(Uint *Err, Uint Flags); +// int Proc_SpawnWorker(void); +Uint Proc_MakeUserStack(void); +//void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize); +void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) NORETURN; + int Proc_Demote(Uint *Err, int Dest, tRegs *Regs); +//void Proc_CallFaultHandler(tThread *Thread); +//void Proc_DumpThreadCPUState(tThread *Thread); +//void Proc_Reschedule(void); +void Proc_Scheduler(int CPU, Uint RSP, Uint RIP); + +// === GLOBALS === +//!\brief Used by desctab.asm in SyscallStub +const int ci_offsetof_tThread_KernelStack = offsetof(tThread, KernelStack); +// --- Multiprocessing --- +#if USE_MP +volatile int giNumInitingCPUs = 0; +tMPInfo *gMPFloatPtr = NULL; +tAPIC *gpMP_LocalAPIC = NULL; +Uint8 gaAPIC_to_CPU[256] = {0}; +#endif +tCPU gaCPUs[MAX_CPUS]; +tTSS *gTSSs = NULL; +tTSS gTSS0 = {0}; +// --- Error Recovery --- +Uint32 gaDoubleFaultStack[1024]; + +// === CODE === +/** + * \fn void ArchThreads_Init(void) + * \brief Starts the process scheduler + */ +void ArchThreads_Init(void) +{ + Uint pos = 0; + + #if USE_MP + tMPTable *mptable; + + // Mark BSP as active + gaCPUs[0].State = 2; + + // -- Initialise Multiprocessing + // Find MP Floating Table + // - EBDA/Last 1Kib (640KiB) + for(pos = KERNEL_BASE|0x9F000; pos < (KERNEL_BASE|0xA0000); pos += 16) { + if( *(Uint*)(pos) == MPPTR_IDENT ) { + Log("Possible %p", pos); + if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue; + gMPFloatPtr = (void*)pos; + break; + } + } + // - Last KiB (512KiB base mem) + if(!gMPFloatPtr) { + for(pos = KERNEL_BASE|0x7F000; pos < (KERNEL_BASE|0x80000); pos += 16) { + if( *(Uint*)(pos) == MPPTR_IDENT ) { + Log("Possible %p", pos); + if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue; + gMPFloatPtr = (void*)pos; + break; + } + } + } + // - BIOS ROM + if(!gMPFloatPtr) { + for(pos = KERNEL_BASE|0xE0000; pos < (KERNEL_BASE|0x100000); pos += 16) { + if( *(Uint*)(pos) == MPPTR_IDENT ) { + Log("Possible %p", pos); + if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue; + gMPFloatPtr = (void*)pos; + break; + } + } + } + + // If the MP Table Exists, parse it + if(gMPFloatPtr) + { + int i; + tMPTable_Ent *ents; + Log("gMPFloatPtr = %p", gMPFloatPtr); + Log("*gMPFloatPtr = {"); + Log("\t.Sig = 0x%08x", gMPFloatPtr->Sig); + Log("\t.MPConfig = 0x%08x", gMPFloatPtr->MPConfig); + Log("\t.Length = 0x%02x", gMPFloatPtr->Length); + Log("\t.Version = 0x%02x", gMPFloatPtr->Version); + Log("\t.Checksum = 0x%02x", gMPFloatPtr->Checksum); + Log("\t.Features = [0x%02x,0x%02x,0x%02x,0x%02x,0x%02x]", + gMPFloatPtr->Features[0], gMPFloatPtr->Features[1], + gMPFloatPtr->Features[2], gMPFloatPtr->Features[3], + gMPFloatPtr->Features[4] + ); + Log("}"); + + mptable = (void*)( KERNEL_BASE|gMPFloatPtr->MPConfig ); + Log("mptable = %p", mptable); + Log("*mptable = {"); + Log("\t.Sig = 0x%08x", mptable->Sig); + Log("\t.BaseTableLength = 0x%04x", mptable->BaseTableLength); + Log("\t.SpecRev = 0x%02x", mptable->SpecRev); + Log("\t.Checksum = 0x%02x", mptable->Checksum); + Log("\t.OEMID = '%8c'", mptable->OemID); + Log("\t.ProductID = '%8c'", mptable->ProductID); + Log("\t.OEMTablePtr = %p'", mptable->OEMTablePtr); + Log("\t.OEMTableSize = 0x%04x", mptable->OEMTableSize); + Log("\t.EntryCount = 0x%04x", mptable->EntryCount); + Log("\t.LocalAPICMemMap = 0x%08x", mptable->LocalAPICMemMap); + Log("\t.ExtendedTableLen = 0x%04x", mptable->ExtendedTableLen); + Log("\t.ExtendedTableChecksum = 0x%02x", mptable->ExtendedTableChecksum); + Log("}"); + + gpMP_LocalAPIC = (void*)MM_MapHWPage(mptable->LocalAPICMemMap, 1); + + ents = mptable->Entries; + giNumCPUs = 0; + + for( i = 0; i < mptable->EntryCount; i ++ ) + { + int entSize = 0; + switch( ents->Type ) + { + case 0: // Processor + entSize = 20; + Log("%i: Processor", i); + Log("\t.APICID = %i", ents->Proc.APICID); + Log("\t.APICVer = 0x%02x", ents->Proc.APICVer); + Log("\t.CPUFlags = 0x%02x", ents->Proc.CPUFlags); + Log("\t.CPUSignature = 0x%08x", ents->Proc.CPUSignature); + Log("\t.FeatureFlags = 0x%08x", ents->Proc.FeatureFlags); + + + if( !(ents->Proc.CPUFlags & 1) ) { + Log("DISABLED"); + break; + } + + // Check if there is too many processors + if(giNumCPUs >= MAX_CPUS) { + giNumCPUs ++; // If `giNumCPUs` > MAX_CPUS later, it will be clipped + break; + } + + // Initialise CPU Info + gaAPIC_to_CPU[ents->Proc.APICID] = giNumCPUs; + gaCPUs[giNumCPUs].APICID = ents->Proc.APICID; + gaCPUs[giNumCPUs].State = 0; + giNumCPUs ++; + + // Send IPI + if( !(ents->Proc.CPUFlags & 2) ) + { + MP_StartAP( giNumCPUs-1 ); + } + + break; + case 1: // Bus + entSize = 8; + Log("%i: Bus", i); + Log("\t.ID = %i", ents->Bus.ID); + Log("\t.TypeString = '%6c'", ents->Bus.TypeString); + break; + case 2: // I/O APIC + entSize = 8; + Log("%i: I/O APIC", i); + Log("\t.ID = %i", ents->IOAPIC.ID); + Log("\t.Version = 0x%02x", ents->IOAPIC.Version); + Log("\t.Flags = 0x%02x", ents->IOAPIC.Flags); + Log("\t.Addr = 0x%08x", ents->IOAPIC.Addr); + break; + case 3: // I/O Interrupt Assignment + entSize = 8; + Log("%i: I/O Interrupt Assignment", i); + Log("\t.IntType = %i", ents->IOInt.IntType); + Log("\t.Flags = 0x%04x", ents->IOInt.Flags); + Log("\t.SourceBusID = 0x%02x", ents->IOInt.SourceBusID); + Log("\t.SourceBusIRQ = 0x%02x", ents->IOInt.SourceBusIRQ); + Log("\t.DestAPICID = 0x%02x", ents->IOInt.DestAPICID); + Log("\t.DestAPICIRQ = 0x%02x", ents->IOInt.DestAPICIRQ); + break; + case 4: // Local Interrupt Assignment + entSize = 8; + Log("%i: Local Interrupt Assignment", i); + Log("\t.IntType = %i", ents->LocalInt.IntType); + Log("\t.Flags = 0x%04x", ents->LocalInt.Flags); + Log("\t.SourceBusID = 0x%02x", ents->LocalInt.SourceBusID); + Log("\t.SourceBusIRQ = 0x%02x", ents->LocalInt.SourceBusIRQ); + Log("\t.DestLocalAPICID = 0x%02x", ents->LocalInt.DestLocalAPICID); + Log("\t.DestLocalAPICIRQ = 0x%02x", ents->LocalInt.DestLocalAPICIRQ); + break; + default: + Log("%i: Unknown (%i)", i, ents->Type); + break; + } + ents = (void*)( (Uint)ents + entSize ); + } + + if( giNumCPUs > MAX_CPUS ) { + Warning("Too many CPUs detected (%i), only using %i of them", giNumCPUs, MAX_CPUS); + giNumCPUs = MAX_CPUS; + } + + while( giNumInitingCPUs ) + MM_FinishVirtualInit(); + + Panic("Uh oh... MP Table Parsing is unimplemented\n"); + } + else { + Log("No MP Table was found, assuming uniprocessor\n"); + giNumCPUs = 1; + gTSSs = &gTSS0; + } + #else + giNumCPUs = 1; + gTSSs = &gTSS0; + MM_FinishVirtualInit(); + #endif + + #if USE_MP + // Initialise Normal TSS(s) + for(pos=0;pos> 16; + gGDT[7+pos*2].BaseHi = ((Uint)(&gTSSs[pos])) >> 24; + gGDT[7+pos*2+1].DWord[0] = ((Uint)(&gTSSs[pos])) >> 32; + #if USE_MP + } + for(pos=0;pos>8)&0xFF); // High Byte + + // Create Per-Process Data Block + if( !MM_Allocate(MM_PPD_CFG) ) + { + Warning("Oh, hell, Unable to allocate PPD for Thread#0"); + } + + Proc_InitialiseSSE(); + + Log_Log("Proc", "Multithreading initialised"); +} + +#if USE_MP +void MP_StartAP(int CPU) +{ + Log("Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID); + // Set location of AP startup code and mark for a warm restart + *(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APStartup - (KERNEL_BASE|0xFFFF0); + *(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF; + outb(0x70, 0x0F); outb(0x71, 0x0A); // Warm Reset + MP_SendIPI(gaCPUs[CPU].APICID, 0, 5); + giNumInitingCPUs ++; +} + +void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode) +{ + Uint32 addr = (Uint)gpMP_LocalAPIC + 0x300; + Uint32 val; + + // High + val = (Uint)APICID << 24; + Log("*%p = 0x%08x", addr+0x10, val); + *(Uint32*)(addr+0x10) = val; + // Low (and send) + val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF); + Log("*%p = 0x%08x", addr, val); + *(Uint32*)addr = val; +} +#endif + +/** + * \brief Idle task + */ +void Proc_IdleTask(void *ptr) +{ + tCPU *cpu = ptr; + cpu->IdleThread = Proc_GetCurThread(); + cpu->IdleThread->ThreadName = (char*)"Idle Thread"; + Threads_SetPriority( cpu->IdleThread, -1 ); // Never called randomly + cpu->IdleThread->Quantum = 1; // 1 slice quantum + for(;;) { + HALT(); // Just yeilds + Threads_Yield(); + } +} + +/** + * \fn void Proc_Start(void) + * \brief Start process scheduler + */ +void Proc_Start(void) +{ + #if USE_MP + int i; + #endif + + #if USE_MP + // Start APs + for( i = 0; i < giNumCPUs; i ++ ) + { + int tid; + if(i) gaCPUs[i].Current = NULL; + + Proc_NewKThread(Proc_IdleTask, &gaCPUs[i]); + + // Create Idle Task + gaCPUs[i].IdleThread = Threads_GetThread(tid); + + + // Start the AP + if( i != giProc_BootProcessorID ) { + MP_StartAP( i ); + } + } + + // BSP still should run the current task + gaCPUs[0].Current = &gThreadZero; + __asm__ __volatile__ ("mov %0, %%db0" : : "r"(&gThreadZero)); + + // Start interrupts and wait for APs to come up + Log("Waiting for APs to come up\n"); + __asm__ __volatile__ ("sti"); + while( giNumInitingCPUs ) __asm__ __volatile__ ("hlt"); + #else + Proc_NewKThread(Proc_IdleTask, &gaCPUs[0]); + + // Start Interrupts (and hence scheduler) + __asm__ __volatile__("sti"); + #endif + MM_FinishVirtualInit(); + Log_Log("Proc", "Multithreading started"); +} + +/** + * \fn tThread *Proc_GetCurThread(void) + * \brief Gets the current thread + */ +tThread *Proc_GetCurThread(void) +{ + #if USE_MP + tThread *ret; + __asm__ __volatile__ ("mov %%db0, %0" : "=r"(thread)); + return ret; // gaCPUs[ GetCPUNum() ].Current; + #else + return gaCPUs[ 0 ].Current; + #endif +} + +void Proc_ClearProcess(tProcess *Process) +{ + Log_Warning("Proc", "TODO: Nuke address space etc"); +} + +/* + * + */ +void Proc_ClearThread(tThread *Thread) +{ +} + +/** + * \brief Create a new kernel thread + */ +tTID Proc_NewKThread(void (*Fcn)(void*), void *Data) +{ + Uint rsp; + tThread *newThread, *cur; + + cur = Proc_GetCurThread(); + newThread = Threads_CloneTCB(0); + if(!newThread) return -1; + + // Create new KStack + newThread->KernelStack = MM_NewKStack(); + // Check for errors + if(newThread->KernelStack == 0) { + free(newThread); + return -1; + } + + rsp = newThread->KernelStack; + *(Uint*)(rsp-=8) = (Uint)Data; // Data (shadowed) + *(Uint*)(rsp-=8) = (Uint)Fcn; // Function to call + *(Uint*)(rsp-=8) = (Uint)newThread; // Thread ID + + newThread->SavedState.RSP = rsp; + newThread->SavedState.RIP = (Uint)&NewTaskHeader; + newThread->SavedState.SSE = NULL; +// Log("New (KThread) %p, rsp = %p\n", newThread->SavedState.RIP, newThread->SavedState.RSP); + +// MAGIC_BREAK(); + Threads_AddActive(newThread); + + return newThread->TID; +} + +/** + * \fn int Proc_Clone(Uint Flags) + * \brief Clone the current process + */ +tTID Proc_Clone(Uint Flags) +{ + tThread *newThread, *cur = Proc_GetCurThread(); + Uint rip; + + // Sanity check + if( !(Flags & CLONE_VM) ) { + Log_Error("Proc", "Proc_Clone: Don't leave CLONE_VM unset, use Proc_NewKThread instead"); + return -1; + } + + // Create new TCB + newThread = Threads_CloneTCB(Flags); + if(!newThread) return -1; + + // Save core machine state + rip = Proc_CloneInt(&newThread->SavedState.RSP, &newThread->Process->MemState.CR3); + if(rip == 0) return 0; // Child + newThread->KernelStack = cur->KernelStack; + newThread->SavedState.RIP = rip; + newThread->SavedState.SSE = NULL; + + // DEBUG + #if 0 + Log("New (Clone) %p, rsp = %p, cr3 = %p", rip, newThread->SavedState.RSP, newThread->MemState.CR3); + { + Uint cr3; + __asm__ __volatile__ ("mov %%cr3, %0" : "=r" (cr3)); + Log("Current CR3 = 0x%x, PADDR(RSP) = 0x%x", cr3, MM_GetPhysAddr(newThread->SavedState.RSP)); + } + #endif + // /DEBUG + + // Lock list and add to active + Threads_AddActive(newThread); + + return newThread->TID; +} + +/** + * \fn int Proc_SpawnWorker(void) + * \brief Spawns a new worker thread + */ +int Proc_SpawnWorker(void (*Fcn)(void*), void *Data) +{ + tThread *new, *cur; + Uint stack_contents[3]; + + cur = Proc_GetCurThread(); + + // Create new thread + new = Threads_CloneThreadZero(); + if(!new) { + Warning("Proc_SpawnWorker - Out of heap space!\n"); + return -1; + } + + // Create the stack contents + stack_contents[2] = (Uint)Data; + stack_contents[1] = (Uint)Fcn; + stack_contents[0] = (Uint)new; + + // Create a new worker stack (in PID0's address space) + // - The stack is built by this code using stack_contents + new->KernelStack = MM_NewWorkerStack(stack_contents, sizeof(stack_contents)); + + new->SavedState.RSP = new->KernelStack - sizeof(stack_contents); + new->SavedState.RIP = (Uint)&NewTaskHeader; + new->SavedState.SSE = NULL; + +// Log("New (Worker) %p, rsp = %p\n", new->SavedState.RIP, new->SavedState.RSP); + + // Mark as active + new->Status = THREAD_STAT_PREINIT; + Threads_AddActive( new ); + + return new->TID; +} + +/** + * \brief Creates a new user stack + */ +Uint Proc_MakeUserStack(void) +{ + int i; + Uint base = USER_STACK_TOP - USER_STACK_SZ; + + // Check Prospective Space + for( i = USER_STACK_SZ >> 12; i--; ) + { + if( MM_GetPhysAddr( base + (i<<12) ) != 0 ) + break; + } + + if(i != -1) return 0; + + // Allocate Stack - Allocate incrementally to clean up MM_Dump output + // - Most of the user stack is the zero page + for( i = 0; i < (USER_STACK_SZ-USER_STACK_PREALLOC)/0x1000; i++ ) + { + MM_AllocateZero( base + (i<<12) ); + } + // - but the top USER_STACK_PREALLOC pages are actually allocated + for( ; i < USER_STACK_SZ/0x1000; i++ ) + { + tPAddr alloc = MM_Allocate( base + (i<<12) ); + if( !alloc ) + { + // Error + Log_Error("Proc", "Unable to allocate user stack (%i pages requested)", USER_STACK_SZ/0x1000); + while( i -- ) + MM_Deallocate( base + (i<<12) ); + return 0; + } + } + + return base + USER_STACK_SZ; +} + +void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) +{ + Uint *stack; + int i; + const char **envp = NULL; + Uint16 ss, cs; + + + // Copy Arguments + stack = (void*)Proc_MakeUserStack(); + if(!stack) { + Log_Error("Proc", "Unable to create user stack!"); + Threads_Exit(0, -1); + } + stack -= (DataSize+7)/8; + memcpy( stack, ArgV, DataSize ); + free(ArgV); + + // Adjust Arguments and environment + if(DataSize) + { + Uint delta = (Uint)stack - (Uint)ArgV; + ArgV = (const char**)stack; + for( i = 0; ArgV[i]; i++ ) ArgV[i] += delta; + envp = &ArgV[i+1]; + for( i = 0; envp[i]; i++ ) envp[i] += delta; + } + + // User Mode Segments + // 0x2B = 64-bit + ss = 0x23; cs = 0x2B; + + // Arguments + *--stack = (Uint)envp; + *--stack = (Uint)ArgV; + *--stack = (Uint)ArgC; + *--stack = Base; + + Proc_StartProcess(ss, (Uint)stack, 0x202, cs, Entrypoint); +} + +void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) +{ + if( !(CS == 0x1B || CS == 0x2B) || SS != 0x23 ) { + Log_Error("Proc", "Proc_StartProcess: CS / SS are not valid (%x, %x)", + CS, SS); + Threads_Exit(0, -1); + } +// Log("Proc_StartProcess: (SS=%x, Stack=%p, Flags=%x, CS=%x, IP=%p)", SS, Stack, Flags, CS, IP); +// MM_DumpTables(0, USER_MAX); + if(CS == 0x1B) + { + // 32-bit return + __asm__ __volatile__ ( + "mov %0, %%rsp;\n\t" // Set stack pointer + "mov %2, %%r11;\n\t" // Set RFLAGS + "sysret;\n\t" + : : "r" (Stack), "c" (IP), "r" (Flags) + ); + } + else + { + // 64-bit return + __asm__ __volatile__ ( + "mov %0, %%rsp;\n\t" // Set stack pointer + "mov %2, %%r11;\n\t" // Set RFLAGS + "sysretq;\n\t" + : : "r" (Stack), "c" (IP), "r" (Flags) + ); + } + for(;;); +} + +/** + * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs) + * \brief Demotes a process to a lower permission level + * \param Err Pointer to user's errno + * \param Dest New Permission Level + * \param Regs Pointer to user's register structure + */ +int Proc_Demote(Uint *Err, int Dest, tRegs *Regs) +{ + int cpl = Regs->CS & 3; + // Sanity Check + if(Dest > 3 || Dest < 0) { + *Err = -EINVAL; + return -1; + } + + // Permission Check + if(cpl > Dest) { + *Err = -EACCES; + return -1; + } + + // Change the Segment Registers + Regs->CS = (((Dest+1)<<4) | Dest) - 8; + Regs->SS = ((Dest+1)<<4) | Dest; + + return 0; +} + +/** + * \brief Calls a signal handler in user mode + * \note Used for signals + */ +void Proc_CallFaultHandler(tThread *Thread) +{ + // Never returns + Proc_ReturnToUser(Thread->FaultHandler, Thread->KernelStack, Thread->CurFaultNum); + for(;;); +} + +void Proc_DumpThreadCPUState(tThread *Thread) +{ + Log(" At %04x:%016llx", Thread->SavedState.UserCS, Thread->SavedState.UserRIP); +} + +void Proc_Reschedule(void) +{ + tThread *nextthread, *curthread; + int cpu = GetCPUNum(); + + // TODO: Wait for it? + if(IS_LOCKED(&glThreadListLock)) return; + + curthread = gaCPUs[cpu].Current; + + nextthread = Threads_GetNextToRun(cpu, curthread); + + if(nextthread == curthread) return ; + if(!nextthread) + nextthread = gaCPUs[cpu].IdleThread; + if(!nextthread) + return ; + + #if DEBUG_TRACE_SWITCH + LogF("\nSwitching to task CR3 = 0x%x, RIP = %p, RSP = %p - %i (%s)\n", + nextthread->Process->MemState.CR3, + nextthread->SavedState.RIP, + nextthread->SavedState.RSP, + nextthread->TID, + nextthread->ThreadName + ); + #endif + + // Update CPU state + gaCPUs[cpu].Current = nextthread; + gTSSs[cpu].RSP0 = nextthread->KernelStack-4; + __asm__ __volatile__ ("mov %0, %%db0" : : "r" (nextthread)); + + if( curthread ) + { + // Save FPU/MMX/XMM/SSE state + if( curthread->SavedState.SSE ) + { + Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF ); + curthread->SavedState.bSSEModified = 0; + Proc_DisableSSE(); + } + SwitchTasks( + nextthread->SavedState.RSP, &curthread->SavedState.RSP, + nextthread->SavedState.RIP, &curthread->SavedState.RIP, + nextthread->Process->MemState.CR3 + ); + } + else + { + Uint tmp; + SwitchTasks( + nextthread->SavedState.RSP, &tmp, + nextthread->SavedState.RIP, &tmp, + nextthread->Process->MemState.CR3 + ); + } + return ; +} + +/** + * \fn void Proc_Scheduler(int CPU) + * \brief Swap current thread and clears dead threads + */ +void Proc_Scheduler(int CPU, Uint RSP, Uint RIP) +{ +#if 0 + tThread *thread; + + // If the spinlock is set, let it complete + if(IS_LOCKED(&glThreadListLock)) return; + + // Get current thread + thread = gaCPUs[CPU].Current; + + if( thread ) + { + tRegs *regs; + // Reduce remaining quantum and continue timeslice if non-zero + if(thread->Remaining--) return; + // Reset quantum for next call + thread->Remaining = thread->Quantum; + + // TODO: Make this more stable somehow + { + regs = (tRegs*)(RSP+(1)*8); // CurThread + thread->SavedState.UserCS = regs->CS; + thread->SavedState.UserRIP = regs->RIP; + } + } + + // ACK Timer here? + + Proc_Reschedule(); +#endif +} + +// === EXPORTS === +EXPORT(Proc_SpawnWorker); diff --git a/KernelLand/Kernel/arch/x86_64/rme.c b/KernelLand/Kernel/arch/x86_64/rme.c new file mode 120000 index 00000000..330f4f5f --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/rme.c @@ -0,0 +1 @@ +/home/tpg/Projects/RealmodeEmulator/src/rme.c \ No newline at end of file diff --git a/KernelLand/Kernel/arch/x86_64/rme.h b/KernelLand/Kernel/arch/x86_64/rme.h new file mode 120000 index 00000000..98b2739f --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/rme.h @@ -0,0 +1 @@ +/home/tpg/Projects/RealmodeEmulator/src/rme.h \ No newline at end of file diff --git a/KernelLand/Kernel/arch/x86_64/start32.asm b/KernelLand/Kernel/arch/x86_64/start32.asm new file mode 100644 index 00000000..52b133c6 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/start32.asm @@ -0,0 +1,188 @@ +; +; Acess2 x86_64 port +; + +%include "arch/x86_64/include/common.inc.asm" + +[BITS 32] + +;KERNEL_BASE equ 0xFFFF800000000000 +KERNEL_BASE equ 0xFFFFFFFF80000000 + +[section .multiboot] +mboot: + ; Multiboot macros to make a few lines later more readable + MULTIBOOT_PAGE_ALIGN equ 1<<0 + MULTIBOOT_MEMORY_INFO equ 1<<1 + MULTIBOOT_AOUT_KLUDGE equ 1<<16 + MULTIBOOT_HEADER_MAGIC equ 0x1BADB002 + MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO; | MULTIBOOT_AOUT_KLUDGE + MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) + + ; This is the GRUB Multiboot header. A boot signature + dd MULTIBOOT_HEADER_MAGIC + dd MULTIBOOT_HEADER_FLAGS + dd MULTIBOOT_CHECKSUM + [extern __load_addr] + [extern __bss_start] + [extern gKernelEnd] + ; a.out kludge + dd mboot ; Location of Multiboot Header + dd __load_addr ; Load address + dd __bss_start - KERNEL_BASE ; End of .data + dd gKernelEnd - KERNEL_BASE ; End of .bss (and kernel) + dd start - KERNEL_BASE ; Entrypoint + +[extern start64] + +[section .text] +[global start] +start: + mov [gMultibootMagic - KERNEL_BASE], eax + mov [gMultibootPtr - KERNEL_BASE], ebx + + ; Check for Long Mode support + mov eax, 0x80000000 + cpuid + cmp eax, 0x80000001 ; Compare the A-register with 0x80000001. + mov eax, 0x80000001 + cpuid + jb .not64bitCapable + test edx, 1<<29 + jz .not64bitCapable + + ; Enable PGE (Page Global Enable) + ; + PAE (Physical Address Extension) + ; + PSE (Page Size Extensions) + mov eax, cr4 + or eax, 0x80|0x20|0x10 + mov cr4, eax + + ; Load PDP4 + mov eax, gInitialPML4 - KERNEL_BASE + mov cr3, eax + + ; Enable IA-32e mode + ; (Also enables SYSCALL and NX) + mov ecx, 0xC0000080 + rdmsr + or eax, (1 << 11)|(1 << 8)|(1 << 0) ; NXE, LME, SCE + wrmsr + + ; Enable paging + mov eax, cr0 + or eax, 0x80010000 ; PG & WP + mov cr0, eax + + ; Load GDT + lgdt [gGDTPtr - KERNEL_BASE] + jmp 0x08:start64 - KERNEL_BASE + +.not64bitCapable: + mov ah, 0x0F + mov edi, 0xB8000 + mov esi, csNot64BitCapable - KERNEL_BASE + +.loop: + lodsb + test al, al + jz .hlt + stosw + jmp .loop + +.hlt: + cli + hlt + jmp .hlt + +[section .data] +[global gGDT] +[global gGDTPtr] +gGDT: + dd 0,0 + dd 0x00000000, 0x00209A00 ; 0x08: 64-bit Code + dd 0x00000000, 0x00009200 ; 0x10: 64-bit Data + dd 0x00000000, 0x0040FA00 ; 0x18: 32-bit User Code + dd 0x00000000, 0x0040F200 ; 0x20: User Data + dd 0x00000000, 0x0020FA00 ; 0x28: 64-bit User Code + dd 0x00000000, 0x0000F200 ; 0x30: User Data (64 version) + times MAX_CPUS dd 0, 0x00008900, 0, 0 ; 0x38+16*n: TSS 0 +gGDTPtr: + dw $-gGDT-1 + dd gGDT-KERNEL_BASE + dd 0 +[global gMultibootPtr] +[global gMultibootMagic] +gMultibootMagic: + dd 0 +gMultibootPtr: + dd 0 + +[section .padata] +[global gInitialPML4] +gInitialPML4: ; Covers 256 TiB (Full 48-bit Virtual Address Space) + dd gInitialPDP - KERNEL_BASE + 3, 0 ; Identity Map Low 4Mb + times 0xA0*2-1 dq 0 + dd gStackPDP - KERNEL_BASE + 3, 0 + times 512-4-($-gInitialPML4)/8 dq 0 + dd gInitialPML4 - KERNEL_BASE + 3, 0 ; Fractal Mapping + dq 0 + dq 0 + dd gHighPDP - KERNEL_BASE + 3, 0 ; Map Low 4Mb to kernel base + +gInitialPDP: ; Covers 512 GiB + dd gInitialPD - KERNEL_BASE + 3, 0 + times 511 dq 0 + +gStackPDP: + dd gStackPD - KERNEL_BASE + 3, 0 + times 511 dq 0 + +gHighPDP: ; Covers 512 GiB + times 510 dq 0 + ;dq 0 + 0x143 ; 1 GiB Page from zero + dd gInitialPD - KERNEL_BASE + 3, 0 + dq 0 + +gInitialPD: ; Covers 1 GiB +; dq 0 + 0x143 ; 1 GiB Page from zero + dd gInitialPT1 - KERNEL_BASE + 3, 0 + dd gInitialPT2 - KERNEL_BASE + 3, 0 + times 510 dq 0 + +gStackPD: + dd gKStackPT - KERNEL_BASE + 3, 0 + times 511 dq 0 + +gKStackPT: ; Covers 2 MiB + ; Initial stack - 64KiB + dq 0 + %assign i 0 + %rep INITIAL_KSTACK_SIZE-1 + dd gInitialKernelStack - KERNEL_BASE + i*0x1000 + 0x103, 0 + %assign i i+1 + %endrep + times 512-INITIAL_KSTACK_SIZE dq 0 +gInitialPT1: ; 2 MiB + %assign i 0 + %rep 512 + dq i*4096+0x103 + %assign i i+1 + %endrep +gInitialPT2: ; 2 MiB + %assign i 512 + %rep 512 + dq i*4096+0x103 + %assign i i+1 + %endrep + +[section .padata] +[global gInitialKernelStack] +gInitialKernelStack: + times 0x1000*(INITIAL_KSTACK_SIZE-1) db 0 ; 8 Pages + +[section .rodata] +csNot64BitCapable: + db "Not 64-bit Capable",0 + +; vim: ft=nasm diff --git a/KernelLand/Kernel/arch/x86_64/start64.asm b/KernelLand/Kernel/arch/x86_64/start64.asm new file mode 100644 index 00000000..c8e7f9a8 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/start64.asm @@ -0,0 +1,143 @@ +; +; Acess2 x86_64 Port +; +%include "arch/x86_64/include/common.inc.asm" +[bits 64] +;KERNEL_BASE equ 0xFFFF800000000000 +KERNEL_BASE equ 0xFFFFFFFF80000000 + +[extern kmain] + +[extern gMultibootPtr] +[extern gMultibootMagic] + +[section .text] +[global start64] +start64: + ; Load Registers + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; Go to high memory + mov rax, start64.himem + jmp rax +.himem: + + xor rax, rax + mov dr0, rax ; Set CPU0 + + ; Clear the screen + mov rax, 0x1F201F201F201F20 ; Set the screen to White on blue, space (4 characters) + mov edi, 0xB8000 + mov ecx, 80*25*2/8 + rep stosq + + ; Set kernel stack + mov rsp, 0xFFFFA00000000000 + INITIAL_KSTACK_SIZE*0x1000 + + ; Call main + mov edi, [gMultibootMagic - KERNEL_BASE] + mov esi, [gMultibootPtr - KERNEL_BASE] + call kmain + + cli +.hlt: + hlt + jmp .hlt + +[global GetCPUNum] +GetCPUNum: + mov rax, dr1 + ret + +KSTACK_USERSTATE_SIZE equ (5+2+16+2)*8 ; IRET, ErrorNum, ErrorCode, GPRs, FS&GS +[global Proc_ReturnToUser] +Proc_ReturnToUser: + ; RDI - Handler + ; RSI - Kernel Stack + ; RDX - Signal num + + ; + ; NOTE: This can cause corruption if the signal happens while the user + ; has called a kernel operation. + ; Good thing this can only be called on a user fault. + ; + + xchg bx, bx + ; Get and alter User SP + mov rcx, [rsi-0x20] ; Get user SP + xor eax, eax + mov [rcx-16], rax + sub rcx, 16 + + ; Drop down to user mode + cli + mov rsp, rcx ; Set SP + mov rcx, rdi ; SYSRET IP + + mov rdi, rdx ; Argument for handler + mov r11, 0x202 ; RFlags + db 0x48 + sysret + +; int CallWithArgArray(void *Ptr, int NArgs, Uint *Args) +; Call a function passing the array as arguments +[global CallWithArgArray] +CallWithArgArray: + push rbp + mov rbp, rsp + push r10 + push r11 + + mov [rbp+2*8], rdi ; Save Ptr to stack + + mov r11, rsi ; NArgs + mov r10, rdx ; Args + + ; Arg 1: RDI + mov rdi, [r10] + add r10, 8 + dec r11 + jz .call + ; Arg 2: RSI + mov rsi, [r10] + add r10, 8 + dec r11 + jz .call + ; Arg 3: RDX + mov rdx, [r10] + add r10, 8 + dec r11 + jz .call + ; Arg 4: RCX + mov rcx, [r10] + add r10, 8 + dec r11 + jz .call + ; Arg 5: R8 + mov r8, [r10] + add r10, 8 + dec r11 + jz .call + ; Arg 6: R9 + mov r9, [r10] + add r10, 8 + dec r11 + jz .call + ; No support for more + +.call: + mov rax, [rbp+2*8] ; Ptr + call rax + + pop r11 + pop r10 + + lea rsp, [rbp] + pop rbp + ret + +; vim: ft=nasm diff --git a/KernelLand/Kernel/arch/x86_64/time.c b/KernelLand/Kernel/arch/x86_64/time.c new file mode 100644 index 00000000..03c889fe --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/time.c @@ -0,0 +1,111 @@ +/* + * Acess2 Kernel + * Timekeeping + * arch/x86_64/time.c + */ +#include +#include + +// === MACROS === +#define TIMER_QUANTUM 100 +#define TIMER_FREQ PIT_TIMER_BASE_N/(PIT_TIMER_BASE_D*PIT_TIMER_DIVISOR) +#define MS_PER_TICK_WHOLE (1000*(PIT_TIMER_BASE_D*PIT_TIMER_DIVISOR)/PIT_TIMER_BASE_N) +#define MS_PER_TICK_FRACT ((0x80000000ULL*1000ULL*PIT_TIMER_BASE_D*PIT_TIMER_DIVISOR/PIT_TIMER_BASE_N)&0x7FFFFFFF) + +// === IMPORTS === +extern volatile Sint64 giTimestamp; +extern volatile Uint64 giTicks; +extern volatile Uint64 giPartMiliseconds; +extern void Timer_CallTimers(void); + +// === GLOBALS === +volatile Uint64 giTime_TSCAtLastTick = 0; +volatile Uint64 giTime_TSCPerTick = 0; + +// === PROTOTYPES === +//Sint64 now(void); + int Time_Setup(void); +void Time_UpdateTimestamp(void); +Uint64 Time_ReadTSC(void); + +// === CODE === +/** + * \fn Sint64 now() + * \brief Return the current timestamp + */ +Sint64 now(void) +{ + Uint64 tsc = Time_ReadTSC(); + tsc -= giTime_TSCAtLastTick; + tsc *= MS_PER_TICK_WHOLE; + if( giTime_TSCPerTick ) { + tsc /= giTime_TSCPerTick; + } + else + tsc = 0; + return giTimestamp + tsc; +} + +/** + * \fn int Time_Setup(void) + * \brief Sets the system time from the Realtime-Clock + */ +int Time_Setup(void) +{ + Log_Log("Timer", "PIT Timer firing at %iHz, %i.0x%08x miliseconds per tick", + TIMER_FREQ, MS_PER_TICK_WHOLE, MS_PER_TICK_FRACT); + + // TODO: Read time from RTC + + return 0; +} + +/** + * \brief Called on the timekeeping IRQ + */ +void Time_UpdateTimestamp(void) +{ + Uint64 curTSC = Time_ReadTSC(); + + if( giTime_TSCAtLastTick ) + { + giTime_TSCPerTick = curTSC - giTime_TSCAtLastTick; + } + giTime_TSCAtLastTick = curTSC; + + giTicks ++; + giTimestamp += MS_PER_TICK_WHOLE; + giPartMiliseconds += MS_PER_TICK_FRACT; + if(giPartMiliseconds > 0x80000000) { + giTimestamp ++; + giPartMiliseconds -= 0x80000000; + } + + Timer_CallTimers(); +} + +#if 0 +/** + * \fn void Time_TimerThread(void) + */ +void Time_TimerThread(void) +{ + Sint64 next; + Threads_SetName("TIMER"); + + next = giTimestamp + TIMER_QUANTUM; + for(;;) + { + while(giTimestamp < next) Threads_Yield(); + next = giTimestamp + TIMER_QUANTUM; + Timer_CallTimers(); + } +} +#endif + +Uint64 Time_ReadTSC(void) +{ + Uint32 a, d; + __asm__ __volatile__ ("rdtsc" : "=a" (a), "=d" (d)); + return ((Uint64)d << 32) | a; +} diff --git a/KernelLand/Kernel/arch/x86_64/vm8086.c b/KernelLand/Kernel/arch/x86_64/vm8086.c new file mode 100644 index 00000000..42515705 --- /dev/null +++ b/KernelLand/Kernel/arch/x86_64/vm8086.c @@ -0,0 +1,56 @@ +/* + */ +#include +#include +#include +//#include "rme.h" + +// === CONSTANTS === +#define VM8086_STACK_SEG 0x9F00 +#define VM8086_STACK_OFS 0x0AFE +#define VM8086_PAGES_PER_INST 4 + +// === PROTOTYPES === + int VM8086_Install(char **Arguments); +//tVM8086 *VM8086_Init(void); +//void VM8086_Free(tVM8086 *State); + +// === GLOBALS === +MODULE_DEFINE(0, 0x100, VM8086, VM8086_Install, NULL, NULL); +tMutex glVM8086_Process; +//tRME_State *gpVM8086_State; +tPID gVM8086_WorkerPID; +tTID gVM8086_CallingThread; +tVM8086 volatile * volatile gpVM8086_State = (void*)-1; // Set to -1 to avoid race conditions + +// === CODE === +int VM8086_Install(char **Arguments) +{ + //gpVM8086_State = RME_CreateState(); + return MODULE_ERR_OK; +} + +tVM8086 *VM8086_Init(void) +{ + return NULL; +} + +void VM8086_Free(tVM8086 *State) +{ + +} + +void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset) +{ + return NULL; +} + +void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset) +{ + return NULL; +} + +void VM8086_Int(tVM8086 *State, Uint8 Interrupt) +{ + +} diff --git a/KernelLand/Kernel/bin/README b/KernelLand/Kernel/bin/README new file mode 100644 index 00000000..63d64bb9 --- /dev/null +++ b/KernelLand/Kernel/bin/README @@ -0,0 +1,24 @@ +Acess2 Binary File Loader Specifcation +-------------------------------------- + +Binary file loaders are defined by creating a \a tBinaryType variable that +is registered with the kernel. + +\a tBinaryType contains seven fields. +-# \a Next + This field is used internally by the kernel and should be set to NULL + when the definition is initialise and not changed by the driver. +-# \a Ident + This field tells the kernel how to recognise this file format. If the + first 32-bits of the file (ANDed with the \a Mask field) match this + value, the file will be passed to this loader. +-# \a Mask + Determines what bits in the first 32-bits of the file matter for the + identifcation. +-# \a Name + This is a C string that uniquely identifies this binary format. +-# \a Load + This field is a pointer to a function that takes a VFS Handle of the + source exectuable file as an argument and returns a \a tBinary + pointer that defines the location and attributes of the exectable's + segments. (See \a tBinary for more information) diff --git a/KernelLand/Kernel/bin/elf.c b/KernelLand/Kernel/bin/elf.c new file mode 100644 index 00000000..8f51290a --- /dev/null +++ b/KernelLand/Kernel/bin/elf.c @@ -0,0 +1,643 @@ +/* + * Acess v0.1 + * ELF Executable Loader Code + */ +#define DEBUG 0 +#include +#include +#include "elf.h" + +#define DEBUG_WARN 1 + +// === PROTOTYPES === +tBinary *Elf_Load(int fp); +tBinary *Elf_Load64(int fp, Elf64_Ehdr *hdr); +tBinary *Elf_Load32(int fp, Elf32_Ehdr *hdr); + int Elf_Relocate(void *Base); + int Elf_Relocate32(void *Base); + int Elf_GetSymbol(void *Base, const char *Name, Uint *ret); + int Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base); +Uint Elf_Int_HashString(const char *str); + +// === GLOBALS === +tBinaryType gELF_Info = { + NULL, + 0x464C457F, 0xFFFFFFFF, // '\x7FELF' + "ELF", + Elf_Load, Elf_Relocate, Elf_GetSymbol + }; + +// === CODE === +tBinary *Elf_Load(int fp) +{ + Elf64_Ehdr hdr; + + // Read ELF Header + VFS_Read(fp, sizeof(hdr), &hdr); + + // Check the file type + if(hdr.e_ident[0] != 0x7F || hdr.e_ident[1] != 'E' || hdr.e_ident[2] != 'L' || hdr.e_ident[3] != 'F') { + Log_Warning("ELF", "Non-ELF File was passed to the ELF loader"); + return NULL; + } + + switch(hdr.e_ident[4]) // EI_CLASS + { + case ELFCLASS32: + return Elf_Load32(fp, (Elf32_Ehdr*)&hdr); + case ELFCLASS64: + return Elf_Load64(fp, &hdr); + default: + Log_Warning("ELF", "Unknown EI_CLASS value %i", hdr.e_ident[4]); + return NULL; + } +} + +tBinary *Elf_Load64(int FD, Elf64_Ehdr *Header) +{ + tBinary *ret; + Elf64_Phdr phtab[Header->e_phnum]; + int nLoadSegments; + int i, j; + + // Sanity check + if( Header->e_phoff == 0 ) + { + Log_Warning("ELF", "No program header, panic!"); + return NULL; + } + if( Header->e_shentsize != sizeof(Elf64_Shdr) ) { + Log_Warning("ELF", "Header gives shentsize as %i, my type is %i", + Header->e_shentsize, sizeof(Elf64_Shdr) ); + } + if( Header->e_phentsize != sizeof(Elf64_Phdr) ) { + Log_Warning("ELF", "Header gives phentsize as %i, my type is %i", + Header->e_phentsize, sizeof(Elf64_Phdr) ); + } + + LOG("Header = {"); + LOG(" e_ident = %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", + Header->e_ident[0], Header->e_ident[1], Header->e_ident[2], Header->e_ident[3], + Header->e_ident[4], Header->e_ident[5], Header->e_ident[6], Header->e_ident[7], + Header->e_ident[8], Header->e_ident[9], Header->e_ident[10], Header->e_ident[11], + Header->e_ident[12], Header->e_ident[13], Header->e_ident[14], Header->e_ident[15] + ); + LOG(" e_type = %i", Header->e_type); + LOG(" e_machine = %i", Header->e_machine); + LOG(" e_version = %i", Header->e_version); + LOG(" e_entry = 0x%llx", Header->e_entry); + LOG(" e_phoff = 0x%llx", Header->e_phoff); + LOG(" e_shoff = 0x%llx", Header->e_shoff); + LOG(" e_flags = 0x%x", Header->e_flags); + LOG(" e_ehsize = %i", Header->e_ehsize); + LOG(" e_phentsize = %i", Header->e_phentsize); + LOG(" e_phnum = %i", Header->e_phnum); + LOG(" e_shentsize = %i", Header->e_shentsize); + LOG(" e_shnum = %i", Header->e_shnum); + LOG(" e_shstrndx = %i", Header->e_shstrndx); + LOG("}"); + + // Load Program Header table + VFS_Seek(FD, Header->e_phoff, SEEK_SET); + VFS_Read(FD, sizeof(Elf64_Phdr)*Header->e_phnum, phtab); + + // Count load segments + nLoadSegments = 0; + for( i = 0; i < Header->e_phnum; i ++ ) + { + if( phtab[i].p_type != PT_LOAD ) continue ; + nLoadSegments ++; + } + + // Allocate Information Structure + ret = malloc( sizeof(tBinary) + sizeof(tBinarySection)*nLoadSegments ); + // Fill Info Struct + ret->Entry = Header->e_entry; + ret->Base = -1; // Set Base to maximum value + ret->NumSections = nLoadSegments; + ret->Interpreter = NULL; + + j = 0; // LoadSections[] index + for( i = 0; i < Header->e_phnum; i ++ ) + { + LOG("phtab[%i] = {", i); + LOG(" .p_type = %i", phtab[i].p_type); + LOG(" .p_flags = 0x%x", phtab[i].p_flags); + LOG(" .p_offset = 0x%llx", phtab[i].p_offset); + LOG(" .p_vaddr = 0x%llx", phtab[i].p_vaddr); + LOG(" .p_paddr = 0x%llx", phtab[i].p_paddr); + LOG(" .p_filesz = 0x%llx", phtab[i].p_filesz); + LOG(" .p_memsz = 0x%llx", phtab[i].p_memsz); + LOG(" .p_align = 0x%llx", phtab[i].p_align); + LOG("}"); + + // Get Interpreter Name + if( phtab[i].p_type == PT_INTERP ) + { + char *tmp; + if(ret->Interpreter) continue; + tmp = malloc(phtab[i].p_filesz); + VFS_Seek(FD, phtab[i].p_offset, 1); + VFS_Read(FD, phtab[i].p_filesz, tmp); + ret->Interpreter = Binary_RegInterp(tmp); + LOG("Interpreter '%s'", tmp); + free(tmp); + continue; + } + + if( phtab[i].p_type != PT_LOAD ) continue ; + + // Find the executable base + if( phtab[i].p_vaddr < ret->Base ) ret->Base = phtab[i].p_vaddr; + + ret->LoadSections[j].Offset = phtab[i].p_offset; + ret->LoadSections[j].Virtual = phtab[i].p_vaddr; + ret->LoadSections[j].FileSize = phtab[i].p_filesz; + ret->LoadSections[j].MemSize = phtab[i].p_memsz; + + ret->LoadSections[j].Flags = 0; + if( !(phtab[i].p_flags & PF_W) ) + ret->LoadSections[j].Flags |= BIN_SECTFLAG_RO; + if( phtab[i].p_flags & PF_X ) + ret->LoadSections[j].Flags |= BIN_SECTFLAG_EXEC; + j ++; + } + + return ret; +} + +tBinary *Elf_Load32(int FD, Elf32_Ehdr *Header) +{ + tBinary *ret; + Elf32_Phdr *phtab; + int i, j; + int iLoadCount; + + ENTER("xFD", FD); + + // Check architecture with current CPU + // - TODO: Support kernel level emulation + #if ARCH_IS_x86 + if( Header->machine != EM_386 ) + { + Log_Warning("ELF", "Unknown architecure on ELF-32"); + LEAVE_RET('n'); + return NULL; + } + #endif + + // Check for a program header + if(Header->phoff == 0) { + #if DEBUG_WARN + Log_Warning("ELF", "File does not contain a program header (phoff == 0)"); + #endif + LEAVE('n'); + return NULL; + } + + // Read Program Header Table + phtab = malloc( sizeof(Elf32_Phdr) * Header->phentcount ); + if( !phtab ) { + LEAVE('n'); + return NULL; + } + LOG("hdr.phoff = 0x%08x", Header->phoff); + VFS_Seek(FD, Header->phoff, SEEK_SET); + VFS_Read(FD, sizeof(Elf32_Phdr)*Header->phentcount, phtab); + + // Count Pages + iLoadCount = 0; + LOG("Header->phentcount = %i", Header->phentcount); + for( i = 0; i < Header->phentcount; i++ ) + { + // Ignore Non-LOAD types + if(phtab[i].Type != PT_LOAD) + continue; + iLoadCount ++; + LOG("phtab[%i] = {VAddr:0x%x, MemSize:0x%x}", i, phtab[i].VAddr, phtab[i].MemSize); + } + + LOG("iLoadCount = %i", iLoadCount); + + // Allocate Information Structure + ret = malloc( sizeof(tBinary) + sizeof(tBinarySection)*iLoadCount ); + // Fill Info Struct + ret->Entry = Header->entrypoint; + ret->Base = -1; // Set Base to maximum value + ret->NumSections = iLoadCount; + ret->Interpreter = NULL; + + // Load Pages + j = 0; + for( i = 0; i < Header->phentcount; i++ ) + { + //LOG("phtab[%i].Type = 0x%x", i, phtab[i].Type); + LOG("phtab[%i] = {", i); + LOG(" .Type = 0x%08x", phtab[i].Type); + LOG(" .Offset = 0x%08x", phtab[i].Offset); + LOG(" .VAddr = 0x%08x", phtab[i].VAddr); + LOG(" .PAddr = 0x%08x", phtab[i].PAddr); + LOG(" .FileSize = 0x%08x", phtab[i].FileSize); + LOG(" .MemSize = 0x%08x", phtab[i].MemSize); + LOG(" .Flags = 0x%08x", phtab[i].Flags); + LOG(" .Align = 0x%08x", phtab[i].Align); + LOG(" }"); + // Get Interpreter Name + if( phtab[i].Type == PT_INTERP ) + { + char *tmp; + if(ret->Interpreter) continue; + tmp = malloc(phtab[i].FileSize); + VFS_Seek(FD, phtab[i].Offset, 1); + VFS_Read(FD, phtab[i].FileSize, tmp); + ret->Interpreter = Binary_RegInterp(tmp); + LOG("Interpreter '%s'", tmp); + free(tmp); + continue; + } + // Ignore non-LOAD types + if(phtab[i].Type != PT_LOAD) continue; + + // Find Base + if(phtab[i].VAddr < ret->Base) ret->Base = phtab[i].VAddr; + + LOG("phtab[%i] = {VAddr:0x%x,Offset:0x%x,FileSize:0x%x}", + i, phtab[i].VAddr, phtab[i].Offset, phtab[i].FileSize); + + ret->LoadSections[j].Offset = phtab[i].Offset; + ret->LoadSections[j].FileSize = phtab[i].FileSize; + ret->LoadSections[j].Virtual = phtab[i].VAddr; + ret->LoadSections[j].MemSize = phtab[i].MemSize; + ret->LoadSections[j].Flags = 0; + if( !(phtab[i].Flags & PF_W) ) + ret->LoadSections[j].Flags |= BIN_SECTFLAG_RO; + if( phtab[i].Flags & PF_X ) + ret->LoadSections[j].Flags |= BIN_SECTFLAG_EXEC; + j ++; + } + + // Clean Up + free(phtab); + // Return + LEAVE('p', ret); + return ret; +} + +// --- ELF RELOCATION --- +int Elf_Relocate(void *Base) +{ + Elf64_Ehdr *hdr = Base; + + switch( hdr->e_ident[EI_CLASS] ) + { + case ELFCLASS32: + return Elf_Relocate32(Base); + case ELFCLASS64: + return 0; + default: + return 1; + } +} + + +/** + * \brief Relocates a loaded ELF Executable + */ +int Elf_Relocate32(void *Base) +{ + Elf32_Ehdr *hdr = Base; + Elf32_Phdr *phtab; + int i, j; // Counters + char *libPath; + Uint iRealBase = -1; + Uint iBaseDiff; + int iSegmentCount; + int iSymCount = 0; + Elf32_Rel *rel = NULL; + Elf32_Rela *rela = NULL; + Uint32 *pltgot = NULL; + void *plt = NULL; + Uint32 *ptr; + int relSz=0, relEntSz=8; + int relaSz=0, relaEntSz=8; + int pltSz=0, pltType=0; + Elf32_Dyn *dynamicTab = NULL; // Dynamic Table Pointer + char *dynstrtab = NULL; // .dynamic String Table + Elf32_Sym *dynsymtab = NULL; + int bFailed = 0; + + ENTER("pBase", Base); + + // Parse Program Header to get Dynamic Table + phtab = (void *)( (tVAddr)Base + hdr->phoff ); + iSegmentCount = hdr->phentcount; + for(i = 0; i < iSegmentCount; i ++ ) + { + // Determine linked base address + if(phtab[i].Type == PT_LOAD && iRealBase > phtab[i].VAddr) + iRealBase = phtab[i].VAddr; + + // Find Dynamic Section + if(phtab[i].Type == PT_DYNAMIC) { + if(dynamicTab) { + Log_Warning("ELF", "Elf_Relocate - Multiple PT_DYNAMIC segments\n"); + continue; + } + dynamicTab = (void *) (tVAddr) phtab[i].VAddr; + j = i; // Save Dynamic Table ID + break; + } + } + + // Check if a PT_DYNAMIC segement was found + if(!dynamicTab) { + Log_Warning("ELF", "Elf_Relocate: No PT_DYNAMIC segment in image, returning\n"); + LEAVE('x', 0); + return 0; + } + + // Page Align real base + iRealBase &= ~0xFFF; + + // Adjust "Real" Base + iBaseDiff = (Uint)Base - iRealBase; + // Adjust Dynamic Table + dynamicTab = (void *) ((Uint)dynamicTab + iBaseDiff); + + // === Get Symbol table and String Table === + for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++) + { + switch(dynamicTab[j].d_tag) + { + // --- Symbol Table --- + case DT_SYMTAB: + dynamicTab[j].d_val += iBaseDiff; + dynsymtab = (void*) (tVAddr) dynamicTab[j].d_val; + hdr->misc.SymTable = dynamicTab[j].d_val; // Saved in unused bytes of ident + break; + + // --- String Table --- + case DT_STRTAB: + dynamicTab[j].d_val += iBaseDiff; + dynstrtab = (void*) (tVAddr) dynamicTab[j].d_val; + break; + + // --- Hash Table -- + case DT_HASH: + dynamicTab[j].d_val += iBaseDiff; + iSymCount = ((Uint*)((tVAddr)dynamicTab[j].d_val))[1]; + hdr->misc.HashTable = dynamicTab[j].d_val; // Saved in unused bytes of ident + break; + } + } + + if( !dynsymtab && iSymCount > 0 ) { + Log_Warning("ELF", "Elf_Relocate: No Dynamic symbol table, but count >0"); + return 0; + } + + // Alter Symbols to true base + for(i = 0; i < iSymCount; i ++) + { + dynsymtab[i].value += iBaseDiff; + dynsymtab[i].nameOfs += (Uint)dynstrtab; + //LOG("Sym '%s' = 0x%x (relocated)\n", dynsymtab[i].name, dynsymtab[i].value); + } + + // === Add to loaded list (can be imported now) === + //Binary_AddLoaded( (Uint)Base ); + + // === Parse Relocation Data === + for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++) + { + switch(dynamicTab[j].d_tag) + { + // --- Shared Library Name --- + case DT_SONAME: + LOG(".so Name '%s'\n", dynstrtab+dynamicTab[j].d_val); + break; + // --- Needed Library --- + case DT_NEEDED: + libPath = dynstrtab + dynamicTab[j].d_val; + Log_Notice("ELF", "%p - Required Library '%s' (Ignored in kernel mode)\n", Base, libPath); + break; + // --- PLT/GOT --- + case DT_PLTGOT: pltgot = (void*)(iBaseDiff+dynamicTab[j].d_val); break; + case DT_JMPREL: plt = (void*)(iBaseDiff+dynamicTab[j].d_val); break; + case DT_PLTREL: pltType = dynamicTab[j].d_val; break; + case DT_PLTRELSZ: pltSz = dynamicTab[j].d_val; break; + + // --- Relocation --- + case DT_REL: rel = (void*)(iBaseDiff + dynamicTab[j].d_val); break; + case DT_RELSZ: relSz = dynamicTab[j].d_val; break; + case DT_RELENT: relEntSz = dynamicTab[j].d_val; break; + + case DT_RELA: rela = (void*)(iBaseDiff + dynamicTab[j].d_val); break; + case DT_RELASZ: relaSz = dynamicTab[j].d_val; break; + case DT_RELAENT: relaEntSz = dynamicTab[j].d_val; break; + } + } + + // Parse Relocation Entries + if(rel && relSz) + { + j = relSz / relEntSz; + for( i = 0; i < j; i++ ) + { + ptr = (void*)(iBaseDiff + rel[i].r_offset); + if( !Elf_Int_DoRelocate(rel[i].r_info, ptr, *ptr, dynsymtab, (Uint)Base) ) { + bFailed = 1; + } + } + } + // Parse Relocation Entries + if(rela && relaSz) + { + j = relaSz / relaEntSz; + for( i = 0; i < j; i++ ) + { + ptr = (void*)(iBaseDiff + rela[i].r_offset); + if( !Elf_Int_DoRelocate(rela[i].r_info, ptr, rela[i].r_addend, dynsymtab, (Uint)Base) ) { + bFailed = 1; + } + } + } + + // === Process PLT (Procedure Linkage Table) === + if(plt && pltSz) + { + if(pltType == DT_REL) + { + Elf32_Rel *pltRel = plt; + j = pltSz / sizeof(Elf32_Rel); + LOG("PLT Rel - plt = %p, pltSz = %i (%i ents)", plt, pltSz, j); + for(i = 0; i < j; i++) + { + ptr = (void*)(iBaseDiff + pltRel[i].r_offset); + if( !Elf_Int_DoRelocate(pltRel[i].r_info, ptr, *ptr, dynsymtab, (Uint)Base) ) { + bFailed = 1; + } + } + } + else + { + Elf32_Rela *pltRela = plt; + j = pltSz / sizeof(Elf32_Rela); + LOG("PLT RelA - plt = %p, pltSz = %i (%i ents)", plt, pltSz, j); + for(i=0;imisc.HashTable; + symtab = (void *) hdr->misc.SymTable; + + nbuckets = pBuckets[0]; + iSymCount = pBuckets[1]; + pBuckets = &pBuckets[2]; + pChains = &pBuckets[ nbuckets ]; + + // Get hash + iNameHash = Elf_Int_HashString(Name); + iNameHash %= nbuckets; + + // Check Bucket + i = pBuckets[ iNameHash ]; + if(symtab[i].shndx != SHN_UNDEF && strcmp(symtab[i].name, Name) == 0) { + if(ret) *ret = symtab[ i ].value; + return 1; + } + + // Walk Chain + while(pChains[i] != STN_UNDEF) + { + i = pChains[i]; + if(symtab[i].shndx != SHN_UNDEF && strcmp(symtab[ i ].name, Name) == 0) { + if(ret) *ret = symtab[ i ].value; + return 1; + } + } + return 0; +} + +/** + * \fn Uint Elf_Int_HashString(char *str) + * \brief Hash a string in the ELF format + * \param str String to hash + * \return Hash value + */ +Uint Elf_Int_HashString(const char *str) +{ + Uint h = 0, g; + while(*str) + { + h = (h << 4) + *str++; + if( (g = h & 0xf0000000) ) + h ^= g >> 24; + h &= ~g; + } + return h; +} diff --git a/KernelLand/Kernel/bin/elf.h b/KernelLand/Kernel/bin/elf.h new file mode 100644 index 00000000..13b5373f --- /dev/null +++ b/KernelLand/Kernel/bin/elf.h @@ -0,0 +1,287 @@ +/** + * \file elf.h + * \brief ELF Exeutable Loader + */ +#ifndef _BIN_ELF_H +#define _BIN_ELF_H + +typedef Uint16 Elf64_Half; +typedef Uint32 Elf64_Word; +typedef Uint64 Elf64_Addr; +typedef Uint64 Elf64_Off; +typedef Uint64 Elf64_Xword; + +enum e_ident_values +{ + EI_MAG0, + EI_MAG1, + EI_MAG2, + EI_MAG3, + EI_CLASS, + EI_DATA, + EI_VERSION, + EI_OSABI, + EI_ABIVERSION, + EI_PAD, + EI_NIDENT = 16, +}; + +#define ELFCLASS32 1 +#define ELFCLASS64 2 + +/** + * \brief ELF File Header + */ +struct sElf32_Ehdr +{ + union { + char ident[16]; //!< Identifier Bytes + struct { + Uint Ident1; + Uint Ident2; + Uint HashTable; + Uint SymTable; + } misc; + }; + Uint16 filetype; //!< File Type + Uint16 machine; //!< Machine / Arch + Uint32 version; //!< Version (File?) + Uint32 entrypoint; //!< Entry Point + Uint32 phoff; //!< Program Header Offset + Uint32 shoff; //!< Section Header Offset + Uint32 flags; //!< Flags + Uint16 headersize; //!< Header Size + Uint16 phentsize; //!< Program Header Entry Size + Uint16 phentcount; //!< Program Header Entry Count + Uint16 shentsize; //!< Section Header Entry Size + Uint16 shentcount; //!< Section Header Entry Count + Uint16 shstrindex; //!< Section Header String Table Index +} __attribute__ ((packed)); + +typedef struct +{ + unsigned char e_ident[16]; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +/** + * \brief Executable Types + */ +enum eElf32_ExecTypes +{ + ET_NONE = 0, //!< NULL Type + ET_REL = 1, //!< Relocatable (Object) + ET_EXEC = 2, //!< Executable + ET_DYN = 3, //!< Dynamic Library + ET_CORE = 4, //!< Core? + ET_LOPROC = 0xFF00, //!< Low Impl Defined + ET_HIPROC = 0xFFFF //!< High Impl Defined +}; + +/** + \name Section IDs + \{ +*/ +#define SHN_UNDEF 0 //!< Undefined Section +#define SHN_LORESERVE 0xFF00 //!< Low Reserved +#define SHN_LOPROC 0xFF00 //!< Low Impl Defined +#define SHN_HIPROC 0xFF1F //!< High Impl Defined +#define SHN_ABS 0xFFF1 //!< Absolute Address (Base: 0, Size: -1) +#define SHN_COMMON 0xFFF2 //!< Common +#define SHN_HIRESERVE 0xFFFF //!< High Reserved +//! \} + +/** + \enum eElfSectionTypes + \brief ELF Section Types +*/ +enum eElfSectionTypes { + SHT_NULL, //0 + SHT_PROGBITS, //1 + SHT_SYMTAB, //2 + SHT_STRTAB, //3 + SHT_RELA, //4 + SHT_HASH, //5 + SHT_DYNAMIC, //6 + SHT_NOTE, //7 + SHT_NOBITS, //8 + SHT_REL, //9 + SHT_SHLIB, //A + SHT_DYNSYM, //B + SHT_LAST, //C + SHT_LOPROC = 0x70000000, + SHT_HIPROC = 0x7fffffff, + SHT_LOUSER = 0x80000000, + SHT_HIUSER = 0xffffffff +}; + +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 + +struct sElf32_Shent { + Uint32 name; + Uint32 type; + Uint32 flags; + Uint32 address; + Uint32 offset; + Uint32 size; + Uint32 link; + Uint32 info; + Uint32 addralign; + Uint32 entsize; +} __attribute__ ((packed)); //sizeof = 40 + +typedef struct +{ + Elf64_Word sh_name; + Elf64_Word sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +} Elf64_Shdr; + +struct elf_sym_s { + union { + Uint32 nameOfs; + char *name; + }; + Uint32 value; //Address + Uint32 size; + Uint8 info; + Uint8 other; + Uint16 shndx; +} __attribute__ ((packed)); +#define STN_UNDEF 0 // Undefined Symbol + +enum { + PT_NULL, //0 + PT_LOAD, //1 + PT_DYNAMIC, //2 + PT_INTERP, //3 + PT_NOTE, //4 + PT_SHLIB, //5 + PT_PHDR, //6 + PT_LOPROC = 0x70000000, + PT_HIPROC = 0x7fffffff +}; + +struct sElf32_Phdr { + Uint32 Type; + Uint32 Offset; + Uint32 VAddr; + Uint32 PAddr; + Uint32 FileSize; + Uint32 MemSize; + Uint32 Flags; + Uint32 Align; +} __attribute__ ((packed)); + +typedef struct +{ + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} Elf64_Phdr; + +#define PF_X 1 +#define PF_W 2 +#define PF_R 4 + +struct elf32_rel_s { + Uint32 r_offset; + Uint32 r_info; +} __attribute__ ((packed)); + +struct elf32_rela_s { + Uint32 r_offset; + Uint32 r_info; + Sint32 r_addend; +} __attribute__ ((packed)); + +enum { + R_386_NONE = 0, // none + R_386_32, // S+A + R_386_PC32, // S+A-P + R_386_GOT32, // G+A-P + R_386_PLT32, // L+A-P + R_386_COPY, // none + R_386_GLOB_DAT, // S + R_386_JMP_SLOT, // S + R_386_RELATIVE, // B+A + R_386_GOTOFF, // S+A-GOT + R_386_GOTPC, // GOT+A-P + R_386_LAST // none +}; + +#define ELF32_R_SYM(i) ((i)>>8) // Takes an info value and returns a symbol index +#define ELF32_R_TYPE(i) ((i)&0xFF) // Takes an info value and returns a type +#define ELF32_R_INFO(s,t) (((s)<<8)+((t)&0xFF)) // Takes a type and symbol index and returns an info value + +struct elf32_dyn_s { + Uint32 d_tag; + Uint32 d_val; //Also d_ptr +} __attribute__ ((packed)); + +enum { + DT_NULL, //!< Marks End of list + DT_NEEDED, //!< Offset in strtab to needed library + DT_PLTRELSZ, //!< Size in bytes of PLT + DT_PLTGOT, //!< Address of PLT/GOT + DT_HASH, //!< Address of symbol hash table + DT_STRTAB, //!< String Table address + DT_SYMTAB, //!< Symbol Table address + DT_RELA, //!< Relocation table address + DT_RELASZ, //!< Size of relocation table + DT_RELAENT, //!< Size of entry in relocation table + DT_STRSZ, //!< Size of string table + DT_SYMENT, //!< Size of symbol table entry + DT_INIT, //!< Address of initialisation function + DT_FINI, //!< Address of termination function + DT_SONAME, //!< String table offset of so name + DT_RPATH, //!< String table offset of library path + DT_SYMBOLIC,//!< Reverse order of symbol searching for library, search libs first then executable + DT_REL, //!< Relocation Entries (Elf32_Rel instead of Elf32_Rela) + DT_RELSZ, //!< Size of above table (bytes) + DT_RELENT, //!< Size of entry in above table + DT_PLTREL, //!< Relocation entry of PLT + DT_DEBUG, //!< Debugging Entry - Unknown contents + DT_TEXTREL, //!< Indicates that modifcations to a non-writeable segment may occur + DT_JMPREL, //!< Address of PLT only relocation entries + DT_LOPROC = 0x70000000, //!< Low Definable + DT_HIPROC = 0x7FFFFFFF //!< High Definable +}; + +typedef struct sElf32_Ehdr Elf32_Ehdr; +typedef struct sElf32_Phdr Elf32_Phdr; +typedef struct sElf32_Shent Elf32_Shent; +typedef struct elf_sym_s elf_symtab; +typedef struct elf_sym_s Elf32_Sym; +typedef struct elf32_rel_s Elf32_Rel; +typedef struct elf32_rela_s Elf32_Rela; +typedef struct elf32_dyn_s Elf32_Dyn; + +#endif // defined(_EXE_ELF_H) diff --git a/KernelLand/Kernel/bin/pe.c b/KernelLand/Kernel/bin/pe.c new file mode 100644 index 00000000..0e7d1acd --- /dev/null +++ b/KernelLand/Kernel/bin/pe.c @@ -0,0 +1,221 @@ +/* + * Acess v1 + * Portable Executable Loader + */ +#define DEBUG 1 +#include +#include +#include +#include "pe.h" + +// === PROTOTYPES === + int PE_Install(char **Arguments); +tBinary *PE_Load(int fp); +tBinary *MZ_Open(int fp); + int PE_Relocate(void *Base); + int PE_GetSymbol(void *Base, const char *Name, Uint *Ret); + +// === GLOBALS === +MODULE_DEFINE(0, 0x0032, BinPE, PE_Install, NULL, NULL); +const char *gsPE_DefaultInterpreter = "/Acess/Libs/ld-acess.so"; +tBinaryType gPE_Loader = { + NULL, + ('M'|('Z'<<8)), 0xFFFF, // 'MZ' + "PE/DOS", + PE_Load, PE_Relocate, PE_GetSymbol + }; + +// === CODE === +int PE_Install(char **Arguments) +{ + Binary_RegisterType(&gPE_Loader); + return MODULE_ERR_OK; +} + +/** + * \brief Loads a PE Binary + */ +tBinary *PE_Load(int FP) +{ + int count, i, j; + int iSectCount; + tBinary *ret; + tPE_DOS_HEADER dosHdr; + tPE_IMAGE_HEADERS peHeaders; + tPE_SECTION_HEADER *peSections; + char namebuf[9] = {0}; + Uint iFlags, iVA; + + ENTER("xFP", FP); + + // Read DOS header and check + VFS_Read(FP, sizeof(tPE_DOS_HEADER), &dosHdr); + if( dosHdr.Ident != ('M'|('Z'<<8)) ) { + LEAVE('n'); + return NULL; + } + + // - Read PE Header + VFS_Seek(FP, dosHdr.PeHdrOffs, SEEK_SET); + if( VFS_Tell(FP) != dosHdr.PeHdrOffs ) { + ret = MZ_Open(FP); + LEAVE('p', ret); + return ret; + } + VFS_Read(FP, sizeof(tPE_IMAGE_HEADERS), &peHeaders); + + // - Check PE Signature and pass on to the MZ Loader if invalid + if( peHeaders.Signature != (('P')|('E'<<8)) ) { + ret = MZ_Open(FP); + LEAVE('p', ret); + return ret; + } + + // Read Sections (Uses `count` as a temp variable) + count = sizeof(tPE_SECTION_HEADER) * peHeaders.FileHeader.SectionCount; + peSections = malloc( count ); + if(!peSections) + { + Warning("PE_Load - Unable to allocate `peSections`, 0x%x bytes", count); + LEAVE('n'); + return NULL; + } + VFS_Read(FP, count, peSections); + + // Count Pages + iSectCount = 1; // 1st page is headers + for( i = 0; i < peHeaders.FileHeader.SectionCount; i++ ) + { + // Check if the section is loadable + // (VA is zero in non-loadable sections) + if(peSections[i].RVA + peHeaders.OptHeader.ImageBase == 0) continue; + + // Moar pages + iSectCount ++; + } + + LOG("%i Sections", iSectCount); + + // Initialise Executable Information + ret = malloc(sizeof(tBinary) + sizeof(tBinarySection)*iSectCount); + + ret->Entry = peHeaders.OptHeader.EntryPoint + peHeaders.OptHeader.ImageBase; + ret->Base = peHeaders.OptHeader.ImageBase; + ret->Interpreter = gsPE_DefaultInterpreter; + ret->NumSections = iSectCount; + + LOG("Entry=%p, BaseAddress=0x%x\n", ret->Entry, ret->Base); + + ret->LoadSections[0].Virtual = peHeaders.OptHeader.ImageBase; + ret->LoadSections[0].Offset = 0; + ret->LoadSections[0].FileSize = 4096; + ret->LoadSections[0].MemSize = 4096; + ret->LoadSections[0].Flags = 0; + + // Parse Sections + j = 1; // Page Index + for( i = 0; i < peHeaders.FileHeader.SectionCount; i++ ) + { + tBinarySection *sect = &ret->LoadSections[j]; + iVA = peSections[i].RVA + peHeaders.OptHeader.ImageBase; + + // Skip non-loadable sections + if(iVA == 0) continue; + + // Create Name Buffer + memcpy(namebuf, peSections[i].Name, 8); + LOG("Section %i '%s', iVA = %p", i, namebuf, iVA); + + // Create Flags + iFlags = 0; + if(peSections[i].Flags & PE_SECTION_FLAG_MEM_EXECUTE) + iFlags |= BIN_SECTFLAG_EXEC; + if( !(peSections[i].Flags & PE_SECTION_FLAG_MEM_WRITE) ) + iFlags |= BIN_SECTFLAG_RO; + + sect->Virtual = iVA; + sect->Offset = peSections[i].RawOffs; + sect->FileSize = peSections[i].RawSize; + sect->MemSize = peSections[i].VirtualSize; + sect->Flags = iFlags; + j ++; + + LOG("%i Name:'%s', RVA: 0x%x, Size: 0x%x (0x%x), Ofs: 0x%x, Flags: 0x%x", + i, namebuf, + iVA, + peSections[i].VirtualSize, peSections[i].RawSize, peSections[i].RawOffs, + peSections[i].Flags + ); + + } + // Free Executable Memory + free(peSections); + + LEAVE('p', ret); + return ret; +} + +/** + */ +tBinary *MZ_Open(int FP) +{ + ENTER("xFP", FP); + UNIMPLEMENTED(); + LEAVE('n'); + return NULL; +} + +int PE_Relocate(void *Base) +{ + tPE_DOS_HEADER *dosHdr; + tPE_IMAGE_HEADERS *peHeaders; + tPE_SECTION_HEADER *peSections; + tPE_DATA_DIR *directory; + tPE_IMPORT_DIR *impDir; + int i; + Uint iBase = (Uint)Base; + #if 0 + void *hLibrary; + char *libPath; + #endif + + ENTER("pBase", Base); + dosHdr = Base; + peHeaders = (void*)( iBase + dosHdr->PeHdrOffs ); + LOG("Prefered Base %p", peHeaders->OptHeader.ImageBase); + peSections = (void*)( iBase + sizeof(tPE_IMAGE_HEADERS) ); + + directory = (void*)(peSections[0].RVA + iBase); + + // === Load Import Tables + impDir = (void*)( directory[PE_DIR_IMPORT].RVA + iBase ); + for( i = 0; impDir[i].DLLName != NULL; i++ ) + { + impDir[i].DLLName += iBase; + impDir[i].ImportLookupTable += iBase/4; + impDir[i].ImportAddressTable += iBase/4; + LOG("DLL Required '%s'(0x%x)", impDir[i].DLLName, impDir[i].DLLName); + #if 0 + libPath = FindLibrary(impDir[i].DLLName); + if(libPath == NULL) + { + Warning("PE_Relocate - Unable to find library '%s'"); + LEAVE('i', -1); + return -1; + } + LOG("DLL Path = '%s'", libPath); + hLibrary = DynLib_Load(libPath, 0); + #endif + } + + for(i=0;i +#include +#include +#include +#include + +// === CONSTANTS === +#define BIN_LOWEST MM_USER_MIN // 1MiB +#define BIN_GRANUALITY 0x10000 // 64KiB +#define BIN_HIGHEST (USER_LIB_MAX-BIN_GRANUALITY) // Just below the kernel +#define KLIB_LOWEST MM_MODULE_MIN +#define KLIB_GRANUALITY 0x10000 // 32KiB +#define KLIB_HIGHEST (MM_MODULE_MAX-KLIB_GRANUALITY) + +// === TYPES === +typedef struct sKernelBin { + struct sKernelBin *Next; + void *Base; + tBinary *Info; +} tKernelBin; + +// === IMPORTS === +extern char *Threads_GetName(int ID); +extern tKernelSymbol gKernelSymbols[]; +extern tKernelSymbol gKernelSymbolsEnd[]; +extern tBinaryType gELF_Info; + +// === PROTOTYPES === + int Binary_int_CacheArgs(const char **Path, const char ***ArgV, const char ***EnvP, void *DestBuffer); +tVAddr Binary_Load(const char *Path, tVAddr *EntryPoint); +tBinary *Binary_GetInfo(tMount MountID, tInode InodeID); +tVAddr Binary_MapIn(tBinary *Binary, const char *Path, tVAddr LoadMin, tVAddr LoadMax); +tVAddr Binary_IsMapped(tBinary *Binary); +tBinary *Binary_DoLoad(tMount MountID, tInode Inode, const char *Path); +void Binary_Dereference(tBinary *Info); +#if 0 +Uint Binary_Relocate(void *Base); +#endif +Uint Binary_GetSymbolEx(const char *Name, Uint *Value); +#if 0 +Uint Binary_FindSymbol(void *Base, const char *Name, Uint *Val); +#endif + int Binary_int_CheckMemFree( tVAddr _start, size_t _len ); + +// === GLOBALS === +tShortSpinlock glBinListLock; +tBinary *glLoadedBinaries = NULL; +char **gsaRegInterps = NULL; + int giRegInterps = 0; +tShortSpinlock glKBinListLock; +tKernelBin *glLoadedKernelLibs; +tBinaryType *gRegBinTypes = &gELF_Info; + +// === FUNCTIONS === +/** + * \brief Registers a binary type + */ +int Binary_RegisterType(tBinaryType *Type) +{ + Type->Next = gRegBinTypes; + gRegBinTypes = Type; + return 1; +} + +/** + * \fn int Proc_Spawn(const char *Path) + */ +int Proc_Spawn(const char *Path) +{ + char stackPath[strlen(Path)+1]; + ENTER("sPath", Path); + + strcpy(stackPath, Path); + + LOG("stackPath = '%s'", stackPath); + + if(Proc_Clone(CLONE_VM|CLONE_NOUSER) == 0) + { + // CHILD + const char *args[2] = {stackPath, NULL}; + LOG("stackPath = '%s'", stackPath); + Proc_Execve(stackPath, args, &args[1], 0); + for(;;); + } + LEAVE('i', 0); + return 0; +} + +/** + * \todo Document + */ +int Binary_int_CacheArgs(const char **Path, const char ***ArgV, const char ***EnvP, void *DestBuffer) +{ + int size, argc=0, envc=0; + int i; + char *strbuf; + const char **arrays; + + // Calculate size + size = 0; + if( ArgV && *ArgV ) + { + const char **argv = *ArgV; + for( argc = 0; argv[argc]; argc ++ ) + size += strlen( argv[argc] ) + 1; + } + if( EnvP && *EnvP ) + { + const char **envp = *EnvP; + for( envc = 0; envp[envc]; envc ++ ) + size += strlen( envp[envc] ) + 1; + } + size = (size + sizeof(void*)-1) & ~(sizeof(void*)-1); // Word align + size += (argc+1+envc+1)*sizeof(void*); // Arrays + if( Path ) + { + size += strlen( *Path ) + 1; + } + + if( DestBuffer ) + { + arrays = DestBuffer; + strbuf = (void*)&arrays[argc+1+envc+1]; + + // Fill ArgV + if( ArgV && *ArgV ) + { + const char **argv = *ArgV; + for( i = 0; argv[i]; i ++ ) + { + arrays[i] = strbuf; + strcpy(strbuf, argv[i]); + strbuf += strlen( argv[i] ) + 1; + } + *ArgV = arrays; + arrays += i; + } + *arrays++ = NULL; + // Fill EnvP + if( EnvP && *EnvP ) + { + const char **envp = *EnvP; + for( i = 0; envp[i]; i ++ ) + { + arrays[i] = strbuf; + strcpy(strbuf, envp[i]); + strbuf += strlen( envp[i] ) + 1; + } + *EnvP = arrays; + arrays += i; + } + *arrays++ = NULL; + // Fill path + if( Path ) + { + strcpy(strbuf, *Path); + *Path = strbuf; + } + } + + return size; +} + +/** + * \brief Create a new process with the specified set of file descriptors + */ +int Proc_SysSpawn(const char *Binary, const char **ArgV, const char **EnvP, int nFD, int *FDs) +{ + void *handles; + void *cachebuf; + int size; + tPID ret; + + // --- Save File, ArgV and EnvP + size = Binary_int_CacheArgs( &Binary, &ArgV, &EnvP, NULL ); + cachebuf = malloc( size ); + Binary_int_CacheArgs( &Binary, &ArgV, &EnvP, cachebuf ); + + // Cache the VFS handles + handles = VFS_SaveHandles(nFD, FDs); + + // Create new process + ret = Proc_Clone(CLONE_VM|CLONE_NOUSER); + if( ret == 0 ) + { + VFS_RestoreHandles(nFD, handles); + VFS_FreeSavedHandles(nFD, handles); + // Frees cachebuf + Proc_Execve(Binary, ArgV, EnvP, size); + for(;;); + } + if( ret < 0 ) + { + VFS_FreeSavedHandles(nFD, handles); + } + + return ret; +} + +/** + * \brief Replace the current user image with another + * \param File File to load as the next image + * \param ArgV Arguments to pass to user + * \param EnvP User's environment + * \note Called Proc_ for historical reasons + */ +int Proc_Execve(const char *File, const char **ArgV, const char **EnvP, int DataSize) +{ + void *cachebuf; + tVAddr entry; + Uint base; // Uint because Proc_StartUser wants it + int argc; + + ENTER("sFile pArgV pEnvP", File, ArgV, EnvP); + + // --- Save File, ArgV and EnvP + if( DataSize == 0 ) + { + DataSize = Binary_int_CacheArgs( &File, &ArgV, &EnvP, NULL ); + cachebuf = malloc( DataSize ); + Binary_int_CacheArgs( &File, &ArgV, &EnvP, cachebuf ); + } + + // --- Get argc + for( argc = 0; ArgV && ArgV[argc]; argc ++ ); + + // --- Set Process Name + Threads_SetName(File); + + // --- Clear User Address space + // NOTE: This is a little roundabout, maybe telling ClearUser to not touch the + // PPD area would be a better idea. + { + int nfd = *Threads_GetMaxFD(); + void *handles; + handles = VFS_SaveHandles(nfd, NULL); + VFS_CloseAllUserHandles(); + MM_ClearUser(); + VFS_RestoreHandles(nfd, handles); + VFS_FreeSavedHandles(nfd, handles); + } + + // --- Load new binary + base = Binary_Load(File, &entry); + if(base == 0) + { + Log_Warning("Binary", "Proc_Execve - Unable to load '%s'", File); + LEAVE('-'); + Threads_Exit(0, -10); + for(;;); + } + + LOG("entry = 0x%x, base = 0x%x", entry, base); + LEAVE('-'); + // --- And... Jump to it + Proc_StartUser(entry, base, argc, ArgV, DataSize); + for(;;); // Tell GCC that we never return +} + +/** + * \brief Load a binary into the current address space + * \param Path Path to binary to load + * \param EntryPoint Pointer for exectuable entry point + * \return Virtual address where the binary has been loaded + */ +tVAddr Binary_Load(const char *Path, tVAddr *EntryPoint) +{ + tMount mount_id; + tInode inode; + tBinary *pBinary; + tVAddr base = -1; + + ENTER("sPath pEntryPoint", Path, EntryPoint); + + // Sanity Check Argument + if(Path == NULL) { + LEAVE('x', 0); + return 0; + } + + // Check if this path has been loaded before. + #if 0 + // TODO: Implement a list of string/tBinary pairs for loaded bins + #endif + + // Get Inode + { + int fd; + tFInfo info; + fd = VFS_Open(Path, VFS_OPENFLAG_READ|VFS_OPENFLAG_EXEC); + if( fd == -1 ) { + LOG("%s does not exist", Path); + LEAVE_RET('x', 0); + } + VFS_FInfo(fd, &info, 0); + VFS_Close(fd); + mount_id = info.mount; + inode = info.inode; + LOG("mount_id = %i, inode = %i", mount_id, inode); + } + + // TODO: Also get modifcation time? + + // Check if the binary has already been loaded + if( !(pBinary = Binary_GetInfo(mount_id, inode)) ) + pBinary = Binary_DoLoad(mount_id, inode, Path); // Else load it + + // Error Check + if(pBinary == NULL) { + LEAVE('x', 0); + return 0; + } + + // Map into process space + base = Binary_MapIn(pBinary, Path, BIN_LOWEST, BIN_HIGHEST); + + // Check for errors + if(base == 0) { + LEAVE('x', 0); + return 0; + } + + // Interpret + if(pBinary->Interpreter) { + tVAddr start; + if( Binary_Load(pBinary->Interpreter, &start) == 0 ) { + LEAVE('x', 0); + return 0; + } + *EntryPoint = start; + } + else + *EntryPoint = pBinary->Entry - pBinary->Base + base; + + // Return + LOG("*EntryPoint = 0x%x", *EntryPoint); + LEAVE('x', base); + return base; // Pass the base as an argument to the user if there is an interpreter +} + +/** + * \brief Finds a matching binary entry + * \param MountID Mountpoint ID of binary file + * \param InodeID Inode ID of the file + * \return Pointer to the binary definition (if already loaded) + */ +tBinary *Binary_GetInfo(tMount MountID, tInode InodeID) +{ + tBinary *pBinary; + for(pBinary = glLoadedBinaries; pBinary; pBinary = pBinary->Next) + { + if(pBinary->MountID == MountID && pBinary->Inode == InodeID) + return pBinary; + } + return NULL; +} + +/** + * \brief Maps an already-loaded binary into an address space. + * \param Binary Pointer to globally stored binary definition + * \param Path Path to the binary's file (for debug) + * \param LoadMin Lowest location to map to + * \param LoadMax Highest location to map to + * \return Base load address + */ +tVAddr Binary_MapIn(tBinary *Binary, const char *Path, tVAddr LoadMin, tVAddr LoadMax) +{ + tVAddr base; + int i, fd; + + ENTER("pBinary sPath pLoadMin pLoadMax", Binary, Path, LoadMin, LoadMax); + + // Reference Executable (Makes sure that it isn't unloaded) + Binary->ReferenceCount ++; + + // Get Binary Base + base = Binary->Base; + + // Check if base is free + if(base != 0) + { + LOG("Checking base %p", base); + for( i = 0; i < Binary->NumSections; i ++ ) + { + if( Binary_int_CheckMemFree( Binary->LoadSections[i].Virtual, Binary->LoadSections[i].MemSize ) ) + { + base = 0; + LOG("Address 0x%x is taken\n", Binary->LoadSections[i].Virtual); + break; + } + } + } + + // Check if the executable has no base or it is not free + if(base == 0) + { + // If so, give it a base + base = LoadMax; + while(base >= LoadMin) + { + for( i = 0; i < Binary->NumSections; i ++ ) + { + tVAddr addr = Binary->LoadSections[i].Virtual - Binary->Base + base; + if( Binary_int_CheckMemFree( addr, Binary->LoadSections[i].MemSize ) ) + break; + } + // If space was found, break + if(i == Binary->NumSections) break; + // Else decrement pointer and try again + base -= BIN_GRANUALITY; + } + LOG("Allocated base %p", base); + } + + // Error Check + if(base < LoadMin) { + Log_Warning("Binary", "Executable '%s' cannot be loaded, no space", Path); + LEAVE('i', 0); + return 0; + } + + // Map Executable In + fd = VFS_OpenInode(Binary->MountID, Binary->Inode, VFS_OPENFLAG_READ); + for( i = 0; i < Binary->NumSections; i ++ ) + { + tBinarySection *sect = &Binary->LoadSections[i]; + Uint protflags, mapflags; + tVAddr addr = sect->Virtual - Binary->Base + base; + LOG("%i - %p to offset 0x%llx (%x)", i, addr, sect->Offset, sect->Flags); + + protflags = MMAP_PROT_READ; + mapflags = MMAP_MAP_FIXED; + + if( sect->Flags & BIN_SECTFLAG_EXEC ) + protflags |= MMAP_PROT_EXEC; + // Read only pages are COW + if( sect->Flags & BIN_SECTFLAG_RO ) { + VFS_MMap( (void*)addr, sect->FileSize, protflags, MMAP_MAP_SHARED|mapflags, fd, sect->Offset ); + } + else { + protflags |= MMAP_PROT_WRITE; + VFS_MMap( (void*)addr, sect->FileSize, protflags, MMAP_MAP_PRIVATE|mapflags, fd, sect->Offset ); + } + + // Apply anonymous memory for BSS + if( sect->FileSize < sect->MemSize ) { + mapflags |= MMAP_MAP_ANONYMOUS; + VFS_MMap( + (void*)(addr + sect->FileSize), sect->MemSize - sect->FileSize, + protflags, MMAP_MAP_PRIVATE|mapflags, + 0, 0 + ); + } + } + + Log_Debug("Binary", "PID %i - Mapped '%s' to %p", Threads_GetPID(), Path, base); + VFS_Close(fd); + + LEAVE('p', base); + return base; +} + +#if 0 +/** + * \fn Uint Binary_IsMapped(tBinary *binary) + * \brief Check if a binary is already mapped into the address space + * \param binary Binary information to check + * \return Current Base or 0 + */ +Uint Binary_IsMapped(tBinary *binary) +{ + Uint iBase; + + // Check prefered base + iBase = binary->Base; + if(MM_GetPage( iBase ) == (binary->Pages[0].Physical & ~0xFFF)) + return iBase; + + for(iBase = BIN_HIGHEST; + iBase >= BIN_LOWEST; + iBase -= BIN_GRANUALITY) + { + if(MM_GetPage( iBase ) == (binary->Pages[0].Physical & ~0xFFF)) + return iBase; + } + + return 0; +} +#endif + +/** + * \fn tBinary *Binary_DoLoad(char *truePath) + * \brief Loads a binary file into memory + * \param truePath Absolute filename of binary + */ +tBinary *Binary_DoLoad(tMount MountID, tInode Inode, const char *Path) +{ + tBinary *pBinary; + int fp; + Uint32 ident; + tBinaryType *bt = gRegBinTypes; + + ENTER("iMountID XInode sPath", MountID, Inode, Path); + + // Open File + fp = VFS_OpenInode(MountID, Inode, VFS_OPENFLAG_READ); + if(fp == -1) { + LOG("Unable to load file, access denied"); + LEAVE('n'); + return NULL; + } + + LOG("fp = 0x%x", fp); + + // Read File Type + VFS_Read(fp, 4, &ident); + VFS_Seek(fp, 0, SEEK_SET); + + LOG("ident = 0x%x", ident); + + // Determine the type + for(; bt; bt = bt->Next) + { + if( (ident & bt->Mask) != (Uint32)bt->Ident ) + continue; + LOG("bt = %p (%s)", bt, bt->Name); + pBinary = bt->Load(fp); + break; + } + + // Close File + VFS_Close(fp); + + // Catch errors + if(!bt) { + Log_Warning("Binary", "'%s' is an unknown file type. (%02x %02x %02x %02x)", + Path, ident&0xFF, (ident>>8)&0xFF, (ident>>16)&0xFF, (ident>>24)&0xFF); + LEAVE('n'); + return NULL; + } + + LOG("pBinary = %p", pBinary); + + // Error Check + if(pBinary == NULL) { + LEAVE('n'); + return NULL; + } + + // Initialise Structure + pBinary->ReferenceCount = 0; + pBinary->MountID = MountID; + pBinary->Inode = Inode; + + // Debug Information + LOG("Interpreter: '%s'", pBinary->Interpreter); + LOG("Base: 0x%x, Entry: 0x%x", pBinary->Base, pBinary->Entry); + LOG("NumSections: %i", pBinary->NumSections); + + // Add to the list + SHORTLOCK(&glBinListLock); + pBinary->Next = glLoadedBinaries; + glLoadedBinaries = pBinary; + SHORTREL(&glBinListLock); + + // TODO: Register the path with the binary + + // Return + LEAVE('p', pBinary); + return pBinary; +} + +/** + * \fn void Binary_Unload(void *Base) + * \brief Unload / Unmap a binary + * \param Base Loaded Base + * \note Currently used only for kernel libaries + */ +void Binary_Unload(void *Base) +{ + tKernelBin *pKBin; + tKernelBin *prev = NULL; + int i; + + if((Uint)Base < 0xC0000000) + { + // TODO: User Binaries + Log_Warning("BIN", "Unloading user binaries is currently unimplemented"); + return; + } + + // Kernel Libraries + for(pKBin = glLoadedKernelLibs; + pKBin; + prev = pKBin, pKBin = pKBin->Next) + { + // Check the base + if(pKBin->Base != Base) continue; + // Deallocate Memory + for(i = 0; i < pKBin->Info->NumSections; i++) + { + // TODO: VFS_MUnmap(); + } + // Dereference Binary + Binary_Dereference( pKBin->Info ); + // Remove from list + if(prev) prev->Next = pKBin->Next; + else glLoadedKernelLibs = pKBin->Next; + // Free Kernel Lib + free(pKBin); + return; + } +} + +/** + * \fn void Binary_Dereference(tBinary *Info) + * \brief Dereferences and if nessasary, deletes a binary + * \param Info Binary information structure + */ +void Binary_Dereference(tBinary *Info) +{ + // Decrement reference count + Info->ReferenceCount --; + + // Check if it is still in use + if(Info->ReferenceCount) return; + + /// \todo Implement binary freeing +} + +/** + * \fn char *Binary_RegInterp(char *Path) + * \brief Registers an Interpreter + * \param Path Path to interpreter provided by executable + */ +char *Binary_RegInterp(char *Path) +{ + int i; + // NULL Check Argument + if(Path == NULL) return NULL; + // NULL Check the array + if(gsaRegInterps == NULL) + { + giRegInterps = 1; + gsaRegInterps = malloc( sizeof(char*) ); + gsaRegInterps[0] = malloc( strlen(Path) ); + strcpy(gsaRegInterps[0], Path); + return gsaRegInterps[0]; + } + + // Scan Array + for( i = 0; i < giRegInterps; i++ ) + { + if(strcmp(gsaRegInterps[i], Path) == 0) + return gsaRegInterps[i]; + } + + // Interpreter is not in list + giRegInterps ++; + gsaRegInterps = malloc( sizeof(char*)*giRegInterps ); + gsaRegInterps[i] = malloc( strlen(Path) ); + strcpy(gsaRegInterps[i], Path); + return gsaRegInterps[i]; +} + +// ============ +// Kernel Binary Handling +// ============ +/** + * \fn void *Binary_LoadKernel(const char *File) + * \brief Load a binary into kernel space + * \note This function shares much with #Binary_Load, but does it's own mapping + * \param File File to load into the kernel + */ +void *Binary_LoadKernel(const char *File) +{ + tBinary *pBinary; + tKernelBin *pKBinary; + tVAddr base = -1; + tMount mount_id; + tInode inode; + + ENTER("sFile", File); + + // Sanity Check Argument + if(File == NULL) { + LEAVE('n'); + return 0; + } + + { + int fd = VFS_Open(File, VFS_OPENFLAG_READ); + tFInfo info; + if(fd == -1) { + LEAVE('n'); + return NULL; + } + VFS_FInfo(fd, &info, 0); + mount_id = info.mount; + inode = info.inode; + VFS_Close(fd); + } + + // Check if the binary has already been loaded + if( (pBinary = Binary_GetInfo(mount_id, inode)) ) + { + for(pKBinary = glLoadedKernelLibs; + pKBinary; + pKBinary = pKBinary->Next ) + { + if(pKBinary->Info == pBinary) { + LEAVE('p', pKBinary->Base); + return pKBinary->Base; + } + } + } + else + pBinary = Binary_DoLoad(mount_id, inode, File); // Else load it + + // Error Check + if(pBinary == NULL) { + LEAVE('n'); + return NULL; + } + + // -------------- + // Now pBinary is valid (either freshly loaded or only user mapped) + // So, map it into kernel space + // -------------- + + // Reference Executable (Makes sure that it isn't unloaded) + pBinary->ReferenceCount ++; + + Binary_MapIn(pBinary, File, KLIB_LOWEST, KLIB_HIGHEST); + + // Relocate Library + if( !Binary_Relocate( (void*)base ) ) + { + Log_Warning("Binary", "Relocation of '%s' failed, unloading", File); + Binary_Unload( (void*)base ); + Binary_Dereference( pBinary ); + LEAVE('n'); + return 0; + } + + // Add to list (relocator must look at itself manually, not via Binary_GetSymbol) + pKBinary = malloc(sizeof(*pKBinary)); + pKBinary->Base = (void*)base; + pKBinary->Info = pBinary; + SHORTLOCK( &glKBinListLock ); + pKBinary->Next = glLoadedKernelLibs; + glLoadedKernelLibs = pKBinary; + SHORTREL( &glKBinListLock ); + + LEAVE('p', base); + return (void*)base; +} + +/** + * \fn Uint Binary_Relocate(void *Base) + * \brief Relocates a loaded binary (used by kernel libraries) + * \param Base Loaded base address of binary + * \return Boolean Success + */ +Uint Binary_Relocate(void *Base) +{ + Uint32 ident = *(Uint32*) Base; + tBinaryType *bt = gRegBinTypes; + + for(; bt; bt = bt->Next) + { + if( (ident & bt->Mask) == (Uint)bt->Ident ) + return bt->Relocate( (void*)Base); + } + + Log_Warning("BIN", "%p is an unknown file type. (%02x %02x %02x %02x)", + Base, ident&0xFF, (ident>>8)&0xFF, (ident>>16)&0xFF, (ident>>24)&0xFF); + return 0; +} + +/** + * \fn int Binary_GetSymbol(char *Name, Uint *Val) + * \brief Get a symbol value + * \return Value of symbol or -1 on error + * + * Gets the value of a symbol from either the currently loaded + * libraries or the kernel's exports. + */ +int Binary_GetSymbol(const char *Name, Uint *Val) +{ + if( Binary_GetSymbolEx(Name, Val) ) return 1; + return 0; +} + +/** + * \fn Uint Binary_GetSymbolEx(char *Name, Uint *Value) + * \brief Get a symbol value + * + * Gets the value of a symbol from either the currently loaded + * libraries or the kernel's exports. + */ +Uint Binary_GetSymbolEx(const char *Name, Uint *Value) +{ + int i; + tKernelBin *pKBin; + int numKSyms = ((Uint)&gKernelSymbolsEnd-(Uint)&gKernelSymbols)/sizeof(tKernelSymbol); + + // Scan Kernel + for( i = 0; i < numKSyms; i++ ) + { + if(strcmp(Name, gKernelSymbols[i].Name) == 0) { + *Value = gKernelSymbols[i].Value; + return 1; + } + } + + // Scan Loaded Libraries + for(pKBin = glLoadedKernelLibs; + pKBin; + pKBin = pKBin->Next ) + { + if( Binary_FindSymbol(pKBin->Base, Name, Value) ) { + return 1; + } + } + + Log_Warning("BIN", "Unable to find symbol '%s'", Name); + return 0; +} + +/** + * \fn Uint Binary_FindSymbol(void *Base, char *Name, Uint *Val) + * \brief Get a symbol from the specified library + * \param Base Base address + * \param Name Name of symbol to find + * \param Val Pointer to place final value + */ +Uint Binary_FindSymbol(void *Base, const char *Name, Uint *Val) +{ + Uint32 ident = *(Uint32*) Base; + tBinaryType *bt = gRegBinTypes; + + for(; bt; bt = bt->Next) + { + if( (ident & bt->Mask) == (Uint)bt->Ident ) + return bt->GetSymbol(Base, Name, Val); + } + + Log_Warning("BIN", "Binary_FindSymbol - %p is an unknown file type. (%02x %02x %02x %02x)", + Base, ident&0xFF, ident>>8, ident>>16, ident>>24); + return 0; +} + +/** + * \brief Check if a range of memory is fully free + * \return Inverse boolean free (0 if all pages are unmapped) + */ +int Binary_int_CheckMemFree( tVAddr _start, size_t _len ) +{ + _len += _start & (PAGE_SIZE-1); + _len = (_len + PAGE_SIZE - 1) & ~(PAGE_SIZE-1); + _start &= ~(PAGE_SIZE-1); + for( ; _len > PAGE_SIZE; _len -= PAGE_SIZE, _start += PAGE_SIZE ) { + if( MM_GetPhysAddr(_start) != 0 ) + return 1; + } + if( _len == PAGE_SIZE && MM_GetPhysAddr(_start) != 0 ) + return 1; + return 0; +} + + +// === EXPORTS === +EXPORT(Binary_FindSymbol); +EXPORT(Binary_Unload); diff --git a/KernelLand/Kernel/debug.c b/KernelLand/Kernel/debug.c new file mode 100644 index 00000000..355624d5 --- /dev/null +++ b/KernelLand/Kernel/debug.c @@ -0,0 +1,432 @@ +/* + * AcessOS Microkernel Version + * debug.c + * + * TODO: Move the Debug_putchar methods out to the arch/ tree + */ +#include +#include + +#define DEBUG_MAX_LINE_LEN 256 + +#define LOCK_DEBUG_OUTPUT 1 + +#define TRACE_TO_KTERM 0 + +// === IMPORTS === +extern void Threads_Dump(void); +extern void KernelPanic_SetMode(void); +extern void KernelPanic_PutChar(char Ch); + +// === PROTOTYPES === +static void Debug_Putchar(char ch); +static void Debug_Puts(int bUseKTerm, const char *Str); +void Debug_DbgOnlyFmt(const char *format, va_list args); +void Debug_FmtS(int bUseKTerm, const char *format, ...); +void Debug_Fmt(int bUseKTerm, const char *format, va_list args); +void Debug_SetKTerminal(const char *File); + +// === GLOBALS === + int gDebug_Level = 0; + int giDebug_KTerm = -1; + int gbDebug_IsKPanic = 0; +volatile int gbInPutChar = 0; +#if LOCK_DEBUG_OUTPUT +tShortSpinlock glDebug_Lock; +#endif + +// === CODE === +static void Debug_Putchar(char ch) +{ + Debug_PutCharDebug(ch); + if( !gbDebug_IsKPanic ) + { + if(gbInPutChar) return ; + gbInPutChar = 1; + if(giDebug_KTerm != -1) + VFS_Write(giDebug_KTerm, 1, &ch); + gbInPutChar = 0; + } + else + KernelPanic_PutChar(ch); +} + +static void Debug_Puts(int UseKTerm, const char *Str) +{ + int len = 0; + + Debug_PutStringDebug(Str); + + if( gbDebug_IsKPanic ) + { + for( len = 0; Str[len]; len ++ ) + KernelPanic_PutChar( Str[len] ); + } + else + for( len = 0; Str[len]; len ++ ); + + // Output to the kernel terminal + if( UseKTerm && !gbDebug_IsKPanic && giDebug_KTerm != -1) + { + if(gbInPutChar) return ; + gbInPutChar = 1; + VFS_Write(giDebug_KTerm, len, Str); + gbInPutChar = 0; + } +} + +void Debug_DbgOnlyFmt(const char *format, va_list args) +{ + Debug_Fmt(0, format, args); +} + +void Debug_Fmt(int bUseKTerm, const char *format, va_list args) +{ + char buf[DEBUG_MAX_LINE_LEN]; + int len; + buf[DEBUG_MAX_LINE_LEN-1] = 0; + len = vsnprintf(buf, DEBUG_MAX_LINE_LEN-1, format, args); + //if( len < DEBUG_MAX_LINE ) + // do something + Debug_Puts(bUseKTerm, buf); + return ; +} + +void Debug_FmtS(int bUseKTerm, const char *format, ...) +{ + va_list args; + va_start(args, format); + Debug_Fmt(bUseKTerm, format, args); + va_end(args); +} + +void Debug_KernelPanic(void) +{ + #if LOCK_DEBUG_OUTPUT + SHORTREL(&glDebug_Lock); + #endif + gbDebug_IsKPanic = 1; + KernelPanic_SetMode(); +} + +/** + * \fn void LogF(const char *Msg, ...) + * \brief Raw debug log (no new line, no prefix) + */ +void LogF(const char *Fmt, ...) +{ + va_list args; + + #if LOCK_DEBUG_OUTPUT + SHORTLOCK(&glDebug_Lock); + #endif + + va_start(args, Fmt); + + Debug_Fmt(1, Fmt, args); + + va_end(args); + + #if LOCK_DEBUG_OUTPUT + SHORTREL(&glDebug_Lock); + #endif +} +/** + * \fn void Debug(const char *Msg, ...) + * \brief Print only to the debug channel (not KTerm) + */ +void Debug(const char *Fmt, ...) +{ + va_list args; + + #if LOCK_DEBUG_OUTPUT + SHORTLOCK(&glDebug_Lock); + #endif + + Debug_Puts(0, "Debug: "); + va_start(args, Fmt); + Debug_DbgOnlyFmt(Fmt, args); + va_end(args); + Debug_PutCharDebug('\r'); + Debug_PutCharDebug('\n'); + #if LOCK_DEBUG_OUTPUT + SHORTREL(&glDebug_Lock); + #endif +} +/** + * \fn void Log(const char *Msg, ...) + */ +void Log(const char *Fmt, ...) +{ + va_list args; + + #if LOCK_DEBUG_OUTPUT + SHORTLOCK(&glDebug_Lock); + #endif + + Debug_Puts(1, "Log: "); + va_start(args, Fmt); + Debug_Fmt(1, Fmt, args); + va_end(args); + Debug_Puts(1, "\r\n"); + + #if LOCK_DEBUG_OUTPUT + SHORTREL(&glDebug_Lock); + #endif +} +void Warning(const char *Fmt, ...) +{ + va_list args; + + #if LOCK_DEBUG_OUTPUT + SHORTLOCK(&glDebug_Lock); + #endif + + Debug_Puts(1, "Warning: "); + va_start(args, Fmt); + Debug_Fmt(1, Fmt, args); + va_end(args); + Debug_Putchar('\r'); + Debug_Putchar('\n'); + + #if LOCK_DEBUG_OUTPUT + SHORTREL(&glDebug_Lock); + #endif +} +void Panic(const char *Fmt, ...) +{ + va_list args; + + #if LOCK_DEBUG_OUTPUT + SHORTLOCK(&glDebug_Lock); + #endif + // And never SHORTREL + + Debug_KernelPanic(); + + Debug_Puts(1, "Panic: "); + va_start(args, Fmt); + Debug_Fmt(1, Fmt, args); + va_end(args); + Debug_Putchar('\r'); + Debug_Putchar('\n'); + + Threads_Dump(); + + for(;;) ; +} + +void Debug_SetKTerminal(const char *File) +{ + int tmp; + if(giDebug_KTerm != -1) { + tmp = giDebug_KTerm; + giDebug_KTerm = -1; + VFS_Close(tmp); + } + tmp = VFS_Open(File, VFS_OPENFLAG_WRITE); +// Log_Log("Debug", "Opened '%s' as 0x%x", File, tmp); + giDebug_KTerm = tmp; +// Log_Log("Debug", "Returning to %p", __builtin_return_address(0)); +} + +void Debug_Enter(const char *FuncName, const char *ArgTypes, ...) +{ + va_list args; + int i; + int pos; + tTID tid = Threads_GetTID(); + + #if LOCK_DEBUG_OUTPUT + SHORTLOCK(&glDebug_Lock); + #endif + + i = gDebug_Level ++; + + va_start(args, ArgTypes); + + Debug_FmtS(TRACE_TO_KTERM, "%014lli ", now()); + while(i--) Debug_Puts(TRACE_TO_KTERM, " "); + + Debug_Puts(TRACE_TO_KTERM, FuncName); + Debug_FmtS(TRACE_TO_KTERM, "[%i]", tid); + Debug_Puts(TRACE_TO_KTERM, ": ("); + + while(*ArgTypes) + { + pos = strpos(ArgTypes, ' '); + if(pos == -1 || pos > 1) { + if(pos == -1) + Debug_Puts(TRACE_TO_KTERM, ArgTypes+1); + else { + Debug_FmtS(TRACE_TO_KTERM, "%.*s", pos-1, ArgTypes+1); + } + Debug_Puts(TRACE_TO_KTERM, "="); + } + switch(*ArgTypes) + { + case 'p': Debug_FmtS(TRACE_TO_KTERM, "%p", va_arg(args, void*)); break; + case 'P': Debug_FmtS(TRACE_TO_KTERM, "%P", va_arg(args, tPAddr)); break; + case 's': Debug_FmtS(TRACE_TO_KTERM, "'%s'", va_arg(args, char*)); break; + case 'i': Debug_FmtS(TRACE_TO_KTERM, "%i", va_arg(args, int)); break; + case 'u': Debug_FmtS(TRACE_TO_KTERM, "%u", va_arg(args, Uint)); break; + case 'x': Debug_FmtS(TRACE_TO_KTERM, "0x%x", va_arg(args, Uint)); break; + case 'b': Debug_FmtS(TRACE_TO_KTERM, "0b%b", va_arg(args, Uint)); break; + case 'X': Debug_FmtS(TRACE_TO_KTERM, "0x%llx", va_arg(args, Uint64)); break; // Extended (64-Bit) + case 'B': Debug_FmtS(TRACE_TO_KTERM, "0b%llb", va_arg(args, Uint64)); break; // Extended (64-Bit) + } + if(pos != -1) { + Debug_Puts(TRACE_TO_KTERM, ", "); + } + + if(pos == -1) break; + ArgTypes = &ArgTypes[pos+1]; + } + + va_end(args); + Debug_Puts(TRACE_TO_KTERM, ")\r\n"); + + #if LOCK_DEBUG_OUTPUT + SHORTREL(&glDebug_Lock); + #endif +} + +void Debug_Log(const char *FuncName, const char *Fmt, ...) +{ + va_list args; + int i = gDebug_Level; + tTID tid = Threads_GetTID(); + + #if LOCK_DEBUG_OUTPUT + SHORTLOCK(&glDebug_Lock); + #endif + + Debug_FmtS(TRACE_TO_KTERM, "%014lli ", now()); + while(i--) Debug_Puts(TRACE_TO_KTERM, " "); + + Debug_Puts(TRACE_TO_KTERM, FuncName); + Debug_FmtS(TRACE_TO_KTERM, "[%i]", tid); + Debug_Puts(TRACE_TO_KTERM, ": "); + + va_start(args, Fmt); + Debug_Fmt(TRACE_TO_KTERM, Fmt, args); + va_end(args); + + Debug_Puts(TRACE_TO_KTERM, "\r\n"); + + #if LOCK_DEBUG_OUTPUT + SHORTREL(&glDebug_Lock); + #endif +} + +void Debug_Leave(const char *FuncName, char RetType, ...) +{ + va_list args; + int i; + tTID tid = Threads_GetTID(); + + #if LOCK_DEBUG_OUTPUT + SHORTLOCK(&glDebug_Lock); + #endif + + i = --gDebug_Level; + + va_start(args, RetType); + + if( i == -1 ) { + gDebug_Level = 0; + i = 0; + } + Debug_FmtS(TRACE_TO_KTERM, "%014lli ", now()); + // Indenting + while(i--) Debug_Puts(TRACE_TO_KTERM, " "); + + Debug_Puts(TRACE_TO_KTERM, FuncName); + Debug_FmtS(TRACE_TO_KTERM, "[%i]", tid); + Debug_Puts(TRACE_TO_KTERM, ": RETURN"); + + // No Return + if(RetType == '-') { + Debug_Puts(TRACE_TO_KTERM, "\r\n"); + #if LOCK_DEBUG_OUTPUT + SHORTREL(&glDebug_Lock); + #endif + return; + } + + switch(RetType) + { + case 'n': Debug_Puts(TRACE_TO_KTERM, " NULL"); break; + case 'p': Debug_Fmt(TRACE_TO_KTERM, " %p", args); break; + case 'P': Debug_Fmt(TRACE_TO_KTERM, " %P", args); break; // PAddr + case 's': Debug_Fmt(TRACE_TO_KTERM, " '%s'", args); break; + case 'i': Debug_Fmt(TRACE_TO_KTERM, " %i", args); break; + case 'u': Debug_Fmt(TRACE_TO_KTERM, " %u", args); break; + case 'x': Debug_Fmt(TRACE_TO_KTERM, " 0x%x", args); break; + // Extended (64-Bit) + case 'X': Debug_Fmt(TRACE_TO_KTERM, " 0x%llx", args); break; + } + Debug_Puts(TRACE_TO_KTERM, "\r\n"); + + va_end(args); + + #if LOCK_DEBUG_OUTPUT + SHORTREL(&glDebug_Lock); + #endif +} + +void Debug_HexDump(const char *Header, const void *Data, Uint Length) +{ + const Uint8 *cdat = Data; + Uint pos = 0; + LogF("%014lli ", now()); + Debug_Puts(1, Header); + LogF(" (Hexdump of %p)\r\n", Data); + + #define CH(n) ((' '<=cdat[(n)]&&cdat[(n)]<0x7F) ? cdat[(n)] : '.') + + while(Length >= 16) + { + LogF("%014lli Log: %04x:" + " %02x %02x %02x %02x %02x %02x %02x %02x" + " %02x %02x %02x %02x %02x %02x %02x %02x" + " %c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c\r\n", + now(), + pos, + cdat[ 0], cdat[ 1], cdat[ 2], cdat[ 3], cdat[ 4], cdat[ 5], cdat[ 6], cdat[ 7], + cdat[ 8], cdat[ 9], cdat[10], cdat[11], cdat[12], cdat[13], cdat[14], cdat[15], + CH(0), CH(1), CH(2), CH(3), CH(4), CH(5), CH(6), CH(7), + CH(8), CH(9), CH(10), CH(11), CH(12), CH(13), CH(14), CH(15) + ); + Length -= 16; + cdat += 16; + pos += 16; + } + + { + int i ; + LogF("%014lli Log: %04x: ", now(), pos); + for(i = 0; i < Length; i ++) + { + LogF("%02x ", cdat[i]); + } + for( ; i < 16; i ++) LogF(" "); + LogF(" "); + for(i = 0; i < Length; i ++) + { + if( i == 8 ) LogF(" "); + LogF("%c", CH(i)); + } + + Debug_Putchar('\r'); + Debug_Putchar('\n'); + } +} + +// --- EXPORTS --- +EXPORT(Debug); +EXPORT(Log); +EXPORT(Warning); +EXPORT(Debug_Enter); +EXPORT(Debug_Log); +EXPORT(Debug_Leave); diff --git a/KernelLand/Kernel/drv/Makefile b/KernelLand/Kernel/drv/Makefile new file mode 100644 index 00000000..44f17cde --- /dev/null +++ b/KernelLand/Kernel/drv/Makefile @@ -0,0 +1,20 @@ +# Acess2 Module/Driver Templater Makefile +# Makefile.tpl + +-include ../../Makefile.cfg + +CPPFLAGS = -I../include -I../arch/$(ARCHDIR)/include -DARCH=$(ARCH) -DBUILD_MODULE +CFLAGS = -Wall -Werror $(CPPFLAGS) + +.PHONY: all clean + +all: bochsvbe.kmd + +%.kmd: %.o + $(CC) -shared -nostdlib -o $@ $< + +%.o: %.c + $(CC) $(CFLAGS) -o $@ -c $< + +#ata_x86.kmd: ata_x86.o +#bochsvbe.kmd: bochsvbe.o diff --git a/KernelLand/Kernel/drv/fifo.c b/KernelLand/Kernel/drv/fifo.c new file mode 100644 index 00000000..0779a60b --- /dev/null +++ b/KernelLand/Kernel/drv/fifo.c @@ -0,0 +1,436 @@ +/* AcessOS + * FIFO Pipe Driver + */ +#define DEBUG 0 +#include +#include +#include +#include + +// === CONSTANTS === +#define DEFAULT_RING_SIZE 2048 +#define PF_BLOCKING 1 + +// === TYPES === +typedef struct sPipe { + struct sPipe *Next; + char *Name; + tVFS_Node Node; + Uint Flags; + int ReadPos; + int WritePos; + int BufSize; + char *Buffer; +} tPipe; + +// === PROTOTYPES === + int FIFO_Install(char **Arguments); + int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data); +char *FIFO_ReadDir(tVFS_Node *Node, int Id); +tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename); + int FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags); +void FIFO_Reference(tVFS_Node *Node); +void FIFO_Close(tVFS_Node *Node); + int FIFO_Relink(tVFS_Node *Node, const char *OldName, const char *NewName); +Uint64 FIFO_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 FIFO_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); +tPipe *FIFO_Int_NewPipe(int Size, const char *Name); + +// === GLOBALS === +MODULE_DEFINE(0, 0x0032, FIFO, FIFO_Install, NULL, NULL); +tVFS_NodeType gFIFO_DirNodeType = { + .TypeName = "FIFO Dir Node", + .ReadDir = FIFO_ReadDir, + .FindDir = FIFO_FindDir, + .MkNod = FIFO_MkNod, + .Relink = FIFO_Relink, + .IOCtl = FIFO_IOCtl +}; +tVFS_NodeType gFIFO_PipeNodeType = { + .TypeName = "FIFO Pipe Node", + .Read = FIFO_Read, + .Write = FIFO_Write, + .Close = FIFO_Close, + .Reference = FIFO_Reference +}; +tDevFS_Driver gFIFO_DriverInfo = { + NULL, "fifo", + { + .Size = 1, + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRW, + .Flags = VFS_FFLAG_DIRECTORY, + .Type = &gFIFO_DirNodeType + } +}; +tVFS_Node gFIFO_AnonNode = { + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRW, + }; +tPipe *gFIFO_NamedPipes = NULL; + +// === CODE === +/** + * \fn int FIFO_Install(char **Options) + * \brief Installs the FIFO Driver + */ +int FIFO_Install(char **Options) +{ + DevFS_AddDevice( &gFIFO_DriverInfo ); + return MODULE_ERR_OK; +} + +/** + * \fn int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data) + */ +int FIFO_IOCtl(tVFS_Node *Node, int Id, void *Data) +{ + return 0; +} + +/** + * \fn char *FIFO_ReadDir(tVFS_Node *Node, int Id) + * \brief Reads from the FIFO root + */ +char *FIFO_ReadDir(tVFS_Node *Node, int Id) +{ + tPipe *tmp = gFIFO_NamedPipes; + + // Entry 0 is Anon Pipes + if(Id == 0) return strdup("anon"); + + // Find the id'th node + while(--Id && tmp) tmp = tmp->Next; + // If node found, return it + if(tmp) return strdup(tmp->Name); + // else error return + return NULL; +} + +/** + * \fn tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename) + * \brief Find a file in the FIFO root + * \note Creates an anon pipe if anon is requested + */ +tVFS_Node *FIFO_FindDir(tVFS_Node *Node, const char *Filename) +{ + tPipe *tmp; + if(!Filename) return NULL; + + // NULL String Check + if(Filename[0] == '\0') return NULL; + + // Anon Pipe + if(Filename[0] == 'a' && Filename[1] == 'n' + && Filename[2] == 'o' && Filename[3] == 'n' + && Filename[4] == '\0') { + tmp = FIFO_Int_NewPipe(DEFAULT_RING_SIZE, "anon"); + return &tmp->Node; + } + + // Check Named List + tmp = gFIFO_NamedPipes; + while(tmp) + { + if(strcmp(tmp->Name, Filename) == 0) + return &tmp->Node; + tmp = tmp->Next; + } + return NULL; +} + +/** + * \fn int FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags) + */ +int FIFO_MkNod(tVFS_Node *Node, const char *Name, Uint Flags) +{ + return 0; +} + +void FIFO_Reference(tVFS_Node *Node) +{ + if(!Node->ImplPtr) return ; + + Node->ReferenceCount ++; +} + +/** + * \fn void FIFO_Close(tVFS_Node *Node) + * \brief Close a FIFO end + */ +void FIFO_Close(tVFS_Node *Node) +{ + tPipe *pipe; + if(!Node->ImplPtr) return ; + + Node->ReferenceCount --; + if(Node->ReferenceCount) return ; + + pipe = Node->ImplPtr; + + if(strcmp(pipe->Name, "anon") == 0) { + Log_Debug("FIFO", "Pipe %p closed", Node->ImplPtr); + free(Node->ImplPtr); + return ; + } + + return ; +} + +/** + * \fn int FIFO_Relink(tVFS_Node *Node, const char *OldName, const char *NewName) + * \brief Relink a file (Deletes named pipes) + */ +int FIFO_Relink(tVFS_Node *Node, const char *OldName, const char *NewName) +{ + tPipe *pipe, *tmp; + + if(Node != &gFIFO_DriverInfo.RootNode) return 0; + + // Can't relink anon + if(strcmp(OldName, "anon")) return 0; + + // Find node + for(pipe = gFIFO_NamedPipes; + pipe; + pipe = pipe->Next) + { + if(strcmp(pipe->Name, OldName) == 0) + break; + } + if(!pipe) return 0; + + // Relink a named pipe + if(NewName) { + // Check new name + for(tmp = gFIFO_NamedPipes; + tmp; + tmp = tmp->Next) + { + if(strcmp(tmp->Name, NewName) == 0) return 0; + } + // Create new name + free(pipe->Name); + pipe->Name = malloc(strlen(NewName)+1); + strcpy(pipe->Name, NewName); + return 1; + } + + // Unlink the pipe + if(Node->ImplPtr) { + free(Node->ImplPtr); + return 1; + } + + return 0; +} + +/** + * \fn Uint64 FIFO_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Read from a fifo pipe + */ +Uint64 FIFO_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tPipe *pipe = Node->ImplPtr; + Uint len; + Uint remaining = Length; + + if(!pipe) return 0; + + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + + while(remaining) + { + // Wait for buffer to fill + if(pipe->Flags & PF_BLOCKING) + { + if( pipe->ReadPos == pipe->WritePos ) + VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "FIFO_Read"); + + } + else + { + if(pipe->ReadPos == pipe->WritePos) + { + VFS_MarkAvaliable(Node, 0); + LEAVE('i', 0); + return 0; + } + } + + len = remaining; + if( pipe->ReadPos < pipe->WritePos ) + { + int avail_bytes = pipe->WritePos - pipe->ReadPos; + if( avail_bytes < remaining ) len = avail_bytes; + } + else + { + int avail_bytes = pipe->WritePos + pipe->BufSize - pipe->ReadPos; + if( avail_bytes < remaining ) len = avail_bytes; + } + + LOG("len = %i, remaining = %i", len, remaining); + + // Check if read overflows buffer + if(len > pipe->BufSize - pipe->ReadPos) + { + int ofs = pipe->BufSize - pipe->ReadPos; + memcpy(Buffer, &pipe->Buffer[pipe->ReadPos], ofs); + memcpy((Uint8*)Buffer + ofs, &pipe->Buffer, len-ofs); + } + else + { + memcpy(Buffer, &pipe->Buffer[pipe->ReadPos], len); + } + + // Increment read position + pipe->ReadPos += len; + pipe->ReadPos %= pipe->BufSize; + + // Mark some flags + if( pipe->ReadPos == pipe->WritePos ) { + VFS_MarkAvaliable(Node, 0); + } + VFS_MarkFull(Node, 0); // Buffer can't still be full + + // Decrement Remaining Bytes + remaining -= len; + // Increment Buffer address + Buffer = (Uint8*)Buffer + len; + + // TODO: Option to read differently + LEAVE('i', len); + return len; + } + + LEAVE('i', Length); + return Length; + +} + +/** + * \fn Uint64 FIFO_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Write to a fifo pipe + */ +Uint64 FIFO_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + tPipe *pipe = Node->ImplPtr; + Uint len; + Uint remaining = Length; + + if(!pipe) return 0; + + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + + while(remaining) + { + // Wait for buffer to empty + if(pipe->Flags & PF_BLOCKING) { + if( pipe->ReadPos == (pipe->WritePos+1)%pipe->BufSize ) + VFS_SelectNode(Node, VFS_SELECT_WRITE, NULL, "FIFO_Write"); + + len = remaining; + if( pipe->ReadPos > pipe->WritePos ) + { + int rem_space = pipe->ReadPos - pipe->WritePos; + if(rem_space < remaining) len = rem_space; + } + else + { + int rem_space = pipe->ReadPos + pipe->BufSize - pipe->WritePos; + if(rem_space < remaining) len = rem_space; + } + } + else + { + if(pipe->ReadPos == (pipe->WritePos+1)%pipe->BufSize) + { + LEAVE('i', 0); + return 0; + } + // Write buffer + if(pipe->ReadPos - pipe->WritePos < remaining) + len = pipe->ReadPos - pipe->WritePos; + else + len = remaining; + } + + // Check if write overflows buffer + if(len > pipe->BufSize - pipe->WritePos) + { + int ofs = pipe->BufSize - pipe->WritePos; + memcpy(&pipe->Buffer[pipe->WritePos], Buffer, ofs); + memcpy(&pipe->Buffer, (Uint8*)Buffer + ofs, len-ofs); + } + else + { + memcpy(&pipe->Buffer[pipe->WritePos], Buffer, len); + } + + // Increment read position + pipe->WritePos += len; + pipe->WritePos %= pipe->BufSize; + + // Mark some flags + if( pipe->ReadPos == pipe->WritePos ) { + VFS_MarkFull(Node, 1); // Buffer full + } + VFS_MarkAvaliable(Node, 1); + + // Decrement Remaining Bytes + remaining -= len; + // Increment Buffer address + Buffer = (Uint8*)Buffer + len; + } + + LEAVE('i', Length); + return Length; +} + +// --- HELPERS --- +/** + * \fn tPipe *FIFO_Int_NewPipe(int Size, const char *Name) + * \brief Create a new pipe + */ +tPipe *FIFO_Int_NewPipe(int Size, const char *Name) +{ + tPipe *ret; + int namelen = strlen(Name) + 1; + int allocsize = sizeof(tPipe) + sizeof(tVFS_ACL) + Size + namelen; + + ret = calloc(1, allocsize); + if(!ret) return NULL; + + // Clear Return + ret->Flags = PF_BLOCKING; + + // Allocate Buffer + ret->BufSize = Size; + ret->Buffer = (void*)( (Uint)ret + sizeof(tPipe) + sizeof(tVFS_ACL) ); + + // Set name (and FIFO name) + ret->Name = ret->Buffer + Size; + strcpy(ret->Name, Name); + // - Start empty, max of `Size` + //Semaphore_Init( &ret->Semaphore, 0, Size, "FIFO", ret->Name ); + + // Set Node + ret->Node.ReferenceCount = 1; + ret->Node.Size = 0; + ret->Node.ImplPtr = ret; + ret->Node.UID = Threads_GetUID(); + ret->Node.GID = Threads_GetGID(); + ret->Node.NumACLs = 1; + ret->Node.ACLs = (void*)( (Uint)ret + sizeof(tPipe) ); + ret->Node.ACLs->Group = 0; + ret->Node.ACLs->ID = ret->Node.UID; + ret->Node.ACLs->Inv = 0; + ret->Node.ACLs->Perms = -1; + ret->Node.CTime + = ret->Node.MTime + = ret->Node.ATime = now(); + ret->Node.Type = &gFIFO_PipeNodeType; + + return ret; +} diff --git a/KernelLand/Kernel/drv/iocache.c b/KernelLand/Kernel/drv/iocache.c new file mode 100644 index 00000000..8f735419 --- /dev/null +++ b/KernelLand/Kernel/drv/iocache.c @@ -0,0 +1,355 @@ +/* + * Acess2 Kernel + * - IO Cache + * + * By thePowersGang (John Hodge) + * + * TODO: Convert to use spare physical pages instead + */ +#define DEBUG 0 +#include +#include + +// === TYPES === +typedef struct sIOCache_Ent tIOCache_Ent; +typedef struct sIOCache_PageInfo tIOCache_PageInfo; + +// === STRUCTURES === +struct sIOCache_Ent +{ + tIOCache_Ent *Next; + Uint64 Num; + Sint64 LastAccess; + Sint64 LastWrite; + Uint8 Data[]; +}; + +struct sIOCache_PageInfo +{ + tIOCache_PageInfo *GlobalNext; + tIOCache_PageInfo *CacheNext; + tIOCache *Owner; + tPAddr BasePhys; + Uint64 BaseOffset; +}; + +struct sIOCache +{ + tIOCache *Next; + int SectorSize; + tMutex Lock; + int Mode; + Uint32 ID; + tIOCache_WriteCallback Write; + int CacheSize; + int CacheUsed; + tIOCache_Ent *Entries; +}; + +// === GLOBALS === +tShortSpinlock glIOCache_Caches; +tIOCache *gIOCache_Caches = NULL; + int giIOCache_NumCaches = 0; +tIOCache_PageInfo *gIOCache_GlobalPages; + +// === CODE === +/** + * \fn tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize ) + * \brief Creates a new IO Cache + */ +tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize ) +{ + tIOCache *ret = calloc( 1, sizeof(tIOCache) ); + + // Sanity Check + if(!ret) return NULL; + + // Fill Structure + ret->SectorSize = SectorSize; + ret->Mode = IOCACHE_WRITEBACK; + ret->ID = ID; + ret->Write = Write; + ret->CacheSize = CacheSize; + + // Append to list + SHORTLOCK( &glIOCache_Caches ); + ret->Next = gIOCache_Caches; + gIOCache_Caches = ret; + SHORTREL( &glIOCache_Caches ); + + // Return + return ret; +} + +/** + * \fn int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer ) + * \brief Read from a cached sector + */ +int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer ) +{ + + ENTER("pCache XSector pBuffer", Cache, Sector, Buffer); + + // Sanity Check! + if(!Cache || !Buffer) { + LEAVE('i', -1); + return -1; + } + + // Lock + Mutex_Acquire( &Cache->Lock ); + if(Cache->CacheSize == 0) { + Mutex_Release( &Cache->Lock ); + LEAVE('i', -1); + return -1; + } + + #if IOCACHE_USE_PAGES + tIOCache_PageInfo *page; + size_t offset = (Sector*Cache->SectorSize) % PAGE_SIZE; + Uint64 wanted_base = (Sector*Cache->SectorSize) & ~(PAGE_SIZE-1); + for( page = Cache->Pages; page; page = page->CacheNext ) + { + void *tmp; + if(page->BaseOffset < WantedBase) continue; + if(page->BaseOffset > WantedBase) break; + tmp = MM_MapTemp( page->BasePhys ); + memcpy( Buffer, tmp + offset, Cache->SectorSize ); + MM_FreeTemp( tmp ); + } + #else + tIOCache_Ent *ent; + // Search the list + for( ent = Cache->Entries; ent; ent = ent->Next ) + { + // Have we found what we are looking for? + if( ent->Num == Sector ) { + memcpy(Buffer, ent->Data, Cache->SectorSize); + ent->LastAccess = now(); + Mutex_Release( &Cache->Lock ); + LEAVE('i', 1); + return 1; + } + // It's a sorted list, so as soon as we go past `Sector` we know + // it's not there + if(ent->Num > Sector) break; + } + #endif + + Mutex_Release( &Cache->Lock ); + LEAVE('i', 0); + return 0; +} + +/** + * \fn int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer ) + * \brief Cache a sector + */ +int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer ) +{ + tIOCache_Ent *ent, *prev; + tIOCache_Ent *new; + tIOCache_Ent *oldest = NULL, *oldestPrev; + + // Sanity Check! + if(!Cache || !Buffer) + return -1; + + // Lock + Mutex_Acquire( &Cache->Lock ); + if(Cache->CacheSize == 0) { + Mutex_Release( &Cache->Lock ); + return -1; + } + + // Search the list + prev = (tIOCache_Ent*)&Cache->Entries; + for( ent = Cache->Entries; ent; prev = ent, ent = ent->Next ) + { + // Is it already here? + if( ent->Num == Sector ) { + Mutex_Release( &Cache->Lock ); + return 0; + } + + // Check if we have found the oldest entry + if( !oldest || oldest->LastAccess > ent->LastAccess ) { + oldest = ent; + oldestPrev = prev; + } + + // Here we go! + if(ent->Num > Sector) + break; + } + + // Create the new entry + new = malloc( sizeof(tIOCache_Ent) + Cache->SectorSize ); + new->Next = ent; + new->Num = Sector; + new->LastAccess = now(); + new->LastWrite = 0; // Zero is special, it means unmodified + memcpy(new->Data, Buffer, Cache->SectorSize); + + // Have we reached the maximum cached entries? + if( Cache->CacheUsed == Cache->CacheSize ) + { + tIOCache_Ent *savedPrev = prev; + oldestPrev = (tIOCache_Ent*)&Cache->Entries; + // If so, search for the least recently accessed entry + for( ; ent; prev = ent, ent = ent->Next ) + { + // Check if we have found the oldest entry + if( !oldest || oldest->LastAccess > ent->LastAccess ) { + oldest = ent; + oldestPrev = prev; + } + } + if( !oldest ) { + Log_Error("IOCache", "Cache full, but also empty"); + return -1; + } + // Remove from list, write back and free + oldestPrev->Next = oldest->Next; + if(oldest->LastWrite && Cache->Mode != IOCACHE_VIRTUAL) + Cache->Write(Cache->ID, oldest->Num, oldest->Data); + free(oldest); + + // Decrement the used count + Cache->CacheUsed --; + + // Restore `prev` + prev = savedPrev; + } + + // Append to list + prev->Next = new; + Cache->CacheUsed ++; + + // Release Spinlock + Mutex_Release( &Cache->Lock ); + + // Return success + return 1; +} + +/** + * \fn int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer ) + * \brief Read from a cached sector + */ +int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer ) +{ + tIOCache_Ent *ent; + + // Sanity Check! + if(!Cache || !Buffer) + return -1; + // Lock + Mutex_Acquire( &Cache->Lock ); + if(Cache->CacheSize == 0) { + Mutex_Release( &Cache->Lock ); + return -1; + } + + // Search the list + for( ent = Cache->Entries; ent; ent = ent->Next ) + { + // Have we found what we are looking for? + if( ent->Num == Sector ) { + memcpy(ent->Data, Buffer, Cache->SectorSize); + ent->LastAccess = ent->LastWrite = now(); + + if(Cache->Mode == IOCACHE_WRITEBACK) { + Cache->Write(Cache->ID, Sector, Buffer); + ent->LastWrite = 0; + } + + Mutex_Release( &Cache->Lock ); + return 1; + } + // It's a sorted list, so as soon as we go past `Sector` we know + // it's not there + if(ent->Num > Sector) break; + } + + Mutex_Release( &Cache->Lock ); + return 0; +} + +/** + * \fn void IOCache_Flush( tIOCache *Cache ) + * \brief Flush a cache + */ +void IOCache_Flush( tIOCache *Cache ) +{ + tIOCache_Ent *ent; + + if( Cache->Mode == IOCACHE_VIRTUAL ) return; + + // Lock + Mutex_Acquire( &Cache->Lock ); + if(Cache->CacheSize == 0) { + Mutex_Release( &Cache->Lock ); + return; + } + + // Write All + for( ent = Cache->Entries; ent; ent = ent->Next ) + { + Cache->Write(Cache->ID, ent->Num, ent->Data); + ent->LastWrite = 0; + } + + Mutex_Release( &Cache->Lock ); +} + +/** + * \fn void IOCache_Destroy( tIOCache *Cache ) + * \brief Destroy a cache + */ +void IOCache_Destroy( tIOCache *Cache ) +{ + tIOCache_Ent *ent, *prev = NULL; + + // Lock + Mutex_Acquire( &Cache->Lock ); + if(Cache->CacheSize == 0) { + Mutex_Release( &Cache->Lock ); + return; + } + + // Free All + for(ent = Cache->Entries; + ent; + prev = ent, ent = ent->Next, free(prev) ) + { + if( Cache->Mode != IOCACHE_VIRTUAL ) + { + Cache->Write(Cache->ID, ent->Num, ent->Data); + ent->LastWrite = 0; + } + } + + Cache->CacheSize = 0; + + Mutex_Release( &Cache->Lock ); + + // Remove from list + SHORTLOCK( &glIOCache_Caches ); + { + tIOCache *cache; + tIOCache *prev_cache = (tIOCache*)&gIOCache_Caches; + for(cache = gIOCache_Caches; + cache; + prev_cache = cache, cache = cache->Next ) + { + if(cache == Cache) { + prev_cache->Next = cache->Next; + break; + } + } + } + SHORTREL( &glIOCache_Caches ); + + free(Cache); +} diff --git a/KernelLand/Kernel/drv/pci.c b/KernelLand/Kernel/drv/pci.c new file mode 100644 index 00000000..8745080b --- /dev/null +++ b/KernelLand/Kernel/drv/pci.c @@ -0,0 +1,500 @@ +/* + * AcessOS/AcessBasic v0.1 + * PCI Bus Driver + */ +#define DEBUG 0 +#include +#include +#include +#include +#include +#include + +#define LIST_DEVICES 1 + +// === STRUCTURES === +typedef struct sPCIDevice +{ + Uint16 bus, slot, fcn; + Uint16 vendor, device; + Uint32 class; // Class:Subclass:ProgIf + Uint8 revision; + Uint32 ConfigCache[256/4]; + char Name[8]; + tVFS_Node Node; +} tPCIDevice; + +// === CONSTANTS === +#define SPACE_STEP 5 +#define MAX_RESERVED_PORT 0xD00 + +// === PROTOTYPES === + int PCI_Install(char **Arguments); + int PCI_ScanBus(int ID, int bFill); + +char *PCI_int_ReadDirRoot(tVFS_Node *node, int pos); +tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename); +Uint32 PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset); +Uint64 PCI_int_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer); + int PCI_int_EnumDevice(Uint16 bus, Uint16 dev, Uint16 fcn, tPCIDevice *info); + +// === GLOBALS === +MODULE_DEFINE(0, 0x0100, PCI, PCI_Install, NULL, NULL); + int giPCI_BusCount = 1; + int giPCI_InodeHandle = -1; + int giPCI_DeviceCount = 0; +tPCIDevice *gPCI_Devices = NULL; +tVFS_NodeType gPCI_RootNodeType = { + .TypeName = "PCI Root Node", + .ReadDir = PCI_int_ReadDirRoot, + .FindDir = PCI_int_FindDirRoot +}; +tVFS_NodeType gPCI_DevNodeType = { + .TypeName = "PCI Dev Node", + .Read = PCI_int_ReadDevice +}; +tDevFS_Driver gPCI_DriverStruct = { + NULL, "pci", + { + .Flags = VFS_FFLAG_DIRECTORY, + .Size = -1, + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Type = &gPCI_RootNodeType + } +}; +Uint32 *gaPCI_PortBitmap = NULL; +Uint32 gaPCI_BusBitmap[256/32]; + +// === CODE === +/** + * \brief Scan the PCI Bus for devices + * \param Arguments Boot-time parameters + */ +int PCI_Install(char **Arguments) +{ + int i; + void *tmpPtr; + + // Build Portmap + gaPCI_PortBitmap = malloc( 1 << 13 ); + if( !gaPCI_PortBitmap ) { + Log_Error("PCI", "Unable to allocate %i bytes for bitmap", 1 << 13); + return MODULE_ERR_MALLOC; + } + memset( gaPCI_PortBitmap, 0, 1 << 13 ); + for( i = 0; i < MAX_RESERVED_PORT / 32; i ++ ) + gaPCI_PortBitmap[i] = -1; + for( i = 0; i < MAX_RESERVED_PORT % 32; i ++ ) + gaPCI_PortBitmap[MAX_RESERVED_PORT / 32] = 1 << i; + + // Scan Bus (Bus 0, Don't fill gPCI_Devices) + i = PCI_ScanBus(0, 0); + if(i != MODULE_ERR_OK) return i; + + if(giPCI_DeviceCount == 0) { + Log_Notice("PCI", "No devices were found"); + return MODULE_ERR_NOTNEEDED; + } + + // Allocate device buffer + tmpPtr = malloc(giPCI_DeviceCount * sizeof(tPCIDevice)); + if(tmpPtr == NULL) { + Log_Warning("PCI", "Malloc ERROR"); + return MODULE_ERR_MALLOC; + } + gPCI_Devices = tmpPtr; + + Log_Log("PCI", "%i devices, filling structure", giPCI_DeviceCount); + + // Reset counts + giPCI_DeviceCount = 0; + giPCI_BusCount = 0; + memset(gaPCI_BusBitmap, 0, sizeof(gaPCI_BusBitmap)); + // Rescan, filling the PCI device array + PCI_ScanBus(0, 1); + + // Complete Driver Structure + gPCI_DriverStruct.RootNode.Size = giPCI_DeviceCount; + + // And add to DevFS + DevFS_AddDevice(&gPCI_DriverStruct); + + return MODULE_ERR_OK; +} + +/** + * \brief Scans a specific PCI Bus + * \param BusID PCI Bus ID to scan + * \param bFill Fill the \a gPCI_Devices array? + */ +int PCI_ScanBus(int BusID, int bFill) +{ + int dev, fcn; + tPCIDevice devInfo; + + if( gaPCI_BusBitmap[BusID/32] & (1 << (BusID%32)) ) + return MODULE_ERR_OK; + + gaPCI_BusBitmap[BusID/32] |= (1 << (BusID%32)); + + for( dev = 0; dev < 32; dev++ ) // 32 Devices per bus + { + for( fcn = 0; fcn < 8; fcn++ ) // Max 8 functions per device + { + // Check if the device/function exists + if(!PCI_int_EnumDevice(BusID, dev, fcn, &devInfo)) + continue; + + if(devInfo.class == PCI_OC_PCIBRIDGE) + { + #if LIST_DEVICES + if( !bFill ) + Log_Log("PCI", "Bridge @ %i,%i:%i (0x%x:0x%x)", + BusID, dev, fcn, devInfo.vendor, devInfo.device); + #endif + //TODO: Handle PCI-PCI Bridges + //PCI_ScanBus(devInfo.???, bFill); + giPCI_BusCount ++; + } + else + { + #if LIST_DEVICES + if( !bFill ) + Log_Log("PCI", "Device %i,%i:%i %06x => 0x%04x:0x%04x", + BusID, dev, fcn, devInfo.class, devInfo.vendor, devInfo.device); + #endif + } + + if( bFill ) { + devInfo.Node.Inode = giPCI_DeviceCount; + memcpy(&gPCI_Devices[giPCI_DeviceCount], &devInfo, sizeof(tPCIDevice)); + } + giPCI_DeviceCount ++; + + // If bit 23 of (soemthing) is set, there are sub-functions + if(fcn == 0 && !(devInfo.ConfigCache[3] & 0x00800000) ) + break; + } + } + + return MODULE_ERR_OK; +} + +/** + * \brief Read from Root of PCI Driver +*/ +char *PCI_int_ReadDirRoot(tVFS_Node *Node, int Pos) +{ + ENTER("pNode iPos", Node, Pos); + if(Pos < 0 || Pos >= giPCI_DeviceCount) { + LEAVE('n'); + return NULL; + } + + LEAVE('s', gPCI_Devices[Pos].Name); + return strdup( gPCI_Devices[Pos].Name ); +} +/** + */ +tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename) +{ + int bus,slot,fcn; + int i; + // Validate Filename (Pointer and length) + if(!filename || strlen(filename) != 7) + return NULL; + // Check for spacers + if(filename[2] != '.' || filename[5] != ':') + return NULL; + + // Get Information + if(filename[0] < '0' || filename[0] > '9') return NULL; + bus = (filename[0] - '0')*10; + if(filename[1] < '0' || filename[1] > '9') return NULL; + bus += filename[1] - '0'; + if(filename[3] < '0' || filename[3] > '9') return NULL; + slot = (filename[3] - '0')*10; + if(filename[4] < '0' || filename[4] > '9') return NULL; + slot += filename[4] - '0'; + if(filename[6] < '0' || filename[6] > '9') return NULL; + fcn = filename[6] - '0'; + + // Find Match + for(i=0;i 256 ) return 0; + + memcpy( + buffer, + (char*)gPCI_Devices[node->Inode].ConfigCache + pos, + length); + + return length; +} + +// --- Kernel Code Interface --- +/** + * \brief Counts the devices with the specified codes + * \param vendor Vendor ID + * \param device Device ID + */ +int PCI_CountDevices(Uint16 vendor, Uint16 device) +{ + int i, ret=0; + for(i=0;i= giPCI_DeviceCount) return 1; + + if(Vendor) *Vendor = dev->vendor; + if(Device) *Device = dev->device; + if(Class) *Class = dev->class; + return 0; +} + +int PCI_GetDeviceVersion(tPCIDev ID, Uint8 *Revision) +{ + tPCIDevice *dev = &gPCI_Devices[ID]; + if(ID < 0 || ID >= giPCI_DeviceCount) return 1; + + if(Revision) *Revision = dev->revision; + return 0; +} + +int PCI_GetDeviceSubsys(tPCIDev ID, Uint16 *SubsystemVendor, Uint16 *SubsystemID) +{ + tPCIDevice *dev = &gPCI_Devices[ID]; + if(ID < 0 || ID >= giPCI_DeviceCount) return 1; + + if(SubsystemVendor) *SubsystemVendor = dev->ConfigCache[0x2c/4] & 0xFFFF; + if(SubsystemID) *SubsystemID = dev->ConfigCache[0x2c/4] >> 16; + + return 0; +} + +Uint32 PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset) +{ + Bus &= 0xFF; + Slot &= 0x1F; + Fcn &= 7; + Offset &= 0xFC; + return ((Uint32)Bus << 16) | (Slot << 11) | (Fcn << 8) | (Offset & 0xFC); +} + +Uint32 PCI_ConfigRead(tPCIDev ID, int Offset, int Size) +{ + tPCIDevice *dev; + Uint32 dword, addr; + + if( ID < 0 || ID >= giPCI_DeviceCount ) return 0; + if( Offset < 0 || Offset > 256 ) return 0; + + // TODO: Should I support non-aligned reads? + if( Offset & (Size - 1) ) return 0; + + dev = &gPCI_Devices[ID]; + addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset); + + dword = PCI_CfgReadDWord(addr); + gPCI_Devices[ID].ConfigCache[Offset/4] = dword; + switch( Size ) + { + case 1: return (dword >> (8 * (Offset&3))) & 0xFF; + case 2: return (dword >> (8 * (Offset&2))) & 0xFFFF; + case 4: return dword; + default: + return 0; + } +} + +void PCI_ConfigWrite(tPCIDev ID, int Offset, int Size, Uint32 Value) +{ + tPCIDevice *dev; + Uint32 dword, addr; + int shift; + if( ID < 0 || ID >= giPCI_DeviceCount ) return ; + if( Offset < 0 || Offset > 256 ) return ; + + dev = &gPCI_Devices[ID]; + addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset); + + if(Size != 4) + dword = PCI_CfgReadDWord(addr); + switch(Size) + { + case 1: + shift = (Offset&3)*8; + dword &= ~(0xFF << shift); + dword |= Value << shift; + break; + case 2: + shift = (Offset&2)*8; + dword &= ~(0xFFFF << shift); + dword |= Value << shift; + break; + case 4: + dword = Value; + break; + default: + return; + } + PCI_CfgWriteDWord(addr, dword); +} + +/** + * \brief Get the IRQ assigned to a device + */ +Uint8 PCI_GetIRQ(tPCIDev id) +{ + if(id < 0 || id >= giPCI_DeviceCount) + return 0; + return gPCI_Devices[id].ConfigCache[15] & 0xFF; + //return PCI_CfgReadByte( gPCI_Devices[id].bus, gPCI_Devices[id].slot, gPCI_Devices[id].fcn, 0x3C); +} + +/** + * \brief Read the a BAR (base address register) from the PCI config space + */ +Uint32 PCI_GetBAR(tPCIDev id, int BARNum) +{ + if(id < 0 || id >= giPCI_DeviceCount) + return 0; + if(BARNum < 0 || BARNum >= 6) + return 0; + return gPCI_Devices[id].ConfigCache[4+BARNum]; +} + +/** + * \brief Get device information for a slot/function + */ +int PCI_int_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, tPCIDevice *info) +{ + Uint32 vendor_dev, tmp; + int i; + Uint32 addr; + addr = PCI_int_GetBusAddr(bus, slot, fcn, 0); + + vendor_dev = PCI_CfgReadDWord( addr ); + if((vendor_dev & 0xFFFF) == 0xFFFF) // Invalid Device + return 0; + + info->ConfigCache[0] = vendor_dev; + for( i = 1, addr += 4; i < 256/4; i ++, addr += 4 ) + { + info->ConfigCache[i] = PCI_CfgReadDWord(addr); + } + + info->bus = bus; + info->slot = slot; + info->fcn = fcn; + info->vendor = vendor_dev & 0xFFFF; + info->device = vendor_dev >> 16; + tmp = info->ConfigCache[2]; + info->revision = tmp & 0xFF; + info->class = tmp >> 8; + +// #if LIST_DEVICES +// Log("BAR0 0x%08x BAR1 0x%08x BAR2 0x%08x", info->ConfigCache[4], info->ConfigCache[5], info->ConfigCache[6]); +// Log("BAR3 0x%08x BAR4 0x%08x BAR5 0x%08x", info->ConfigCache[7], info->ConfigCache[8], info->ConfigCache[9]); +// Log("Class: 0x%06x", info->class); +// #endif + + // Make node name + info->Name[0] = '0' + bus/10; + info->Name[1] = '0' + bus%10; + info->Name[2] = '.'; + info->Name[3] = '0' + slot/10; + info->Name[4] = '0' + slot%10; + info->Name[5] = ':'; + info->Name[6] = '0' + fcn; + info->Name[7] = '\0'; + + // Create VFS Node + memset( &info->Node, 0, sizeof(tVFS_Node) ); + info->Node.Size = 256; + + info->Node.NumACLs = 1; + info->Node.ACLs = &gVFS_ACL_EveryoneRO; + + info->Node.Type = &gPCI_RootNodeType; + + return 1; +} + +// === EXPORTS === +//* +EXPORT(PCI_CountDevices); +EXPORT(PCI_GetDevice); +EXPORT(PCI_GetDeviceByClass); +EXPORT(PCI_GetDeviceInfo); +EXPORT(PCI_GetDeviceVersion); +EXPORT(PCI_GetDeviceSubsys); +//EXPORT(PCI_AssignPort); +EXPORT(PCI_GetBAR); +EXPORT(PCI_GetIRQ); +//*/ diff --git a/KernelLand/Kernel/drv/proc.c b/KernelLand/Kernel/drv/proc.c new file mode 100644 index 00000000..73f45354 --- /dev/null +++ b/KernelLand/Kernel/drv/proc.c @@ -0,0 +1,393 @@ +/* + * Acess2 + * - Kernel Status Driver + */ +#define DEBUG 1 +#include +#include +#include +#include + +// === CONSTANTS === +#define VERSION ((0 << 8) | (1)) // 0.01 + +// === TYPES === +typedef struct sSysFS_Ent +{ + struct sSysFS_Ent *Next; + struct sSysFS_Ent *ListNext; + struct sSysFS_Ent *Parent; + tVFS_Node Node; + char Name[]; +} tSysFS_Ent; + +// === PROTOTYPES === + int SysFS_Install(char **Arguments); + int SysFS_IOCtl(tVFS_Node *Node, int Id, void *Data); + +#if 0 + int SysFS_RegisterFile(const char *Path, const char *Data, int Length); + int SysFS_UpdateFile(int ID, const char *Data, int Length); + int SysFS_RemoveFile(int ID); +#endif + +char *SysFS_Comm_ReadDir(tVFS_Node *Node, int Id); +tVFS_Node *SysFS_Comm_FindDir(tVFS_Node *Node, const char *Filename); +Uint64 SysFS_Comm_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +void SysFS_Comm_CloseFile(tVFS_Node *Node); + +// === GLOBALS === +extern tSysFS_Ent gSysFS_Version; // Defined Later +extern tSysFS_Ent gSysFS_Root; // Defined Later +MODULE_DEFINE(0, VERSION, SysFS, SysFS_Install, NULL, NULL); +tVFS_NodeType gSysFS_FileNodeType = { + .TypeName = "SysFS File", + .Read = SysFS_Comm_ReadFile + }; +tVFS_NodeType gSysFS_DirNodeType = { + .TypeName = "SysFS Dir", + .ReadDir = SysFS_Comm_ReadDir, + .FindDir = SysFS_Comm_FindDir + }; +tSysFS_Ent gSysFS_Version_Kernel = { + NULL, NULL, // Nexts + &gSysFS_Version, // Parent + { + .Inode = 1, // File #1 + .ImplPtr = NULL, + .ImplInt = (Uint)&gSysFS_Version_Kernel, // Self-Link + .Size = 0, + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRO, + .Type = &gSysFS_FileNodeType + }, + "Kernel" +}; +tSysFS_Ent gSysFS_Version = { + NULL, NULL, + &gSysFS_Root, + { + .Size = 1, + .ImplPtr = &gSysFS_Version_Kernel, + .ImplInt = (Uint)&gSysFS_Version, // Self-Link + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Flags = VFS_FFLAG_DIRECTORY, + .Type = &gSysFS_DirNodeType + }, + "Version" +}; +// Root of the SysFS tree (just used to keep the code clean) +tSysFS_Ent gSysFS_Root = { + NULL, NULL, + NULL, + { + .Size = 1, + .ImplPtr = &gSysFS_Version, + .ImplInt = (Uint)&gSysFS_Root // Self-Link + }, + "/" +}; +tDevFS_Driver gSysFS_DriverInfo = { + NULL, "system", + { + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Flags = VFS_FFLAG_DIRECTORY, + .ImplPtr = &gSysFS_Version, + .Size = sizeof(gSysFS_Root)/sizeof(tSysFS_Ent), + .Type = &gSysFS_DirNodeType + } +}; + int giSysFS_NextFileID = 2; +tSysFS_Ent *gSysFS_FileList; + +// === CODE === +/** + * \fn int SysFS_Install(char **Options) + * \brief Installs the SysFS Driver + */ +int SysFS_Install(char **Options) +{ + { + const char *fmt = "Acess2 "EXPAND_STR(KERNEL_VERSION)" "EXPAND_STR(ARCHDIR)" build %i, hash %s"; + gSysFS_Version_Kernel.Node.Size = sprintf(NULL, fmt, BUILD_NUM, gsGitHash); + gSysFS_Version_Kernel.Node.ImplPtr = malloc( gSysFS_Version_Kernel.Node.Size + 1 ); + sprintf(gSysFS_Version_Kernel.Node.ImplPtr, fmt, BUILD_NUM, gsGitHash); + } + + DevFS_AddDevice( &gSysFS_DriverInfo ); + return MODULE_ERR_OK; +} + +/** + * \fn int SysFS_RegisterFile(char *Path, char *Data, int Length) + * \brief Registers a file (buffer) for the user to be able to read from + * \param Path Path for the file to be accessable from (relative to SysFS root) + * \param Data Pointer to the data buffer (must be non-volatile) + * \param Length Length of the data buffer + * \return The file's identifier + */ +int SysFS_RegisterFile(const char *Path, const char *Data, int Length) +{ + int start = 0; + int tmp; + tSysFS_Ent *ent = NULL; + tSysFS_Ent *child, *prev; + + // Find parent directory + while( (tmp = strpos(&Path[start], '/')) != -1 ) + { + prev = NULL; + + if(ent) + child = ent->Node.ImplPtr; + else + child = gSysFS_DriverInfo.RootNode.ImplPtr; + for( ; child; prev = child, child = child->Next ) + { + if( strncmp( &Path[start], child->Name, tmp+1 ) == '/' ) + break; + } + + // Need a new directory? + if( !child ) + { + child = calloc( 1, sizeof(tSysFS_Ent)+tmp+1 ); + child->Next = NULL; + memcpy(child->Name, &Path[start], tmp); + child->Name[tmp] = '\0'; + child->Parent = ent; + child->Node.Inode = 0; + child->Node.ImplPtr = NULL; + child->Node.ImplInt = (Uint)child; // Uplink + child->Node.NumACLs = 1; + child->Node.ACLs = &gVFS_ACL_EveryoneRX; + child->Node.Flags = VFS_FFLAG_DIRECTORY; + child->Node.Type = &gSysFS_DirNodeType; + if( !prev ) { + if(ent) + ent->Node.ImplPtr = child; + else + gSysFS_DriverInfo.RootNode.ImplPtr = child; + // ^^^ Impossible (There is already /Version) + } + else + prev->Next = child; + if(ent) + ent->Node.Size ++; + else + gSysFS_DriverInfo.RootNode.Size ++; + Log_Log("SysFS", "Added directory '%s'", child->Name); + } + + ent = child; + + start = tmp+1; + } + + // ent: Parent tSysFS_Ent or NULL + // start: beginning of last path element + + // Check if the name is taken + prev = NULL; + if(ent) + child = ent->Node.ImplPtr; + else + child = gSysFS_DriverInfo.RootNode.ImplPtr; + for( ; child; child = child->Next ) + { + if( strcmp( &Path[start], child->Name ) == 0 ) + break; + } + if( child ) { + Log_Warning("SysFS", "'%s' is taken (in '%s')\n", &Path[start], Path); + return 0; + } + + // Create new node + child = calloc( 1, sizeof(tSysFS_Ent)+strlen(&Path[start])+1 ); + child->Next = NULL; + strcpy(child->Name, &Path[start]); + child->Parent = ent; + + child->Node.Inode = giSysFS_NextFileID++; + child->Node.ImplPtr = (void*)Data; + child->Node.ImplInt = (Uint)child; // Uplink + child->Node.Size = Length; + child->Node.NumACLs = 1; + child->Node.ACLs = &gVFS_ACL_EveryoneRO; + child->Node.Type = &gSysFS_FileNodeType; + + // Add to parent's child list + if(ent) { + ent->Node.Size ++; + child->Next = ent->Node.ImplPtr; + ent->Node.ImplPtr = child; + } + else { + gSysFS_DriverInfo.RootNode.Size ++; + child->Next = gSysFS_DriverInfo.RootNode.ImplPtr; + gSysFS_DriverInfo.RootNode.ImplPtr = child; + } + // Add to global file list + child->ListNext = gSysFS_FileList; + gSysFS_FileList = child; + + Log_Log("SysFS", "Added '%s' (%p)", Path, Data); + + return child->Node.Inode; +} + +/** + * \fn int SysFS_UpdateFile(int ID, char *Data, int Length) + * \brief Updates a file + * \param ID Identifier returned by ::SysFS_RegisterFile + * \param Data Pointer to the data buffer + * \param Length Length of the data buffer + * \return Boolean Success + */ +int SysFS_UpdateFile(int ID, const char *Data, int Length) +{ + tSysFS_Ent *ent; + + for( ent = gSysFS_FileList; ent; ent = ent->Next ) + { + // It's a reverse sorted list + if(ent->Node.Inode < ID) return 0; + if(ent->Node.Inode == ID) + { + ent->Node.ImplPtr = (void*)Data; + ent->Node.Size = Length; + return 1; + } + } + + return 0; +} + +/** + * \fn int SysFS_RemoveFile(int ID) + * \brief Removes a file from user access + * \param ID Identifier returned by ::SysFS_RegisterFile + * \return Boolean Success + * \note If a handle is still open to the file, it will be invalidated + */ +int SysFS_RemoveFile(int ID) +{ + tSysFS_Ent *file; + tSysFS_Ent *ent, *parent, *prev; + + prev = NULL; + for( ent = gSysFS_FileList; ent; prev = ent, ent = ent->Next ) + { + // It's a reverse sorted list + if(ent->Node.Inode < ID) return 0; + if(ent->Node.Inode == ID) break; + } + + if(!ent) return 0; + + // Set up for next part + file = ent; + parent = file->Parent; + + // Remove from file list + if(prev) + prev->ListNext = file->ListNext; + else + gSysFS_FileList = file->ListNext; + file->Node.Size = 0; + file->Node.ImplPtr = NULL; + + // Search parent directory + for( ent = parent->Node.ImplPtr; ent; prev = ent, ent = ent->Next ) + { + if( ent == file ) break; + } + if(!ent) { + Log_Warning("SysFS", "Bookkeeping Error: File in list, but not in directory"); + return 0; + } + + // Remove from parent directory + if(prev) + prev->Next = ent->Next; + else + parent->Node.ImplPtr = ent->Next; + + // Free if not in use + if(file->Node.ReferenceCount == 0) + free(file); + + return 1; +} + +/** + * \fn char *SysFS_Comm_ReadDir(tVFS_Node *Node, int Pos) + * \brief Reads from a SysFS directory + */ +char *SysFS_Comm_ReadDir(tVFS_Node *Node, int Pos) +{ + tSysFS_Ent *child = (tSysFS_Ent*)Node->ImplPtr; + if(Pos < 0 || Pos >= Node->Size) return NULL; + + for( ; child; child = child->Next, Pos-- ) + { + if( Pos == 0 ) return strdup(child->Name); + } + return NULL; +} + +/** + * \fn tVFS_Node *SysFS_Comm_FindDir(tVFS_Node *Node, const char *Filename) + * \brief Find a file in a SysFS directory + */ +tVFS_Node *SysFS_Comm_FindDir(tVFS_Node *Node, const char *Filename) +{ + tSysFS_Ent *child = (tSysFS_Ent*)Node->ImplPtr; + + for( ; child; child = child->Next ) + { + if( strcmp(child->Name, Filename) == 0 ) + return &child->Node; + } + + return NULL; +} + +/** + * \fn Uint64 SysFS_Comm_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Read from an exposed buffer + */ +Uint64 SysFS_Comm_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + if( Offset > Node->Size ) return -1; + if( Length > Node->Size ) Length = Node->Size; + if( Offset + Length > Node->Size) Length = Node->Size - Offset; + + if( Node->ImplPtr ) + memcpy(Buffer, (void*)((Uint)Node->ImplPtr+(Uint)Offset), Length); + else + return -1; + + return Length; +} + +/** + * \fn void SysFS_Comm_CloseFile(tVFS_Node *Node) + * \brief Closes an open file + * \param Node Node to close + */ +void SysFS_Comm_CloseFile(tVFS_Node *Node) +{ + // Dereference + Node->ReferenceCount --; + if( Node->ReferenceCount > 0 ) return; + + // Check if it is still valid + if( Node->ImplPtr ) return; + + // Delete + free( (void*)Node->ImplInt ); +} diff --git a/KernelLand/Kernel/drv/vterm.c b/KernelLand/Kernel/drv/vterm.c new file mode 100644 index 00000000..2ae38ee8 --- /dev/null +++ b/KernelLand/Kernel/drv/vterm.c @@ -0,0 +1,764 @@ +/* + * Acess2 Virtual Terminal Driver + */ +#define DEBUG 0 +#include "vterm.h" +#include +#include +#include +#include +#include +#include + +// === CONSTANTS === +#define VERSION ((0<<8)|(50)) + +#define NUM_VTS 8 +//#define DEFAULT_OUTPUT "BochsGA" +#define DEFAULT_OUTPUT "Vesa" +#define FALLBACK_OUTPUT "x86_VGAText" +#define DEFAULT_INPUT "PS2Keyboard" +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 +#define DEFAULT_SCROLLBACK 2 // 2 Screens of text + current screen +//#define DEFAULT_SCROLLBACK 0 + +// === TYPES === + +// === IMPORTS === +extern void Debug_SetKTerminal(const char *File); + +// === PROTOTYPES === + int VT_Install(char **Arguments); +char *VT_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name); + int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data); +Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); + int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data); + +// === CONSTANTS === + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, VTerm, VT_Install, NULL, DEFAULT_INPUT, NULL); +tVFS_NodeType gVT_RootNodeType = { + .TypeName = "VTerm Root", + .ReadDir = VT_ReadDir, + .FindDir = VT_FindDir, + .IOCtl = VT_Root_IOCtl + }; +tVFS_NodeType gVT_TermNodeType = { + .TypeName = "VTerm", + .Read = VT_Read, + .Write = VT_Write, + .IOCtl = VT_Terminal_IOCtl + }; +tDevFS_Driver gVT_DrvInfo = { + NULL, "VTerm", + { + .Flags = VFS_FFLAG_DIRECTORY, + .Size = NUM_VTS, + .Inode = -1, + .NumACLs = 0, + .Type = &gVT_RootNodeType + } +}; +// --- Terminals --- +tVTerm gVT_Terminals[NUM_VTS]; + int giVT_CurrentTerminal = 0; +tVTerm *gpVT_CurTerm = &gVT_Terminals[0]; +// --- Video State --- +short giVT_RealWidth = DEFAULT_WIDTH; //!< Screen Width +short giVT_RealHeight = DEFAULT_HEIGHT; //!< Screen Height + int giVT_Scrollback = DEFAULT_SCROLLBACK; +// --- Driver Handles --- +char *gsVT_OutputDevice = NULL; +char *gsVT_InputDevice = NULL; + int giVT_OutputDevHandle = -2; + int giVT_InputDevHandle = -2; + +// === CODE === +/** + * \fn int VT_Install(char **Arguments) + * \brief Installs the Virtual Terminal Driver + */ +int VT_Install(char **Arguments) +{ + int i; + + // Scan Arguments + if(Arguments) + { + char **args; + const char *arg; + for(args = Arguments; (arg = *args); args++ ) + { + char data[strlen(arg)+1]; + char *opt = data; + char *val; + + val = strchr(arg, '='); + strcpy(data, arg); + if( val ) { + data[ val - arg ] = '\0'; + val ++; + } + Log_Debug("VTerm", "Argument '%s'", arg); + + if( strcmp(opt, "Video") == 0 ) { + if( !gsVT_OutputDevice ) + gsVT_OutputDevice = strdup(val); + } + else if( strcmp(opt, "Input") == 0 ) { + if( !gsVT_InputDevice ) + gsVT_InputDevice = strdup(val); + } + else if( strcmp(opt, "Width") == 0 ) { + giVT_RealWidth = atoi( val ); + } + else if( strcmp(opt, "Height") == 0 ) { + giVT_RealHeight = atoi( val ); + } + else if( strcmp(opt, "Scrollback") == 0 ) { + giVT_Scrollback = atoi( val ); + } + } + } + + // Apply Defaults + if(!gsVT_OutputDevice) gsVT_OutputDevice = (char*)DEFAULT_OUTPUT; + else if( Module_EnsureLoaded( gsVT_OutputDevice ) ) gsVT_OutputDevice = (char*)DEFAULT_OUTPUT; + if( Module_EnsureLoaded( gsVT_OutputDevice ) ) gsVT_OutputDevice = (char*)FALLBACK_OUTPUT; + if( Module_EnsureLoaded( gsVT_OutputDevice ) ) { + Log_Error("VTerm", "Fallback video '%s' is not avaliable, giving up", FALLBACK_OUTPUT); + return MODULE_ERR_MISC; + } + + if(!gsVT_InputDevice) gsVT_InputDevice = (char*)DEFAULT_INPUT; + else if( Module_EnsureLoaded( gsVT_InputDevice ) ) gsVT_InputDevice = (char*)DEFAULT_INPUT; + + // Create device paths + { + char *tmp; + tmp = malloc( 9 + strlen(gsVT_OutputDevice) + 1 ); + strcpy(tmp, "/Devices/"); + strcpy(&tmp[9], gsVT_OutputDevice); + gsVT_OutputDevice = tmp; + + tmp = malloc( 9 + strlen(gsVT_InputDevice) + 1 ); + strcpy(tmp, "/Devices/"); + strcpy(&tmp[9], gsVT_InputDevice); + gsVT_InputDevice = tmp; + } + + Log_Log("VTerm", "Using '%s' as output", gsVT_OutputDevice); + Log_Log("VTerm", "Using '%s' as input", gsVT_InputDevice); + + VT_InitOutput(); + VT_InitInput(); + + // Create Nodes + for( i = 0; i < NUM_VTS; i++ ) + { + gVT_Terminals[i].Mode = TERM_MODE_TEXT; + gVT_Terminals[i].Flags = 0; +// gVT_Terminals[i].Flags = VT_FLAG_HIDECSR; //HACK - Stop all those memcpy calls + gVT_Terminals[i].CurColour = DEFAULT_COLOUR; + gVT_Terminals[i].WritePos = 0; + gVT_Terminals[i].AltWritePos = 0; + gVT_Terminals[i].ViewPos = 0; + gVT_Terminals[i].ReadingThread = -1; + gVT_Terminals[i].ScrollHeight = 0; + + // Initialise + VT_int_ChangeMode( &gVT_Terminals[i], + TERM_MODE_TEXT, giVT_RealWidth, giVT_RealHeight ); + + gVT_Terminals[i].Name[0] = '0'+i; + gVT_Terminals[i].Name[1] = '\0'; + gVT_Terminals[i].Node.Inode = i; + gVT_Terminals[i].Node.ImplPtr = &gVT_Terminals[i]; + gVT_Terminals[i].Node.NumACLs = 0; // Only root can open virtual terminals + + gVT_Terminals[i].Node.Type = &gVT_TermNodeType; +// Semaphore_Init(&gVT_Terminals[i].InputSemaphore, 0, MAX_INPUT_CHARS8, "VTerm", gVT_Terminals[i].Name); + } + + // Add to DevFS + DevFS_AddDevice( &gVT_DrvInfo ); + + // Set kernel output to VT0 + Debug_SetKTerminal("/Devices/VTerm/0"); + + return MODULE_ERR_OK; +} + +/** + * \brief Set the video resolution + * \param Width New screen width + * \param Height New screen height + */ +void VT_SetResolution(int Width, int Height) +{ + tVideo_IOCtl_Mode mode = {0}; + int tmp; + int i; + + // Create the video mode + mode.width = Width; + mode.height = Height; + mode.bpp = 32; + mode.flags = 0; + + // Set video mode + VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_FINDMODE, &mode ); + tmp = mode.id; + if( Width != mode.width || Height != mode.height ) + { + Log_Warning("VTerm", + "Selected resolution (%ix%i is not supported) by the device, using (%ix%i)", + giVT_RealWidth, giVT_RealHeight, + mode.width, mode.height + ); + giVT_RealWidth = mode.width; + giVT_RealHeight = mode.height; + } + VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_GETSETMODE, &tmp ); + + // Resize text terminals if needed + if( gVT_Terminals[0].Text && (giVT_RealWidth != mode.width || giVT_RealHeight != mode.height) ) + { + int newBufSize = (giVT_RealWidth/giVT_CharWidth) + *(giVT_RealHeight/giVT_CharHeight) + *(giVT_Scrollback+1); + //tVT_Char *tmp; + // Resize the text terminals + Log_Debug("VTerm", "Resizing terminals to %ix%i", + giVT_RealWidth/giVT_CharWidth, giVT_RealHeight/giVT_CharHeight); + for( i = 0; i < NUM_VTS; i ++ ) + { + if( gVT_Terminals[i].Mode != TERM_MODE_TEXT ) continue; + + gVT_Terminals[i].TextWidth = giVT_RealWidth/giVT_CharWidth; + gVT_Terminals[i].TextHeight = giVT_RealHeight/giVT_CharHeight; + gVT_Terminals[i].ScrollHeight = gVT_Terminals[i].TextHeight; + + gVT_Terminals[i].Text = realloc( + gVT_Terminals[i].Text, + newBufSize*sizeof(tVT_Char) + ); + } + } +} + +/** + * \fn char *VT_ReadDir(tVFS_Node *Node, int Pos) + * \brief Read from the VTerm Directory + */ +char *VT_ReadDir(tVFS_Node *Node, int Pos) +{ + if(Pos < 0) return NULL; + if(Pos >= NUM_VTS) return NULL; + return strdup( gVT_Terminals[Pos].Name ); +} + +/** + * \fn tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name) + * \brief Find an item in the VTerm directory + * \param Node Root node + * \param Name Name (number) of the terminal + */ +tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name) +{ + int num; + + ENTER("pNode sName", Node, Name); + + // Open the input and output files if needed + if(giVT_OutputDevHandle == -2) VT_InitOutput(); + if(giVT_InputDevHandle == -2) VT_InitInput(); + + // Sanity check name + if(Name[0] < '0' || Name[0] > '9' || Name[1] != '\0') { + LEAVE('n'); + return NULL; + } + // Get index + num = Name[0] - '0'; + if(num >= NUM_VTS) { + LEAVE('n'); + return NULL; + } + // Return node + LEAVE('p', &gVT_Terminals[num].Node); + return &gVT_Terminals[num].Node; +} + +/** + * \fn int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data) + * \brief Control the VTerm Driver + */ +int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data) +{ + int len; + switch(Id) + { + case DRV_IOCTL_TYPE: return DRV_TYPE_MISC; + case DRV_IOCTL_IDENT: memcpy(Data, "VT\0\0", 4); return 0; + case DRV_IOCTL_VERSION: return VERSION; + case DRV_IOCTL_LOOKUP: return 0; + + case 4: // Get Video Driver + if(Data) strcpy(Data, gsVT_OutputDevice); + return strlen(gsVT_OutputDevice); + + case 5: // Set Video Driver + if(!Data) return -EINVAL; + if(Threads_GetUID() != 0) return -EACCES; + + len = strlen(Data); + + // TODO: Check if the string used is a heap string + + free(gsVT_OutputDevice); + + gsVT_OutputDevice = malloc(len+1); + strcpy(gsVT_OutputDevice, Data); + + VFS_Close(giVT_OutputDevHandle); + giVT_OutputDevHandle = -1; + + VT_InitOutput(); + return 1; + } + return 0; +} + +/** + * \brief Read from a virtual terminal + */ +Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + int pos = 0; + int avail; + tVTerm *term = &gVT_Terminals[ Node->Inode ]; + Uint32 *codepoint_buf = Buffer; + Uint32 *codepoint_in; + + Mutex_Acquire( &term->ReadingLock ); + + // Check current mode + switch(term->Mode) + { + // Text Mode (UTF-8) + case TERM_MODE_TEXT: + VT_int_UpdateCursor(term, 1); + + VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UTF-8)"); + + avail = term->InputWrite - term->InputRead; + if(avail < 0) + avail += MAX_INPUT_CHARS8; + if(avail > Length - pos) + avail = Length - pos; + + while( avail -- ) + { + ((char*)Buffer)[pos] = term->InputBuffer[term->InputRead]; + pos ++; + term->InputRead ++; + while(term->InputRead >= MAX_INPUT_CHARS8) + term->InputRead -= MAX_INPUT_CHARS8; + } + break; + + //case TERM_MODE_FB: + // Other - UCS-4 + default: + VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UCS-4)"); + + avail = term->InputWrite - term->InputRead; + if(avail < 0) + avail += MAX_INPUT_CHARS32; + Length /= 4; + if(avail > Length - pos) + avail = Length - pos; + + codepoint_in = (void*)term->InputBuffer; + codepoint_buf = Buffer; + + while( avail -- ) + { + codepoint_buf[pos] = codepoint_in[term->InputRead]; + pos ++; + term->InputRead ++; + while(term->InputRead >= MAX_INPUT_CHARS32) + term->InputRead -= MAX_INPUT_CHARS32; + } + pos *= 4; + break; + } + + // Mark none avaliable if buffer empty + if( term->InputRead == term->InputWrite ) + VFS_MarkAvaliable(&term->Node, 0); + + term->ReadingThread = -1; + +// VT_int_UpdateCursor(term, term->Mode == TERM_MODE_TEXT); + + Mutex_Release( &term->ReadingLock ); + + return pos; +} + +/** + * \fn Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) + * \brief Write to a virtual terminal + */ +Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + tVTerm *term = &gVT_Terminals[ Node->Inode ]; + int size; + + // Write + switch( term->Mode ) + { + // Print Text + case TERM_MODE_TEXT: + VT_int_PutString(term, Buffer, Length); + break; + + // Framebuffer :) + case TERM_MODE_FB: + // - Sanity Checking + size = term->Width*term->Height*4; + if( Offset > size ) { + Log_Notice("VTerm", "VT_Write: Offset (0x%llx) > FBSize (0x%x)", + Offset, size); + return 0; + } + if( Offset + Length > size ) { + Log_Notice("VTerm", "VT_Write: Offset+Length (0x%llx) > FBSize (0x%x)", + Offset+Length, size); + Length = size - Offset; + } + + // Update screen if needed + if( Node->Inode == giVT_CurrentTerminal ) + { + if( giVT_RealHeight > term->Height ) + Offset += (giVT_RealHeight - term->Height) / 2 * term->Width * 4; + // Handle undersized virtual terminals + if( giVT_RealWidth > term->Width ) + { + // No? :( Well, just center it + int x, y, w, h; + Uint dst_ofs; + // TODO: Fix to handle the final line correctly? + x = Offset/4; y = x / term->Width; x %= term->Width; + w = Length/4+x; h = w / term->Width; w %= term->Width; + + // Center + x += (giVT_RealWidth - term->Width) / 2; + dst_ofs = (x + y * giVT_RealWidth) * 4; + while(h--) + { + VFS_WriteAt( giVT_OutputDevHandle, + dst_ofs, + term->Width * 4, + Buffer + ); + Buffer = (void*)( (Uint)Buffer + term->Width*4 ); + dst_ofs += giVT_RealWidth * 4; + } + return 0; + } + else + { + return VFS_WriteAt( giVT_OutputDevHandle, Offset, Length, Buffer ); + } + } + else + { + if( !term->Buffer ) + term->Buffer = malloc( term->Width * term->Height * 4 ); + // Copy to the local cache + memcpy( (char*)term->Buffer + (Uint)Offset, Buffer, Length ); + } + break; + // Just pass on (for now) + // TODO: Handle locally too to ensure no information is lost on + // VT Switch (and to isolate terminals from each other) + case TERM_MODE_2DACCEL: + //case TERM_MODE_3DACCEL: + if( Node->Inode == giVT_CurrentTerminal ) + { + VFS_Write( giVT_OutputDevHandle, Length, Buffer ); + } + break; + } + + return 0; +} + +/** + * \fn int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data) + * \brief Call an IO Control on a virtual terminal + */ +int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data) +{ + int *iData = Data; + int ret; + tVTerm *term = Node->ImplPtr; + ENTER("pNode iId pData", Node, Id, Data); + + if(Id >= DRV_IOCTL_LOOKUP) { + // Only root can fiddle with graphics modes + // TODO: Remove this and replace with user ownership + if( Threads_GetUID() != 0 ) return -1; + } + + switch(Id) + { + // --- Core Defined + case DRV_IOCTL_TYPE: + LEAVE('i', DRV_TYPE_TERMINAL); + return DRV_TYPE_TERMINAL; + case DRV_IOCTL_IDENT: + memcpy(Data, "VT\0\0", 4); + LEAVE('i', 0); + return 0; + case DRV_IOCTL_VERSION: + LEAVE('x', VERSION); + return VERSION; + case DRV_IOCTL_LOOKUP: + LEAVE('i', 0); + return 0; + + // Get/Set the mode (and apply any changes) + case TERM_IOCTL_MODETYPE: + if(Data != NULL) + { + if( CheckMem(Data, sizeof(int)) == 0 ) { + LEAVE('i', -1); + return -1; + } + Log_Log("VTerm", "VTerm %i mode set to %i", (int)Node->Inode, *iData); + + // Update mode if needed + if( term->Mode != *iData || term->NewWidth || term->NewHeight) + { + // Adjust for text mode + if( *iData == TERM_MODE_TEXT ) { + term->NewHeight *= giVT_CharHeight; + term->NewWidth *= giVT_CharWidth; + } + // Fill unchanged dimensions + if(term->NewHeight == 0) term->NewHeight = term->Height; + if(term->NewWidth == 0) term->NewWidth = term->Width; + // Set new mode + VT_int_ChangeMode(term, *iData, term->NewWidth, term->NewHeight); + // Clear unapplied dimensions + term->NewWidth = 0; + term->NewHeight = 0; + } + + // Update the screen dimensions + if(Node->Inode == giVT_CurrentTerminal) + VT_SetTerminal( giVT_CurrentTerminal ); + } + LEAVE('i', term->Mode); + return term->Mode; + + // Get/set the terminal width + case TERM_IOCTL_WIDTH: + if(Data != NULL) { + if( CheckMem(Data, sizeof(int)) == 0 ) { + LEAVE('i', -1); + return -1; + } + term->NewWidth = *iData; + } + if( term->NewWidth ) + ret = term->NewWidth; + else if( term->Mode == TERM_MODE_TEXT ) + ret = term->TextWidth; + else + ret = term->Width; + LEAVE('i', ret); + return ret; + + // Get/set the terminal height + case TERM_IOCTL_HEIGHT: + if(Data != NULL) { + if( CheckMem(Data, sizeof(int)) == 0 ) { + LEAVE('i', -1); + return -1; + } + term->NewHeight = *iData; + } + if( term->NewHeight ) + ret = term->NewHeight; + else if( term->Mode == TERM_MODE_TEXT ) + ret = term->TextHeight; + else + ret = term->Height; + LEAVE('i', ret); + return ret; + + case TERM_IOCTL_FORCESHOW: + Log_Log("VTerm", "Thread %i forced VTerm %i to be shown", + Threads_GetTID(), (int)Node->Inode); + VT_SetTerminal( Node->Inode ); + LEAVE('i', 1); + return 1; + + case TERM_IOCTL_GETSETCURSOR: + if(Data != NULL) + { + tVideo_IOCtl_Pos *pos = Data; + if( !CheckMem(Data, sizeof(*pos)) ) { + errno = -EINVAL; + LEAVE('i', -1); + return -1; + } + + if( term->Mode == TERM_MODE_TEXT ) + { + if(term->Flags & VT_FLAG_ALTBUF) + term->AltWritePos = pos->x + pos->y * term->TextWidth; + else + term->WritePos = pos->x + pos->y * term->TextWidth + term->ViewPos; + VT_int_UpdateCursor(term, 0); + } + else + { + term->VideoCursorX = pos->x; + term->VideoCursorY = pos->y; + VT_int_UpdateCursor(term, 1); + } + } + ret = (term->Flags & VT_FLAG_ALTBUF) ? term->AltWritePos : term->WritePos-term->ViewPos; + LEAVE('i', ret); + return ret; + + case TERM_IOCTL_SETCURSORBITMAP: { + tVideo_IOCtl_Bitmap *bmp = Data; + if( Data == NULL ) + { + free( term->VideoCursor ); + term->VideoCursor = NULL; + LEAVE('i', 0); + return 0; + } + + // Sanity check bitmap + if( !CheckMem(bmp, sizeof(tVideo_IOCtl_Bitmap)) ) { + Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp); + errno = -EINVAL; + LEAVE_RET('i', -1); + } + if( !CheckMem(bmp->Data, bmp->W*bmp->H*sizeof(Uint32)) ) { + Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp); + errno = -EINVAL; + LEAVE_RET('i', -1); + } + + // Reallocate if needed + if(term->VideoCursor) + { + if(bmp->W * bmp->H != term->VideoCursor->W * term->VideoCursor->H) { + free(term->VideoCursor); + term->VideoCursor = NULL; + } + } + if(!term->VideoCursor) { + term->VideoCursor = malloc(sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32)); + if(!term->VideoCursor) { + Log_Error("VTerm", "Unable to allocate memory for cursor"); + errno = -ENOMEM; + LEAVE_RET('i', -1); + } + } + + memcpy(term->VideoCursor, bmp, sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32)); + + Log_Debug("VTerm", "Set VT%i's cursor to %p %ix%i", + (int)term->Node.Inode, bmp, bmp->W, bmp->H); + + if(gpVT_CurTerm == term) + VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, term->VideoCursor); + + LEAVE('i', 0); + return 0; } + } + LEAVE('i', -1); + return -1; +} + +/** + * \fn void VT_SetTerminal(int ID) + * \brief Set the current terminal + */ +void VT_SetTerminal(int ID) +{ + // Copy the screen state + if( ID != giVT_CurrentTerminal && gpVT_CurTerm->Mode != TERM_MODE_TEXT ) + { + if( !gpVT_CurTerm->Buffer ) + gpVT_CurTerm->Buffer = malloc( gpVT_CurTerm->Width*gpVT_CurTerm->Height*4 ); + if( gpVT_CurTerm->Width < giVT_RealWidth ) + { + int line; + Uint ofs = 0; + Uint32 *dest = gpVT_CurTerm->Buffer; + // Slower scanline copy + for( line = 0; line < gpVT_CurTerm->Height; line ++ ) + { + VFS_ReadAt(giVT_OutputDevHandle, ofs, gpVT_CurTerm->Width*4, dest); + ofs += giVT_RealWidth * 4; + dest += gpVT_CurTerm->Width; + } + } + else + { + VFS_ReadAt(giVT_OutputDevHandle, + 0, gpVT_CurTerm->Height*giVT_RealWidth*4, + gpVT_CurTerm->Buffer + ); + } + } + + // Update current terminal ID + Log_Log("VTerm", "Changed terminal from %i to %i", giVT_CurrentTerminal, ID); + giVT_CurrentTerminal = ID; + gpVT_CurTerm = &gVT_Terminals[ID]; + + if( gpVT_CurTerm->Mode == TERM_MODE_TEXT ) + { + VT_SetMode( VIDEO_BUFFMT_TEXT ); + } + else + { + // Update the cursor image + if(gpVT_CurTerm->VideoCursor) + VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, gpVT_CurTerm->VideoCursor); + VT_SetMode( VIDEO_BUFFMT_FRAMEBUFFER ); + } + + if(gpVT_CurTerm->Buffer) + { + // TODO: Handle non equal sized + VFS_WriteAt( + giVT_OutputDevHandle, + 0, + gpVT_CurTerm->Width*gpVT_CurTerm->Height*sizeof(Uint32), + gpVT_CurTerm->Buffer + ); + } + + VT_int_UpdateCursor(gpVT_CurTerm, 1); + // Update the screen + VT_int_UpdateScreen(gpVT_CurTerm, 1); +} diff --git a/KernelLand/Kernel/drv/vterm.h b/KernelLand/Kernel/drv/vterm.h new file mode 100644 index 00000000..cd37ebd9 --- /dev/null +++ b/KernelLand/Kernel/drv/vterm.h @@ -0,0 +1,122 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * drv/vterm.h + * - Virtual Terminal - Common + */ +#ifndef _VTERM_H_ +#define _VTERM_H_ + +#include +#include // tVT_Char +#include +#include + +// === CONSTANTS === +#define MAX_INPUT_CHARS32 64 +#define MAX_INPUT_CHARS8 (MAX_INPUT_CHARS32*4) +#define DEFAULT_COLOUR (VT_COL_BLACK|(0xAAA<<16)) + +/** + * \{ + */ +#define VT_FLAG_HIDECSR 0x01 //!< Hide the cursor +#define VT_FLAG_ALTBUF 0x02 //!< Alternate screen buffer +#define VT_FLAG_RAWIN 0x04 //!< Don't handle ^Z/^C/^V +#define VT_FLAG_HASFB 0x10 //!< Set if the VTerm has requested the Framebuffer +#define VT_FLAG_SHOWCSR 0x20 //!< Always show the text cursor +/** + * \} + */ + +enum eVT_InModes { + VT_INMODE_TEXT8, // UTF-8 Text Mode (VT100/xterm Emulation) + VT_INMODE_TEXT32, // UTF-32 Text Mode (Acess Native) + NUM_VT_INMODES +}; + + +// === TYPES == +typedef struct sVTerm tVTerm; + +// === STRUCTURES === +struct sVTerm +{ + int Mode; //!< Current Mode (see ::eTplTerminal_Modes) + int Flags; //!< Flags (see VT_FLAG_*) + + short NewWidth; //!< Un-applied dimensions (Width) + short NewHeight; //!< Un-applied dimensions (Height) + short Width; //!< Virtual Width + short Height; //!< Virtual Height + short TextWidth; //!< Text Virtual Width + short TextHeight; //!< Text Virtual Height + + Uint32 CurColour; //!< Current Text Colour + + int ViewPos; //!< View Buffer Offset (Text Only) + int WritePos; //!< Write Buffer Offset (Text Only) + tVT_Char *Text; + + tVT_Char *AltBuf; //!< Alternate Screen Buffer + int AltWritePos; //!< Alternate write position + short ScrollTop; //!< Top of scrolling region (smallest) + short ScrollHeight; //!< Length of scrolling region + + int VideoCursorX; + int VideoCursorY; + + tMutex ReadingLock; //!< Lock the VTerm when a process is reading from it + tTID ReadingThread; //!< Owner of the lock + int InputRead; //!< Input buffer read position + int InputWrite; //!< Input buffer write position + char InputBuffer[MAX_INPUT_CHARS8]; +// tSemaphore InputSemaphore; + + Uint32 *Buffer; + + // TODO: Do I need to keep this about? + // When should it be deallocated? on move to text mode, or some other time + // Call set again, it's freed, and if NULL it doesn't get reallocated. + tVideo_IOCtl_Bitmap *VideoCursor; + + char Name[2]; //!< Name of the terminal + tVFS_Node Node; +}; + +// === GOBALS === +extern tVTerm *gpVT_CurTerm; +extern int giVT_Scrollback; +extern short giVT_RealWidth; //!< Screen Width +extern short giVT_RealHeight; //!< Screen Width +extern char *gsVT_OutputDevice; +extern char *gsVT_InputDevice; +extern int giVT_OutputDevHandle; +extern int giVT_InputDevHandle; + +// === FUNCTIONS === +extern void VT_SetResolution(int Width, int Height); +extern void VT_SetTerminal(int ID); +// --- Output --- +extern void VT_InitOutput(void); +extern void VT_SetMode(int Mode); +extern void VT_int_ScrollFramebuffer( tVTerm *Term, int Count ); +extern void VT_int_UpdateCursor( tVTerm *Term, int bShow ); +extern void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ); +// --- Input --- +extern void VT_InitInput(void); +extern void VT_KBCallBack(Uint32 Codepoint); +// --- VT100 Emulation --- +extern void VT_int_ParseEscape_StandardLarge(tVTerm *Term, char CmdChar, int argc, int *args); +extern int VT_int_ParseEscape(tVTerm *Term, const char *Buffer); +// --- Terminal Buffer --- +extern void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count); +extern void VT_int_PutChar(tVTerm *Term, Uint32 Ch); +extern void VT_int_ScrollText(tVTerm *Term, int Count); +extern void VT_int_ClearLine(tVTerm *Term, int Num); +extern void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight); +extern void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled); + +#endif + diff --git a/KernelLand/Kernel/drv/vterm_font.c b/KernelLand/Kernel/drv/vterm_font.c new file mode 100644 index 00000000..841ea504 --- /dev/null +++ b/KernelLand/Kernel/drv/vterm_font.c @@ -0,0 +1,250 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * drv/vterm_font.c + * - Virtual Terminal - Font rendering code + */ +#include "vterm.h" +#include + +// --- +// Font Render +// --- +#define MONOSPACE_FONT 10816 + +#if MONOSPACE_FONT == 10808 // 8x8 +# include "vterm_font_8x8.h" +#elif MONOSPACE_FONT == 10816 // 8x16 +# include "vterm_font_8x16.h" +#endif + +// === PROTOTYPES === +Uint8 *VT_Font_GetChar(Uint32 Codepoint); + +// === GLOBALS === +int giVT_CharWidth = FONT_WIDTH; +int giVT_CharHeight = FONT_HEIGHT; + +// === CODE === +/** + * \brief Render a font character + */ +void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Depth, int Pitch, Uint32 BGC, Uint32 FGC) +{ + Uint8 *font; + int x, y; + + // 8-bpp and below + if( Depth <= 8 ) + { + Uint8 *buf = Buffer; + + font = VT_Font_GetChar(Codepoint); + + for(y = 0; y < FONT_HEIGHT; y ++) + { + for(x = 0; x < FONT_WIDTH; x ++) + { + if(*font & (1 << (FONT_WIDTH-x-1))) + buf[x] = FGC; + else + buf[x] = BGC; + } + buf = (void*)( (tVAddr)buf + Pitch ); + font ++; + } + } + // 16-bpp and below + else if( Depth <= 16 ) + { + Uint16 *buf = Buffer; + + font = VT_Font_GetChar(Codepoint); + + for(y = 0; y < FONT_HEIGHT; y ++) + { + for(x = 0; x < FONT_WIDTH; x ++) + { + if(*font & (1 << (FONT_WIDTH-x-1))) + buf[x] = FGC; + else + buf[x] = BGC; + } + buf = (void*)( (tVAddr)buf + Pitch ); + font ++; + } + } + // 24-bpp colour + // - Special handling to not overwrite the next pixel + //TODO: Endian issues here + else if( Depth == 24 ) + { + Uint8 *buf = Buffer; + Uint8 bg_r = (BGC >> 16) & 0xFF; + Uint8 bg_g = (BGC >> 8) & 0xFF; + Uint8 bg_b = (BGC >> 0) & 0xFF; + Uint8 fg_r = (FGC >> 16) & 0xFF; + Uint8 fg_g = (FGC >> 8) & 0xFF; + Uint8 fg_b = (FGC >> 0) & 0xFF; + + font = VT_Font_GetChar(Codepoint); + + for(y = 0; y < FONT_HEIGHT; y ++) + { + for(x = 0; x < FONT_WIDTH; x ++) + { + Uint8 r, g, b; + + if(*font & (1 << (FONT_WIDTH-x-1))) { + r = fg_r; g = fg_g; b = fg_b; + } + else { + r = bg_r; g = bg_g; b = bg_b; + } + buf[x*3+0] = b; + buf[x*3+1] = g; + buf[x*3+2] = r; + } + buf = (void*)( (tVAddr)buf + Pitch ); + font ++; + } + } + // 32-bpp colour (nice and easy) + else if( Depth == 32 ) + { + Uint32 *buf = Buffer; + + font = VT_Font_GetChar(Codepoint); + + for(y = 0; y < FONT_HEIGHT; y ++) + { + for(x = 0; x < FONT_WIDTH; x ++) + { + if(*font & (1 << (FONT_WIDTH-x-1))) + buf[x] = FGC; + else + buf[x] = BGC; + } + buf = (Uint32*)( (tVAddr)buf + Pitch ); + font ++; + } + } +} + +/** + * \fn Uint32 VT_Colour12to24(Uint16 Col12) + * \brief Converts a 12-bit colour into 24 bits + */ +Uint32 VT_Colour12to24(Uint16 Col12) +{ + Uint32 ret; + int tmp; + tmp = Col12 & 0xF; + ret = (tmp << 0) | (tmp << 4); + tmp = (Col12 & 0xF0) >> 4; + ret |= (tmp << 8) | (tmp << 12); + tmp = (Col12 & 0xF00) >> 8; + ret |= (tmp << 16) | (tmp << 20); + return ret; +} +/** + * \brief Converts a 12-bit colour into 15 bits + */ +Uint16 VT_Colour12to15(Uint16 Col12) +{ + Uint32 ret; + int tmp; + tmp = Col12 & 0xF; + ret = (tmp << 1) | (tmp & 1); + tmp = (Col12 & 0xF0) >> 4; + ret |= ( (tmp << 1) | (tmp & 1) ) << 5; + tmp = (Col12 & 0xF00) >> 8; + ret |= ( (tmp << 1) | (tmp & 1) ) << 10; + return ret; +} + +/** + * \brief Converts a 12-bit colour into any other depth + * \param Col12 12-bit source colour + * \param Depth Desired bit deptj + * \note Green then blue get the extra avaliable bits (16:5-6-5, 14:4-5-5) + */ +Uint32 VT_Colour12toN(Uint16 Col12, int Depth) +{ + Uint32 ret; + Uint32 r, g, b; + int rSize, gSize, bSize; + + // Fast returns + if( Depth == 24 ) return VT_Colour12to24(Col12); + if( Depth == 15 ) return VT_Colour12to15(Col12); + // - 32 is a special case, it's usually 24-bit colour with an unused byte + if( Depth == 32 ) return VT_Colour12to24(Col12); + + // Bounds checks + if( Depth < 8 ) return 0; + if( Depth > 32 ) return 0; + + r = Col12 & 0xF; + g = (Col12 & 0xF0) >> 4; + b = (Col12 & 0xF00) >> 8; + + rSize = gSize = bSize = Depth / 3; + if( rSize + gSize + bSize < Depth ) // Depth % 3 == 1 + gSize ++; + if( rSize + gSize + bSize < Depth ) // Depth % 3 == 2 + bSize ++; + + // Expand + r <<= rSize - 4; g <<= gSize - 4; b <<= bSize - 4; + // Fill with the lowest bit + if( Col12 & 0x001 ) r |= (1 << (rSize - 4)) - 1; + if( Col12 & 0x010 ) r |= (1 << (gSize - 4)) - 1; + if( Col12 & 0x100 ) r |= (1 << (bSize - 4)) - 1; + + // Create output + ret = r; + ret |= g << rSize; + ret |= b << (rSize + gSize); + + return ret; +} + +/** + * \fn Uint8 *VT_Font_GetChar(Uint32 Codepoint) + * \brief Gets an index into the font array given a Unicode Codepoint + * \note See http://en.wikipedia.org/wiki/CP437 + */ +Uint8 *VT_Font_GetChar(Uint32 Codepoint) +{ + int index = 0; + if(Codepoint < 128) + return &VTermFont[Codepoint*FONT_HEIGHT]; + switch(Codepoint) + { + case 0xC7: index = 128; break; // Ç + case 0xFC: index = 129; break; // ü + case 0xE9: index = 130; break; // é + case 0xE2: index = 131; break; // â + case 0xE4: index = 132; break; // ä + case 0xE0: index = 133; break; // à + case 0xE5: index = 134; break; // å + case 0xE7: index = 135; break; // ç + case 0xEA: index = 136; break; // ê + case 0xEB: index = 137; break; // ë + case 0xE8: index = 138; break; // è + case 0xEF: index = 139; break; // ï + case 0xEE: index = 140; break; // î + case 0xEC: index = 141; break; // ì + case 0xC4: index = 142; break; // Ä + case 0xC5: index = 143; break; // Å + } + + return &VTermFont[index*FONT_HEIGHT]; +} + +EXPORTAS(&giVT_CharWidth, giVT_CharWidth); +EXPORTAS(&giVT_CharHeight, giVT_CharHeight); +EXPORT(VT_Font_Render); +EXPORT(VT_Colour12to24); diff --git a/KernelLand/Kernel/drv/vterm_font_8x16.h b/KernelLand/Kernel/drv/vterm_font_8x16.h new file mode 100644 index 00000000..7c613325 --- /dev/null +++ b/KernelLand/Kernel/drv/vterm_font_8x16.h @@ -0,0 +1,265 @@ +/* + * Taken from http://cvs.savannah.gnu.org/viewvc/vgabios/vgafonts.h?root=vgabios&view=markup + * Altered for Acess2 + */ +#define FONT_WIDTH 8 +#define FONT_HEIGHT 16 +static Uint8 VTermFont[256*16]= +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/KernelLand/Kernel/drv/vterm_font_8x8.h b/KernelLand/Kernel/drv/vterm_font_8x8.h new file mode 100644 index 00000000..2a5311d3 --- /dev/null +++ b/KernelLand/Kernel/drv/vterm_font_8x8.h @@ -0,0 +1,265 @@ +/* + * Taken from http://cvs.savannah.gnu.org/viewvc/vgabios/vgafonts.h?root=vgabios&view=markup + * Altered for Acess2 + */ +#define FONT_WIDTH 8 +#define FONT_HEIGHT 8 +static Uint8 VTermFont[256*8]= +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, + 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, + 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c, + 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, + 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, + 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, + 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, + 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, + 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, + 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0, + 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0, + 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99, + 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, + 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, + 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, + 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, + 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, + 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, + 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00, + 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, + 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, + 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, + 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, + 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, + 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00, + 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00, + 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, + 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00, + 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00, + 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, + 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00, + 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, + 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, + 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00, + 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, + 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, + 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, + 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, + 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, + 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00, + 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00, + 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, + 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00, + 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, + 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, + 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, + 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00, + 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, + 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, + 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00, + 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, + 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00, + 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, + 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, + 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, + 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00, + 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, + 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78, + 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00, + 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38, + 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, + 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, + 0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00, + 0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00, + 0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00, + 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00, + 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00, + 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, + 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18, + 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00, + 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30, + 0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7, + 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70, + 0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00, + 0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00, + 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, + 0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00, + 0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f, + 0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03, + 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00, + 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, + 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00, + 0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0, + 0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, + 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, + 0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00, + 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0, + 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00, + 0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc, + 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00, + 0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00, + 0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0, + 0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00, + 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00, + 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00, + 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, + 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00, + 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, + 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c, + 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/KernelLand/Kernel/drv/vterm_input.c b/KernelLand/Kernel/drv/vterm_input.c new file mode 100644 index 00000000..d83cedee --- /dev/null +++ b/KernelLand/Kernel/drv/vterm_input.c @@ -0,0 +1,232 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * drv/vterm_input.c + * - Virtual Terminal - Input code + */ +#include "vterm.h" +#include + +// === GLOBALS === +// --- Key States --- (Used for VT Switching/Magic Combos) + int gbVT_CtrlDown = 0; + int gbVT_AltDown = 0; + int gbVT_SysrqDown = 0; + +// === CODE === +/** + * \fn void VT_InitInput() + * \brief Initialises the input + */ +void VT_InitInput() +{ + giVT_InputDevHandle = VFS_Open(gsVT_InputDevice, VFS_OPENFLAG_READ); + if(giVT_InputDevHandle == -1) { + Log_Warning("VTerm", "Can't open the input device '%s'", gsVT_InputDevice); + return ; + } + VFS_IOCtl(giVT_InputDevHandle, KB_IOCTL_SETCALLBACK, VT_KBCallBack); +} + +/** + * \fn void VT_KBCallBack(Uint32 Codepoint) + * \brief Called on keyboard interrupt + * \param Codepoint Pseudo-UTF32 character + * + * Handles a key press and sends the key code to the user's buffer. + * If the code creates a kernel-magic sequence, it is not passed to the + * user and is handled in-kernel. + */ +void VT_KBCallBack(Uint32 Codepoint) +{ + tVTerm *term = gpVT_CurTerm; + + // Catch VT binds + switch( Codepoint & KEY_ACTION_MASK ) + { + case KEY_ACTION_RELEASE: + switch(Codepoint & KEY_CODEPOINT_MASK) + { + case KEY_LALT: gbVT_AltDown &= ~1; break; + case KEY_RALT: gbVT_AltDown &= ~2; break; + case KEY_LCTRL: gbVT_CtrlDown &= ~1; break; + case KEY_RCTRL: gbVT_CtrlDown &= ~2; break; + } + break; + + case KEY_ACTION_PRESS: + switch(Codepoint & KEY_CODEPOINT_MASK) + { + case KEY_LALT: gbVT_AltDown |= 1; break; + case KEY_RALT: gbVT_AltDown |= 2; break; + case KEY_LCTRL: gbVT_CtrlDown |= 1; break; + case KEY_RCTRL: gbVT_CtrlDown |= 2; break; + } + + if(!gbVT_AltDown || !gbVT_CtrlDown) + break; + switch(Codepoint & KEY_CODEPOINT_MASK) + { + case KEY_F1: VT_SetTerminal(0); return; + case KEY_F2: VT_SetTerminal(1); return; + case KEY_F3: VT_SetTerminal(2); return; + case KEY_F4: VT_SetTerminal(3); return; + case KEY_F5: VT_SetTerminal(4); return; + case KEY_F6: VT_SetTerminal(5); return; + case KEY_F7: VT_SetTerminal(6); return; + case KEY_F8: VT_SetTerminal(7); return; + case KEY_F9: VT_SetTerminal(8); return; + case KEY_F10: VT_SetTerminal(9); return; + case KEY_F11: VT_SetTerminal(10); return; + case KEY_F12: VT_SetTerminal(11); return; + } + + // Scrolling is only valid in text mode + if(gpVT_CurTerm->Mode != TERM_MODE_TEXT) + break; + + switch(Codepoint & KEY_CODEPOINT_MASK) + { + // Scrolling + case KEY_PGUP: + if( gpVT_CurTerm->Flags & VT_FLAG_ALTBUF ) + return ; + gpVT_CurTerm->ViewPos = MAX( + 0, + gpVT_CurTerm->ViewPos - gpVT_CurTerm->Width + ); + return; + case KEY_PGDOWN: + if( gpVT_CurTerm->Flags & VT_FLAG_ALTBUF ) + return ; + gpVT_CurTerm->ViewPos = MIN( + gpVT_CurTerm->ViewPos + gpVT_CurTerm->Width, + gpVT_CurTerm->Width * gpVT_CurTerm->Height*giVT_Scrollback + ); + return; + } + break; + } + + // Encode key + if(term->Mode == TERM_MODE_TEXT) + { + Uint8 buf[6] = {0}; + int len = 0; + + // Ignore anything that isn't a press or refire + if( (Codepoint & KEY_ACTION_MASK) != KEY_ACTION_PRESS + && (Codepoint & KEY_ACTION_MASK) != KEY_ACTION_REFIRE + ) + { + return ; + } + + Codepoint &= KEY_CODEPOINT_MASK; + + // Ignore Modifer Keys + if(Codepoint > KEY_MODIFIERS) return; + + // Get UTF-8/ANSI Encoding + switch(Codepoint) + { + // 0: No translation, don't send to user + case 0: break; + case KEY_LEFT: + buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'D'; + len = 3; + break; + case KEY_RIGHT: + buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'C'; + len = 3; + break; + case KEY_UP: + buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'A'; + len = 3; + break; + case KEY_DOWN: + buf[0] = '\x1B'; buf[1] = '['; buf[2] = 'B'; + len = 3; + break; + + case KEY_PGUP: + buf[0] = '\x1B'; buf[1] = '['; buf[2] = '5'; buf[3] = '~'; + len = 4; + break; + case KEY_PGDOWN: + buf[0] = '\x1B'; buf[1] = '['; buf[2] = '6'; buf[3] = '~'; + len = 4; + break; + + // Attempt to encode in UTF-8 + default: + len = WriteUTF8( buf, Codepoint ); + if(len == 0) { + Warning("Codepoint (%x) is unrepresentable in UTF-8", Codepoint); + } + break; + } + + if(len == 0) { + // Unprintable / Don't Pass + return; + } + +#if 0 + // Handle meta characters + if( !(term->Flags & VT_FLAG_RAWIN) ) + { + switch(buf[0]) + { + case '\3': // ^C + + break; + } + } +#endif + + // Write + if( MAX_INPUT_CHARS8 - term->InputWrite >= len ) + memcpy( &term->InputBuffer[term->InputWrite], buf, len ); + else { + memcpy( &term->InputBuffer[term->InputWrite], buf, MAX_INPUT_CHARS8 - term->InputWrite ); + memcpy( &term->InputBuffer[0], buf, len - (MAX_INPUT_CHARS8 - term->InputWrite) ); + } + // Roll the buffer over + term->InputWrite += len; + term->InputWrite %= MAX_INPUT_CHARS8; + if( (term->InputWrite - term->InputRead + MAX_INPUT_CHARS8)%MAX_INPUT_CHARS8 < len ) { + term->InputRead = term->InputWrite + 1; + term->InputRead %= MAX_INPUT_CHARS8; + } + } + else + { + // Encode the raw key event + Uint32 *raw_in = (void*)term->InputBuffer; + + #if 0 + // Drop new keys + if( term->InputWrite == term->InputRead ) + return ; + #endif + + raw_in[ term->InputWrite ] = Codepoint; + term->InputWrite ++; + if(term->InputWrite >= MAX_INPUT_CHARS32) + term->InputWrite -= MAX_INPUT_CHARS32; + + #if 1 + // TODO: Should old or new be dropped? + if(term->InputRead == term->InputWrite) { + term->InputRead ++; + if( term->InputRead >= MAX_INPUT_CHARS32 ) + term->InputRead -= MAX_INPUT_CHARS32; + } + #endif + } + + VFS_MarkAvaliable(&term->Node, 1); +} + diff --git a/KernelLand/Kernel/drv/vterm_output.c b/KernelLand/Kernel/drv/vterm_output.c new file mode 100644 index 00000000..95a2bce8 --- /dev/null +++ b/KernelLand/Kernel/drv/vterm_output.c @@ -0,0 +1,171 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * drv/vterm_input.c + * - Virtual Terminal - Input code + */ +#include "vterm.h" +#include + +// === CODE === +/** + * \fn void VT_InitOutput() + * \brief Initialise Video Output + */ +void VT_InitOutput() +{ + giVT_OutputDevHandle = VFS_Open(gsVT_OutputDevice, VFS_OPENFLAG_WRITE); + if(giVT_OutputDevHandle == -1) { + Log_Warning("VTerm", "Oh F**k, I can't open the video device '%s'", gsVT_OutputDevice); + return ; + } + VT_SetResolution( giVT_RealWidth, giVT_RealHeight ); + VT_SetTerminal( 0 ); + VT_SetMode( VIDEO_BUFFMT_TEXT ); +} + +/** + * \brief Set video output buffer mode + */ +void VT_SetMode(int Mode) +{ + VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &Mode ); +} + +/** + * \fn void VT_int_ScrollFramebuffer( tVTerm *Term, int Count ) + * \note Scrolls the framebuffer down by \a Count text lines + */ +void VT_int_ScrollFramebuffer( tVTerm *Term, int Count ) +{ + int tmp; + struct { + Uint8 Op; + Uint16 DstX, DstY; + Uint16 SrcX, SrcY; + Uint16 W, H; + } PACKED buf; + + // Only update if this is the current terminal + if( Term != gpVT_CurTerm ) return; + + if( Count > Term->ScrollHeight ) Count = Term->ScrollHeight; + if( Count < -Term->ScrollHeight ) Count = -Term->ScrollHeight; + + // Switch to 2D Command Stream + tmp = VIDEO_BUFFMT_2DSTREAM; + VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp); + + // BLIT to 0,0 from 0,giVT_CharHeight + buf.Op = VIDEO_2DOP_BLIT; + buf.SrcX = 0; buf.DstX = 0; + // TODO: Don't assume character dimensions + buf.W = Term->TextWidth * giVT_CharWidth; + if( Count > 0 ) + { + buf.SrcY = (Term->ScrollTop+Count) * giVT_CharHeight; + buf.DstY = Term->ScrollTop * giVT_CharHeight; + } + else // Scroll up, move text down + { + Count = -Count; + buf.SrcY = Term->ScrollTop * giVT_CharHeight; + buf.DstY = (Term->ScrollTop+Count) * giVT_CharHeight; + } + buf.H = (Term->ScrollHeight-Count) * giVT_CharHeight; + VFS_WriteAt(giVT_OutputDevHandle, 0, sizeof(buf), &buf); + + // Restore old mode (this function is only called during text mode) + tmp = VIDEO_BUFFMT_TEXT; + VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETBUFFORMAT, &tmp); +} + +void VT_int_UpdateCursor( tVTerm *Term, int bShow ) +{ + tVideo_IOCtl_Pos csr_pos; + + if( Term != gpVT_CurTerm ) return ; + + if( !bShow ) + { + csr_pos.x = -1; + csr_pos.y = -1; + } + else if( Term->Mode == TERM_MODE_TEXT ) + { + int offset; + +// if( !(Term->Flags & VT_FLAG_SHOWCSR) +// && ( (Term->Flags & VT_FLAG_HIDECSR) || !Term->Node.ReadThreads) +// ) + if( !Term->Text || Term->Flags & VT_FLAG_HIDECSR ) + { + csr_pos.x = -1; + csr_pos.y = -1; + } + else + { + if(Term->Flags & VT_FLAG_ALTBUF) + offset = Term->AltWritePos; + else + offset = Term->WritePos - Term->ViewPos; + + csr_pos.x = offset % Term->TextWidth; + csr_pos.y = offset / Term->TextWidth; + if( 0 > csr_pos.y || csr_pos.y >= Term->TextHeight ) + csr_pos.y = -1, csr_pos.x = -1; + } + } + else + { + csr_pos.x = Term->VideoCursorX; + csr_pos.y = Term->VideoCursorY; + } + VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSOR, &csr_pos); +} + +/** + * \fn void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) + * \brief Updates the video framebuffer + */ +void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) +{ + tVT_Char *buffer; + int view_pos, write_pos; + // Only update if this is the current terminal + if( Term != gpVT_CurTerm ) return; + + switch( Term->Mode ) + { + case TERM_MODE_TEXT: + view_pos = (Term->Flags & VT_FLAG_ALTBUF) ? 0 : Term->ViewPos; + write_pos = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltWritePos : Term->WritePos; + buffer = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text; + // Re copy the entire screen? + if(UpdateAll) { + VFS_WriteAt( + giVT_OutputDevHandle, + 0, + Term->TextWidth*Term->TextHeight*sizeof(tVT_Char), + &buffer[view_pos] + ); + } + // Only copy the current line + else { + int ofs = write_pos - write_pos % Term->TextWidth; + VFS_WriteAt( + giVT_OutputDevHandle, + (ofs - view_pos)*sizeof(tVT_Char), + Term->TextWidth*sizeof(tVT_Char), + &buffer[ofs] + ); + } + break; + case TERM_MODE_FB: + break; + } + + VT_int_UpdateCursor(Term, 1); +} + diff --git a/KernelLand/Kernel/drv/vterm_termbuf.c b/KernelLand/Kernel/drv/vterm_termbuf.c new file mode 100644 index 00000000..2511048c --- /dev/null +++ b/KernelLand/Kernel/drv/vterm_termbuf.c @@ -0,0 +1,398 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * drv/vterm_termbuf.c + * - Virtual Terminal - Terminal buffer manipulation + */ +#include "vterm.h" + +// === CODE === + +/** + * \fn void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count) + * \brief Print a string to the Virtual Terminal + */ +void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count) +{ + Uint32 val; + int i; + + // Iterate + for( i = 0; i < Count; i++ ) + { + // Handle escape sequences + if( Buffer[i] == 0x1B ) + { + i ++; + i += VT_int_ParseEscape(Term, (const char*)&Buffer[i]) - 1; + continue; + } + + // Fast check for non UTF-8 + if( Buffer[i] < 128 ) // Plain ASCII + VT_int_PutChar(Term, Buffer[i]); + else { // UTF-8 + i += ReadUTF8(&Buffer[i], &val) - 1; + VT_int_PutChar(Term, val); + } + } + // Update Screen + VT_int_UpdateScreen( Term, 0 ); +} + +/** + * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch) + * \brief Write a single character to a VTerm + */ +void VT_int_PutChar(tVTerm *Term, Uint32 Ch) +{ + int i; + tVT_Char *buffer; + int write_pos; + + if(Term->Flags & VT_FLAG_ALTBUF) { + buffer = Term->AltBuf; + write_pos = Term->AltWritePos; + } + else { + buffer = Term->Text; + write_pos = Term->WritePos; + } + + switch(Ch) + { + case '\0': return; // Ignore NULL byte + case '\n': + VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining + write_pos += Term->TextWidth; + case '\r': + write_pos -= write_pos % Term->TextWidth; + break; + + case '\t': { int tmp = write_pos / Term->TextWidth; + write_pos %= Term->TextWidth; + do { + buffer[ write_pos ].Ch = '\0'; + buffer[ write_pos ].Colour = Term->CurColour; + write_pos ++; + } while(write_pos & 7); + write_pos += tmp * Term->TextWidth; + break; } + + case '\b': + // Backspace is invalid at Offset 0 + if(write_pos == 0) break; + + write_pos --; + // Singe Character + if(buffer[ write_pos ].Ch != '\0') { + buffer[ write_pos ].Ch = 0; + buffer[ write_pos ].Colour = Term->CurColour; + break; + } + // Tab + i = 7; // Limit it to 8 + do { + buffer[ write_pos ].Ch = 0; + buffer[ write_pos ].Colour = Term->CurColour; + write_pos --; + } while(write_pos && i-- && buffer[ write_pos ].Ch == '\0'); + if(buffer[ write_pos ].Ch != '\0') + write_pos ++; + break; + + default: + buffer[ write_pos ].Ch = Ch; + buffer[ write_pos ].Colour = Term->CurColour; + // Update the line before wrapping + if( (write_pos + 1) % Term->TextWidth == 0 ) + VT_int_UpdateScreen( Term, 0 ); + write_pos ++; + break; + } + + if(Term->Flags & VT_FLAG_ALTBUF) + { + Term->AltBuf = buffer; + Term->AltWritePos = write_pos; + + if(Term->AltWritePos >= Term->TextWidth*Term->TextHeight) + { + Term->AltWritePos -= Term->TextWidth; + VT_int_ScrollText(Term, 1); + } + + } + else + { + Term->Text = buffer; + Term->WritePos = write_pos; + // Move Screen + // - Check if we need to scroll the entire scrollback buffer + if(Term->WritePos >= Term->TextWidth*Term->TextHeight*(giVT_Scrollback+1)) + { + int base; + + // Update previous line + Term->WritePos -= Term->TextWidth; + VT_int_UpdateScreen( Term, 0 ); + + // Update view position + base = Term->TextWidth*Term->TextHeight*(giVT_Scrollback); + if(Term->ViewPos < base) + Term->ViewPos += Term->Width; + if(Term->ViewPos > base) + Term->ViewPos = base; + + VT_int_ScrollText(Term, 1); + } + // Ok, so we only need to scroll the screen + else if(Term->WritePos >= Term->ViewPos + Term->TextWidth*Term->TextHeight) + { + // Update the last line + Term->WritePos -= Term->TextWidth; + VT_int_UpdateScreen( Term, 0 ); + Term->WritePos += Term->TextWidth; + + VT_int_ScrollText(Term, 1); + + Term->ViewPos += Term->TextWidth; + } + } + + //LEAVE('-'); +} + +void VT_int_ScrollText(tVTerm *Term, int Count) +{ + tVT_Char *buf; + int height, init_write_pos; + int len, i; + int scroll_top, scroll_height; + + // Get buffer pointer and attributes + if( Term->Flags & VT_FLAG_ALTBUF ) + { + buf = Term->AltBuf; + height = Term->TextHeight; + init_write_pos = Term->AltWritePos; + scroll_top = Term->ScrollTop; + scroll_height = Term->ScrollHeight; + } + else + { + buf = Term->Text; + height = Term->TextHeight*(giVT_Scrollback+1); + init_write_pos = Term->WritePos; + scroll_top = 0; + scroll_height = height; + } + + // Scroll text downwards + if( Count > 0 ) + { + int base; + + // Set up + if(Count > scroll_height) Count = scroll_height; + base = Term->TextWidth*(scroll_top + scroll_height - Count); + len = Term->TextWidth*(scroll_height - Count); + + // Scroll terminal cache + memmove( + &buf[Term->TextWidth*scroll_top], + &buf[Term->TextWidth*(scroll_top+Count)], + len*sizeof(tVT_Char) + ); + // Clear last rows + for( i = 0; i < Term->TextWidth*Count; i ++ ) + { + buf[ base + i ].Ch = 0; + buf[ base + i ].Colour = Term->CurColour; + } + + // Update Screen + VT_int_ScrollFramebuffer( Term, Count ); + if( Term->Flags & VT_FLAG_ALTBUF ) + Term->AltWritePos = base; + else + Term->WritePos = Term->ViewPos + Term->TextWidth*(Term->TextHeight - Count); + for( i = 0; i < Count; i ++ ) + { + VT_int_UpdateScreen( Term, 0 ); + if( Term->Flags & VT_FLAG_ALTBUF ) + Term->AltWritePos += Term->TextWidth; + else + Term->WritePos += Term->TextWidth; + } + } + else + { + Count = -Count; + if(Count > scroll_height) Count = scroll_height; + + len = Term->TextWidth*(scroll_height - Count); + + // Scroll terminal cache + memmove( + &buf[Term->TextWidth*(scroll_top+Count)], + &buf[Term->TextWidth*scroll_top], + len*sizeof(tVT_Char) + ); + // Clear preceding rows + for( i = 0; i < Term->TextWidth*Count; i ++ ) + { + buf[ i ].Ch = 0; + buf[ i ].Colour = Term->CurColour; + } + + VT_int_ScrollFramebuffer( Term, -Count ); + if( Term->Flags & VT_FLAG_ALTBUF ) + Term->AltWritePos = Term->TextWidth*scroll_top; + else + Term->WritePos = Term->ViewPos; + for( i = 0; i < Count; i ++ ) + { + VT_int_UpdateScreen( Term, 0 ); + if( Term->Flags & VT_FLAG_ALTBUF ) + Term->AltWritePos += Term->TextWidth; + else + Term->WritePos += Term->TextWidth; + } + } + + if( Term->Flags & VT_FLAG_ALTBUF ) + Term->AltWritePos = init_write_pos; + else + Term->WritePos = init_write_pos; +} + +/** + * \brief Clears a line in a virtual terminal + * \param Term Terminal to modify + * \param Num Line number to clear + */ +void VT_int_ClearLine(tVTerm *Term, int Num) +{ + int i; + tVT_Char *cell; + + if( Num < 0 || Num >= Term->TextHeight * (giVT_Scrollback + 1) ) return ; + + cell = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text; + cell = &cell[ Num*Term->TextWidth ]; + + for( i = Term->TextWidth; i--; ) + { + cell[ i ].Ch = 0; + cell[ i ].Colour = Term->CurColour; + } +} + +/** + * \brief Update the screen mode + * \param Term Terminal to update + * \param NewMode New mode to set + * \param NewWidth New framebuffer width + * \param NewHeight New framebuffer height + */ +void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight) +{ + int oldW = Term->Width; + int oldTW = Term->TextWidth; + int oldH = Term->Height; + int oldTH = Term->TextHeight; + tVT_Char *oldTBuf = Term->Text; + Uint32 *oldFB = Term->Buffer; + int w, h, i; + + // TODO: Increase RealWidth/RealHeight when this happens + if(NewWidth > giVT_RealWidth) NewWidth = giVT_RealWidth; + if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight; + + Term->Mode = NewMode; + + // Fast exit if no resolution change + if(NewWidth == Term->Width && NewHeight == Term->Height) + return ; + + // Calculate new dimensions + Term->Width = NewWidth; + Term->Height = NewHeight; + Term->TextWidth = NewWidth / giVT_CharWidth; + Term->TextHeight = NewHeight / giVT_CharHeight; + Term->ScrollHeight = Term->TextHeight - (oldTH - Term->ScrollHeight) - Term->ScrollTop; + + // Allocate new buffers + // - Text + Term->Text = calloc( + Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1), + sizeof(tVT_Char) + ); + if(oldTBuf) { + // Copy old buffer + w = (oldTW > Term->TextWidth) ? Term->TextWidth : oldTW; + h = (oldTH > Term->TextHeight) ? Term->TextHeight : oldTH; + h *= giVT_Scrollback + 1; + for( i = 0; i < h; i ++ ) + { + memcpy( + &Term->Text[i*Term->TextWidth], + &oldTBuf[i*oldTW], + w*sizeof(tVT_Char) + ); + } + free(oldTBuf); + } + + // - Alternate Text + Term->AltBuf = realloc( + Term->AltBuf, + Term->TextWidth * Term->TextHeight * sizeof(tVT_Char) + ); + + // - Framebuffer + if(oldFB) { + Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) ); + // Copy old buffer + w = (oldW > Term->Width) ? Term->Width : oldW; + h = (oldH > Term->Height) ? Term->Height : oldH; + for( i = 0; i < h; i ++ ) + { + memcpy( + &Term->Buffer[i*Term->Width], + &oldFB[i*oldW], + w*sizeof(Uint32) + ); + } + free(oldFB); + } + + // Debug + switch(NewMode) + { + case TERM_MODE_TEXT: + Log_Log("VTerm", "Set VT %p to text mode (%ix%i)", + Term, Term->TextWidth, Term->TextHeight); + break; + case TERM_MODE_FB: + Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)", + Term, Term->Width, Term->Height); + break; + //case TERM_MODE_2DACCEL: + //case TERM_MODE_3DACCEL: + // return; + } +} + + +void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled) +{ + if(Enabled) + Term->Flags |= VT_FLAG_ALTBUF; + else + Term->Flags &= ~VT_FLAG_ALTBUF; + VT_int_UpdateScreen(Term, 1); +} + diff --git a/KernelLand/Kernel/drv/vterm_vt100.c b/KernelLand/Kernel/drv/vterm_vt100.c new file mode 100644 index 00000000..fb152a30 --- /dev/null +++ b/KernelLand/Kernel/drv/vterm_vt100.c @@ -0,0 +1,295 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * drv/vterm_vt100.c + * - Virtual Terminal - VT100 (Kinda) Emulation + */ +#include "vterm.h" + +// === CONSTANTS === +const Uint16 caVT100Colours[] = { + // Black, Red, Green, Yellow, Blue, Purple, Cyan, Gray + // Same again, but bright + VT_COL_BLACK, 0x700, 0x070, 0x770, 0x007, 0x707, 0x077, 0xAAA, + VT_COL_GREY, 0xF00, 0x0F0, 0xFF0, 0x00F, 0xF0F, 0x0FF, VT_COL_WHITE + }; + +// === CODE === +/** + * \brief Handle a standard large escape code + * + * Handles any escape code of the form \x1B[n,...A where n is an integer + * and A is any letter. + */ +void VT_int_ParseEscape_StandardLarge(tVTerm *Term, char CmdChar, int argc, int *args) +{ + int tmp = 1; + switch(CmdChar) + { + // Left + case 'D': + tmp = -1; + // Right + case 'C': + if(argc == 1) tmp *= args[0]; + if( Term->Flags & VT_FLAG_ALTBUF ) + { + if( (Term->AltWritePos + tmp) % Term->TextWidth == 0 ) { + Term->AltWritePos -= Term->AltWritePos % Term->TextWidth; + Term->AltWritePos += Term->TextWidth - 1; + } + else + Term->AltWritePos += tmp; + } + else + { + if( (Term->WritePos + tmp) % Term->TextWidth == 0 ) { + Term->WritePos -= Term->WritePos % Term->TextWidth; + Term->WritePos += Term->TextWidth - 1; + } + else + Term->WritePos += tmp; + } + break; + + // Erase + case 'J': + switch(args[0]) + { + case 0: // Erase below + break; + case 1: // Erase above + break; + case 2: // Erase all + if( Term->Flags & VT_FLAG_ALTBUF ) + { + int i = Term->TextHeight; + while( i-- ) VT_int_ClearLine(Term, i); + Term->AltWritePos = 0; + VT_int_UpdateScreen(Term, 1); + } + else + { + int i = Term->TextHeight * (giVT_Scrollback + 1); + while( i-- ) VT_int_ClearLine(Term, i); + Term->WritePos = 0; + Term->ViewPos = 0; + VT_int_UpdateScreen(Term, 1); + } + break; + } + break; + + // Erase in line + case 'K': + switch(args[0]) + { + case 0: // Erase to right + if( Term->Flags & VT_FLAG_ALTBUF ) + { + int i, max; + max = Term->Width - Term->AltWritePos % Term->Width; + for( i = 0; i < max; i ++ ) + Term->AltBuf[Term->AltWritePos+i].Ch = 0; + } + else + { + int i, max; + max = Term->Width - Term->WritePos % Term->Width; + for( i = 0; i < max; i ++ ) + Term->Text[Term->WritePos+i].Ch = 0; + } + VT_int_UpdateScreen(Term, 0); + break; + case 1: // Erase to left + if( Term->Flags & VT_FLAG_ALTBUF ) + { + int i = Term->AltWritePos % Term->Width; + while( i -- ) + Term->AltBuf[Term->AltWritePos++].Ch = 0; + } + else + { + int i = Term->WritePos % Term->Width; + while( i -- ) + Term->Text[Term->WritePos++].Ch = 0; + } + VT_int_UpdateScreen(Term, 0); + break; + case 2: // Erase all + if( Term->Flags & VT_FLAG_ALTBUF ) + { + VT_int_ClearLine(Term, Term->AltWritePos / Term->Width); + } + else + { + VT_int_ClearLine(Term, Term->WritePos / Term->Width); + } + VT_int_UpdateScreen(Term, 0); + break; + } + break; + + // Set cursor position + case 'H': + if( Term->Flags & VT_FLAG_ALTBUF ) + Term->AltWritePos = args[0] + args[1]*Term->TextWidth; + else + Term->WritePos = args[0] + args[1]*Term->TextWidth; + //Log_Debug("VTerm", "args = {%i, %i}", args[0], args[1]); + break; + + // Scroll up `n` lines + case 'S': + tmp = -1; + // Scroll down `n` lines + case 'T': + if(argc == 1) tmp *= args[0]; + if( Term->Flags & VT_FLAG_ALTBUF ) + VT_int_ScrollText(Term, tmp); + else + { + if(Term->ViewPos/Term->TextWidth + tmp < 0) + break; + if(Term->ViewPos/Term->TextWidth + tmp > Term->TextHeight * (giVT_Scrollback + 1)) + break; + + Term->ViewPos += Term->TextWidth*tmp; + } + break; + + // Set Font flags + case 'm': + for( ; argc--; ) + { + int colour_idx; + // Flags + if( 0 <= args[argc] && args[argc] <= 8) + { + switch(args[argc]) + { + case 0: Term->CurColour = DEFAULT_COLOUR; break; // Reset + case 1: Term->CurColour |= 0x80000000; break; // Bright + case 2: Term->CurColour &= ~0x80000000; break; // Dim + } + } + // Foreground Colour + else if(30 <= args[argc] && args[argc] <= 37) { + // Get colour index, accounting for bright bit + colour_idx = args[argc]-30 + ((Term->CurColour>>28) & 8); + Term->CurColour &= 0x8000FFFF; + Term->CurColour |= (Uint32)caVT100Colours[ colour_idx ] << 16; + } + // Background Colour + else if(40 <= args[argc] && args[argc] <= 47) { + // Get colour index, accounting for bright bit + colour_idx = args[argc]-40 + ((Term->CurColour>>12) & 8); + Term->CurColour &= 0xFFFF8000; + Term->CurColour |= caVT100Colours[ colour_idx ]; + } + else { + Log_Warning("VTerm", "Unknown font flag %i", args[argc]); + } + } + break; + + // Set scrolling region + case 'r': + if( argc != 2 ) break; + Term->ScrollTop = args[0]; + Term->ScrollHeight = args[1] - args[0]; + break; + + default: + Log_Warning("VTerm", "Unknown control sequence '\\x1B[%c'", CmdChar); + break; + } +} + +/** + * \fn int VT_int_ParseEscape(tVTerm *Term, const char *Buffer) + * \brief Parses a VT100 Escape code + */ +int VT_int_ParseEscape(tVTerm *Term, const char *Buffer) +{ + char c; + int argc = 0, j = 1; + int args[6] = {0,0,0,0}; + int bQuestionMark = 0; + + switch(Buffer[0]) + { + //Large Code + case '[': + // Get Arguments + c = Buffer[j++]; + if(c == '?') { + bQuestionMark = 1; + c = Buffer[j++]; + } + if( '0' <= c && c <= '9' ) + { + do { + if(c == ';') c = Buffer[j++]; + while('0' <= c && c <= '9') { + args[argc] *= 10; + args[argc] += c-'0'; + c = Buffer[j++]; + } + argc ++; + } while(c == ';'); + } + + // Get Command + if( ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) + { + if( bQuestionMark ) + { + switch(c) + { + // DEC Private Mode Set + case 'h': + if(argc != 1) break; + switch(args[0]) + { + case 25: + Term->Flags &= ~VT_FLAG_HIDECSR; + break; + case 1047: + VT_int_ToggleAltBuffer(Term, 1); + break; + } + break; + case 'l': + if(argc != 1) break; + switch(args[0]) + { + case 25: + Term->Flags |= VT_FLAG_HIDECSR; + break; + case 1047: + VT_int_ToggleAltBuffer(Term, 0); + break; + } + break; + default: + Log_Warning("VTerm", "Unknown control sequence '\\x1B[?%c'", c); + break; + } + } + else + { + VT_int_ParseEscape_StandardLarge(Term, c, argc, args); + } + } + break; + + default: + Log_Notice("VTerm", "TODO: Handle short escape codes"); + break; + } + + //Log_Debug("VTerm", "j = %i, Buffer = '%s'", j, Buffer); + return j; +} diff --git a/KernelLand/Kernel/drvutil.c b/KernelLand/Kernel/drvutil.c new file mode 100644 index 00000000..d19a0504 --- /dev/null +++ b/KernelLand/Kernel/drvutil.c @@ -0,0 +1,714 @@ +/* + * Acess2 Kernel + * - By John Hodge + * + * drvutil.c + * - Common Driver/Filesystem Helper Functions + */ +#define DEBUG 0 +#include +#include +#include + +// === TYPES === + +// === PROTOTYPES === +//int DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers); +//size_t DrvUtil_Video_WriteLFB(int Mode, tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, void *Src); +//void DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap); +//void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y); +void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf); +//void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf); +void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour); +void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H); + +// === GLOBALS === +tDrvUtil_Video_2DHandlers gDrvUtil_Stub_2DFunctions = { + NULL, + DrvUtil_Video_2D_Fill, + DrvUtil_Video_2D_Blit +}; +tVideo_IOCtl_Bitmap gDrvUtil_TextModeCursor = { + 8, 16, + 0, 0, + { + 0, 0, 0 , 0, 0, 0, 0, 0, + 0,-1, 0xFF000000, 0, 0, 0, 0, 0, + 0,-1, 0xFF000000, 0, 0, 0, 0, 0, + 0,-1, 0xFF000000, 0, 0, 0, 0, 0, + 0,-1, 0xFF000000, 0, 0, 0, 0, 0, + 0,-1, 0xFF000000, 0, 0, 0, 0, 0, + 0,-1, 0xFF000000, 0, 0, 0, 0, 0, + 0,-1, 0xFF000000, 0, 0, 0, 0, 0, + 0,-1, 0xFF000000, 0, 0, 0, 0, 0, + 0,-1, 0xFF000000, 0, 0, 0, 0, 0, + 0,-1, 0xFF000000, 0, 0, 0, 0, 0, + 0,-1, 0xFF000000, 0, 0, 0, 0, 0, + 0,-1, 0xFF000000, 0, 0, 0, 0, 0, + 0,-1, 0xFF000000, 0, 0, 0, 0, 0, + 0, 0, 0 , 0, 0, 0, 0, 0, + 0, 0, 0 , 0, 0, 0, 0, 0 + } +}; + +// === CODE === +// --- Video Driver Helpers --- +int DrvUtil_Video_2DStream(void *Ent, const void *Buffer, int Length, + tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers) +{ + const Uint8 *stream = Buffer; + int rem = Length; + int op; + while( rem ) + { + rem --; + op = *stream; + stream ++; + + if(op > NUM_VIDEO_2DOPS) { + Log_Warning("DrvUtil", + "DrvUtil_Video_2DStream: Unknown operation %i", + op); + return Length-rem; + } + + if(op*sizeof(void*) > SizeofHandlers) { + Log_Warning("DrvUtil", + "DrvUtil_Video_2DStream: Driver does not support op %i", + op); + return Length-rem; + } + + switch(op) + { + case VIDEO_2DOP_NOP: break; + + case VIDEO_2DOP_FILL: + if(rem < 10) return Length-rem; + + if(!Handlers->Fill) { + Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver" + " does not support VIDEO_2DOP_FILL"); + return Length-rem; + } + + Handlers->Fill( + Ent, + ((const Uint16*)stream)[0], ((const Uint16*)stream)[1], + ((const Uint16*)stream)[2], ((const Uint16*)stream)[3], + ((const Uint32*)stream)[4] + ); + + rem -= 10; + stream += 10; + break; + + case VIDEO_2DOP_BLIT: + if(rem < 12) return Length-rem; + + if(!Handlers->Blit) { + Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver" + " does not support VIDEO_2DOP_BLIT"); + return Length-rem; + } + + Handlers->Blit( + Ent, + ((const Uint16*)stream)[0], ((const Uint16*)stream)[1], + ((const Uint16*)stream)[2], ((const Uint16*)stream)[3], + ((const Uint16*)stream)[4], ((const Uint16*)stream)[5] + ); + + rem -= 12; + stream += 12; + break; + + } + } + return 0; +} + +int DrvUtil_Video_WriteLFB(tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, const void *Buffer) +{ + Uint8 *dest; + const Uint32 *src = Buffer; + int csr_x, csr_y; + int x, y; + int bytes_per_px = (FBInfo->Depth + 7) / 8; + ENTER("pFBInfo xOffset xLength pBuffer", + Mode, FBInfo, Offset, Length, Buffer); + + csr_x = FBInfo->CursorX; + csr_y = FBInfo->CursorY; + + DrvUtil_Video_RemoveCursor(FBInfo); + + switch( FBInfo->BufferFormat ) + { + case VIDEO_BUFFMT_TEXT: + { + const tVT_Char *chars = Buffer; + int widthInChars = FBInfo->Width/giVT_CharWidth; + int heightInChars = FBInfo->Height/giVT_CharHeight; + int i; + + LOG("bytes_per_px = %i", bytes_per_px); + LOG("widthInChars = %i, heightInChars = %i", widthInChars, heightInChars); + + Length /= sizeof(tVT_Char); Offset /= sizeof(tVT_Char); + + x = Offset % widthInChars; y = Offset / widthInChars; + LOG("x = %i, y = %i", x, y); + + // Sanity Check + if(Offset > heightInChars * widthInChars) LEAVE_RET('i', 0); + if(y >= heightInChars) LEAVE_RET('i', 0); + + if( Offset + Length > heightInChars*widthInChars ) + { + Length = heightInChars*widthInChars - Offset; + } + + dest = FBInfo->Framebuffer; + LOG("dest = %p", dest); + dest += y * giVT_CharHeight * FBInfo->Pitch; + LOG("dest = %p", dest); + + for( i = 0; i < Length; i++ ) + { + if( y >= heightInChars ) + { + Log_Notice("DrvUtil", "Stopped at %i", i); + break; + } + + VT_Font_Render( + chars->Ch, + dest + x*giVT_CharWidth*bytes_per_px, FBInfo->Depth, FBInfo->Pitch, + VT_Colour12toN(chars->BGCol, FBInfo->Depth), + VT_Colour12toN(chars->FGCol, FBInfo->Depth) + ); + + chars ++; + x ++; + if( x >= widthInChars ) + { + x = 0; + y ++; + dest += FBInfo->Pitch*giVT_CharHeight; + LOG("dest = %p", dest); + } + } + Length = i * sizeof(tVT_Char); + } + break; + + case VIDEO_BUFFMT_FRAMEBUFFER: + if(FBInfo->Width*FBInfo->Height*4 < Offset+Length) + { + Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Framebuffer Overflow"); + return 0; + } + + switch(FBInfo->Depth) + { + case 15: + case 16: + Log_Warning("DrvUtil", "TODO: Support 15/16 bpp modes in LFB write"); + break; + case 24: + x = Offset % FBInfo->Width; + y = Offset / FBInfo->Width; + dest = (Uint8*)FBInfo->Framebuffer + y*FBInfo->Pitch; + for( ; Length >= 4; Length -= 4 ) + { + dest[x*3+0] = *src & 0xFF; + dest[x*3+1] = (*src >> 8) & 0xFF; + dest[x*3+2] = (*src >> 16) & 0xFF; + x ++; + if(x == FBInfo->Width) { + dest += FBInfo->Pitch; + x = 0; + } + } + break; + case 32: + // Copy to Frambuffer + if( FBInfo->Pitch != FBInfo->Width*4 ) + { + Uint32 *px; + // Pitch isn't 4*Width + x = Offset % FBInfo->Width; + y = Offset / FBInfo->Height; + + px = (Uint32*)FBInfo->Framebuffer + y*FBInfo->Pitch/4; + + for( ; Length >= 4; Length -= 4, x ) + { + px[x++] = *src ++; + if( x == FBInfo->Width ) { + x = 0; + px += FBInfo->Pitch; + } + } + } + else + { + dest = (Uint8 *)FBInfo->Framebuffer + Offset; + memcpy(dest, Buffer, Length); + } + break; + default: + Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Unknown bit depthn %i", FBInfo->Depth); + break; + } + break; + + case VIDEO_BUFFMT_2DSTREAM: + Length = DrvUtil_Video_2DStream( + FBInfo, Buffer, Length, + &gDrvUtil_Stub_2DFunctions, sizeof(gDrvUtil_Stub_2DFunctions) + ); + break; + + default: + LEAVE('i', -1); + return -1; + } + + DrvUtil_Video_DrawCursor(FBInfo, csr_x, csr_y); + + LEAVE('x', Length); + return Length; +} + +int DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap) +{ + int csrX = Buf->CursorX, csrY = Buf->CursorY; + size_t size; + + // Clear old bitmap + if( Buf->CursorBitmap ) + { + DrvUtil_Video_RemoveCursor(Buf); + if( !Bitmap || Bitmap->W != Buf->CursorBitmap->W || Bitmap->H != Buf->CursorBitmap->H ) + { + free( Buf->CursorSaveBuf ); + Buf->CursorSaveBuf = NULL; + } + if( Buf->CursorBitmap != &gDrvUtil_TextModeCursor) + free(Buf->CursorBitmap); + Buf->CursorBitmap = NULL; + } + + // If the new bitmap is null, disable drawing + if( !Bitmap ) + { + Buf->CursorX = -1; + Buf->CursorY = -1; + return 0; + } + + // Sanity check the bitmap + if( !CheckMem(Bitmap, sizeof(*Bitmap)) || !CheckMem(Bitmap->Data, Bitmap->W*Bitmap->H*sizeof(Uint32)) ) + { + Log_Warning("DrvUtil", "DrvUtil_Video_SetCursor: Bitmap (%p) is in invalid memory", Bitmap); + errno = -EINVAL; + return -1; + } + + // Don't take a copy of the DrvUtil provided cursor + if( Bitmap == &gDrvUtil_TextModeCursor ) + { + Buf->CursorBitmap = Bitmap; + } + else + { + size = sizeof(tVideo_IOCtl_Bitmap) + Bitmap->W*Bitmap->H*4; + + // Take a copy + Buf->CursorBitmap = malloc( size ); + memcpy(Buf->CursorBitmap, Bitmap, size); + } + + // Restore cursor position + DrvUtil_Video_DrawCursor(Buf, csrX, csrY); + return 0; +} + +void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y) +{ + int render_ox=0, render_oy=0, render_w, render_h; + + DrvUtil_Video_RemoveCursor(Buf); + + // X < 0 disables the cursor + if( X < 0 ) { + Buf->CursorX = -1; + return ; + } + + // Sanity checking + if( X < 0 || Y < 0 ) return; + if( X >= Buf->Width || Y >= Buf->Height ) return; + + // Ensure the cursor is enabled + if( !Buf->CursorBitmap ) return ; + + // Save cursor position (for changing the bitmap) + Buf->CursorX = X; Buf->CursorY = Y; + + // Apply cursor's center offset + X -= Buf->CursorBitmap->XOfs; + Y -= Buf->CursorBitmap->YOfs; + + // Get the width of the cursor on screen (clipping to right/bottom edges) + render_w = X > Buf->Width - Buf->CursorBitmap->W ? Buf->Width - X : Buf->CursorBitmap->W; + render_h = Y > Buf->Height - Buf->CursorBitmap->H ? Buf->Height - Y : Buf->CursorBitmap->H; + + // Clipp to left/top edges + if(X < 0) { render_ox = -X; X = 0; } + if(Y < 0) { render_oy = -Y; Y = 0; } + + // Save values + Buf->CursorRenderW = render_w; Buf->CursorRenderH = render_h; + Buf->CursorDestX = X; Buf->CursorDestY = Y; + Buf->CursorReadX = render_ox; Buf->CursorReadY = render_oy; + + // Call render routine + DrvUtil_Video_RenderCursor(Buf); +} + +void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf) +{ + int src_x = Buf->CursorReadX, src_y = Buf->CursorReadY; + int render_w = Buf->CursorRenderW, render_h = Buf->CursorRenderH; + int dest_x = Buf->CursorDestX, dest_y = Buf->CursorDestY; + int bytes_per_px = (Buf->Depth + 7) / 8; + int save_pitch = Buf->CursorBitmap->W * bytes_per_px; + void *dest; + Uint32 *src; + int x, y; + + dest = (Uint8*)Buf->Framebuffer + dest_y*Buf->Pitch + dest_x*bytes_per_px; + src = Buf->CursorBitmap->Data + src_y * Buf->CursorBitmap->W + src_x; + + // Allocate save buffer if not already + if( !Buf->CursorSaveBuf ) + Buf->CursorSaveBuf = malloc( Buf->CursorBitmap->W*Buf->CursorBitmap->H*bytes_per_px ); + + // Save behind the cursor + for( y = 0; y < render_h; y ++ ) + memcpy( + (Uint8*)Buf->CursorSaveBuf + y*save_pitch, + (Uint8*)dest + y*Buf->Pitch, + render_w*bytes_per_px + ); + + // Draw the cursor + switch(Buf->Depth) + { + case 15: + case 16: + Log_Warning("DrvUtil", "TODO: Support 15/16 bpp modes in cursor draw"); + break; + case 24: + for( y = 0; y < render_h; y ++ ) + { + Uint8 *px; + px = dest; + for(x = 0; x < render_w; x ++, px += 3) + { + Uint32 value = src[x]; + // TODO: Should I implement alpha blending? + if(value & 0xFF000000) + { + px[0] = value & 0xFF; + px[1] = (value >> 8) & 0xFF; + px[2] = (value >> 16) & 0xFF; + } + else + ; + } + src += Buf->CursorBitmap->W; + dest = (Uint8*)dest + Buf->Pitch; + } + break; + case 32: + for( y = 0; y < render_h; y ++ ) + { + Uint32 *px; + px = dest; + for(x = 0; x < render_w; x ++, px ++) + { + Uint32 value = src[x]; + // TODO: Should I implement alpha blending? + if(value & 0xFF000000) + *px = value; + else + ; // NOP, completely transparent + } + src += Buf->CursorBitmap->W; + dest = (Uint8*)dest + Buf->Pitch; + } + break; + default: + Log_Error("DrvUtil", "RenderCursor - Unknown bit depth %i", Buf->Depth); + Buf->CursorX = -1; + break; + } +} + +void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf) +{ + int bytes_per_px = (Buf->Depth + 7) / 8; + int y, save_pitch; + Uint8 *dest, *src; + + // Just a little sanity + if( !Buf->CursorBitmap || Buf->CursorX == -1 ) return ; + if( !Buf->CursorSaveBuf ) return ; + +// Debug("DrvUtil_Video_RemoveCursor: (Buf=%p) dest_x=%i, dest_y=%i", Buf, Buf->CursorDestX, Buf->CursorDestY); + + // Set up + save_pitch = Buf->CursorBitmap->W * bytes_per_px; + dest = (Uint8*)Buf->Framebuffer + Buf->CursorDestY * Buf->Pitch + Buf->CursorDestX*bytes_per_px; + src = Buf->CursorSaveBuf; + + // Copy each line back + for( y = 0; y < Buf->CursorRenderH; y ++ ) + { + memcpy( dest, src, Buf->CursorRenderW * bytes_per_px ); + src += save_pitch; + dest += Buf->Pitch; + } + + // Set the cursor as removed + Buf->CursorX = -1; +} + +void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour) +{ + tDrvUtil_Video_BufInfo *FBInfo = Ent; + + // TODO: Handle non-32bit modes + if( FBInfo->Depth != 32 ) return; + + // TODO: Be less hacky + int pitch = FBInfo->Pitch/4; + Uint32 *buf = (Uint32*)FBInfo->Framebuffer + Y*pitch + X; + while( H -- ) { + Uint32 *tmp; + int i; + tmp = buf; + for(i=W;i--;tmp++) *tmp = Colour; + buf += pitch; + } +} + +void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H) +{ + tDrvUtil_Video_BufInfo *FBInfo = Ent; + int scrnpitch = FBInfo->Pitch; + int bytes_per_px = (FBInfo->Depth + 7) / 8; + int dst = DstY*scrnpitch + DstX; + int src = SrcY*scrnpitch + SrcX; + int tmp; + + //Log("Vesa_2D_Blit: (Ent=%p, DstX=%i, DstY=%i, SrcX=%i, SrcY=%i, W=%i, H=%i)", + // Ent, DstX, DstY, SrcX, SrcY, W, H); + + if(SrcX + W > FBInfo->Width) W = FBInfo->Width - SrcX; + if(DstX + W > FBInfo->Width) W = FBInfo->Width - DstX; + if(SrcY + H > FBInfo->Height) H = FBInfo->Height - SrcY; + if(DstY + H > FBInfo->Height) H = FBInfo->Height - DstY; + + //Debug("W = %i, H = %i", W, H); + + if( dst > src ) { + // Reverse copy + dst += H*scrnpitch; + src += H*scrnpitch; + while( H -- ) { + dst -= scrnpitch; + src -= scrnpitch; + tmp = W*bytes_per_px; + for( tmp = W; tmp --; ) { + *((Uint8*)FBInfo->Framebuffer + dst + tmp) = *((Uint8*)FBInfo->Framebuffer + src + tmp); + } + } + } + else if(W == FBInfo->Width && FBInfo->Pitch == FBInfo->Width*bytes_per_px) { + memmove((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, H*FBInfo->Pitch); + } + else { + // Normal copy is OK + while( H -- ) { + memcpy((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, W*bytes_per_px); + dst += scrnpitch; + src += scrnpitch; + } + } + //Log("Vesa_2D_Blit: RETURN"); +} + + +// --- Disk Driver Helpers --- +Uint64 DrvUtil_ReadBlock(Uint64 Start, Uint64 Length, void *Buffer, + tDrvUtil_Read_Callback ReadBlocks, Uint64 BlockSize, Uint Argument) +{ + Uint8 tmp[BlockSize]; // C99 + Uint64 block = Start / BlockSize; + int offset = Start - block * BlockSize; + int leading = BlockSize - offset; + Uint64 num; + int tailings; + Uint64 ret; + + ENTER("XStart XLength pBuffer pReadBlocks XBlockSize xArgument", + Start, Length, Buffer, ReadBlocks, BlockSize, Argument); + + // Non aligned start, let's fix that! + if(offset != 0) + { + if(leading > Length) leading = Length; + LOG("Reading %i bytes from Block1+%i", leading, offset); + ret = ReadBlocks(block, 1, tmp, Argument); + if(ret != 1) { + LEAVE('i', 0); + return 0; + } + memcpy( Buffer, &tmp[offset], leading ); + + if(leading == Length) { + LEAVE('i', leading); + return leading; + } + + Buffer = (Uint8*)Buffer + leading; + block ++; + num = ( Length - leading ) / BlockSize; + tailings = Length - num * BlockSize - leading; + } + else { + num = Length / BlockSize; + tailings = Length % BlockSize; + } + + // Read central blocks + if(num) + { + LOG("Reading %i blocks", num); + ret = ReadBlocks(block, num, Buffer, Argument); + if(ret != num ) { + LEAVE('X', leading + ret * BlockSize); + return leading + ret * BlockSize; + } + } + + // Read last tailing block + if(tailings != 0) + { + LOG("Reading %i bytes from last block", tailings); + block += num; + Buffer = (Uint8*)Buffer + num * BlockSize; + ret = ReadBlocks(block, 1, tmp, Argument); + if(ret != 1) { + LEAVE('X', leading + num * BlockSize); + return leading + num * BlockSize; + } + memcpy( Buffer, tmp, tailings ); + } + + LEAVE('X', Length); + return Length; +} + +Uint64 DrvUtil_WriteBlock(Uint64 Start, Uint64 Length, const void *Buffer, + tDrvUtil_Read_Callback ReadBlocks, tDrvUtil_Write_Callback WriteBlocks, + Uint64 BlockSize, Uint Argument) +{ + Uint8 tmp[BlockSize]; // C99 + Uint64 block = Start / BlockSize; + int offset = Start - block * BlockSize; + int leading = BlockSize - offset; + Uint64 num; + int tailings; + Uint64 ret; + + ENTER("XStart XLength pBuffer pReadBlocks pWriteBlocks XBlockSize xArgument", + Start, Length, Buffer, ReadBlocks, WriteBlocks, BlockSize, Argument); + + // Non aligned start, let's fix that! + if(offset != 0) + { + if(leading > Length) leading = Length; + LOG("Writing %i bytes to Block1+%i", leading, offset); + // Read a copy of the block + ret = ReadBlocks(block, 1, tmp, Argument); + if(ret != 1) { + LEAVE('i', 0); + return 0; + } + // Modify + memcpy( &tmp[offset], Buffer, leading ); + // Write Back + ret = WriteBlocks(block, 1, tmp, Argument); + if(ret != 1) { + LEAVE('i', 0); + return 0; + } + + if(leading == Length) { + LEAVE('i', leading); + return leading; + } + + Buffer = (Uint8*)Buffer + leading; + block ++; + num = ( Length - leading ) / BlockSize; + tailings = Length - num * BlockSize - leading; + } + else { + num = Length / BlockSize; + tailings = Length % BlockSize; + } + + // Read central blocks + if(num) + { + LOG("Writing %i blocks", num); + ret = WriteBlocks(block, num, Buffer, Argument); + if(ret != num ) { + LEAVE('X', leading + ret * BlockSize); + return leading + ret * BlockSize; + } + } + + // Read last tailing block + if(tailings != 0) + { + LOG("Writing %i bytes to last block", tailings); + block += num; + Buffer = (Uint8*)Buffer + num * BlockSize; + // Read + ret = ReadBlocks(block, 1, tmp, Argument); + if(ret != 1) { + LEAVE('X', leading + num * BlockSize); + return leading + num * BlockSize; + } + // Modify + memcpy( tmp, Buffer, tailings ); + // Write + ret = WriteBlocks(block, 1, tmp, Argument); + if(ret != 1) { + LEAVE('X', leading + num * BlockSize); + return leading + num * BlockSize; + } + + } + + LEAVE('X', Length); + return Length; +} diff --git a/KernelLand/Kernel/events.c b/KernelLand/Kernel/events.c new file mode 100644 index 00000000..a9eab786 --- /dev/null +++ b/KernelLand/Kernel/events.c @@ -0,0 +1,90 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * events.c + * - Thread level event handling + */ +#define DEBUG 0 +#include +#include +#include + +// === CODE === +void Threads_PostEvent(tThread *Thread, Uint32 EventMask) +{ + // Sanity checking + if( !Thread ) return ; + if( EventMask == 0 ) return ; + // TODO: Check that only one bit is set? + + ENTER("pThread xEventMask", Thread, EventMask); + + SHORTLOCK( &Thread->IsLocked ); + + Thread->EventState |= EventMask; + LOG("Thread->EventState = 0x%x", Thread->EventState); + + // Currently sleeping on an event? + if( Thread->Status == THREAD_STAT_EVENTSLEEP ) + { + // Waiting on this event? + if( (Uint32)Thread->RetStatus & EventMask ) + { + // Wake up + LOG("Waking thread %p(%i %s)", Thread, Thread->TID, Thread->ThreadName); + Threads_AddActive(Thread); + } + } + + SHORTREL( &Thread->IsLocked ); + LEAVE('-'); +} + +/** + * \brief Wait for an event to occur + */ +Uint32 Threads_WaitEvents(Uint32 EventMask) +{ + Uint32 rv; + tThread *us = Proc_GetCurThread(); + + ENTER("xEventMask", EventMask); + + // Early return check + if( EventMask == 0 ) + { + LEAVE('i', 0); + return 0; + } + + LOG("us = %p(%i %s)", us, us->TID, us->ThreadName); + + // Check if a wait is needed + SHORTLOCK( &us->IsLocked ); + while( !(us->EventState & EventMask) ) + { + LOG("Locked and preparing for wait"); + // Wait + us->RetStatus = EventMask; // HACK: Store EventMask in RetStatus + SHORTLOCK( &glThreadListLock ); + us = Threads_RemActive(); + us->Status = THREAD_STAT_EVENTSLEEP; + // Note stored anywhere because we're woken using other means + SHORTREL( &glThreadListLock ); + SHORTREL( &us->IsLocked ); + while(us->Status == THREAD_STAT_EVENTSLEEP) Threads_Yield(); + // Woken when lock is acquired + SHORTLOCK( &us->IsLocked ); + } + + // Get return value and clear changed event bits + rv = us->EventState & EventMask; + us->EventState &= ~EventMask; + + SHORTREL( &us->IsLocked ); + + LEAVE('x', rv); + return rv; +} + diff --git a/KernelLand/Kernel/heap.c b/KernelLand/Kernel/heap.c new file mode 100644 index 00000000..eefd13cf --- /dev/null +++ b/KernelLand/Kernel/heap.c @@ -0,0 +1,753 @@ +/* + * AcessOS Microkernel Version + * heap.c + */ +#include +#include +#include + +#define WARNINGS 1 +#define DEBUG_TRACE 0 +#define VERBOSE_DUMP 0 + +// === CONSTANTS === +#define HEAP_INIT_SIZE 0x8000 // 32 KiB +#define MIN_SIZE (sizeof(void*))*8 // 8 Machine Words +#define POW2_SIZES 0 +#define COMPACT_HEAP 0 // Use 4 byte header? +#define FIRST_FIT 0 + +//#define MAGIC_FOOT 0x2ACE5505 +//#define MAGIC_FREE 0xACE55000 +//#define MAGIC_USED 0x862B0505 // MAGIC_FOOT ^ MAGIC_FREE +#define MAGIC_FOOT 0x544F4F46 // 'FOOT' +#define MAGIC_FREE 0x45455246 // 'FREE' +#define MAGIC_USED 0x44455355 // 'USED' + +// === PROTOTYPES === +void Heap_Install(void); +void *Heap_Extend(int Bytes); +void *Heap_Merge(tHeapHead *Head); +//void *Heap_Allocate(const char *File, int Line, size_t Bytes); +//void *Heap_AllocateZero(const char *File, int Line, size_t Bytes); +//void *Heap_Reallocate(const char *File, int Line, void *Ptr, size_t Bytes); +//void Heap_Deallocate(void *Ptr); +void Heap_Dump(void); +void Heap_Stats(void); + +// === GLOBALS === +tMutex glHeap; +void *gHeapStart; +void *gHeapEnd; + +// === CODE === +void Heap_Install(void) +{ + gHeapStart = (void*)MM_KHEAP_BASE; + gHeapEnd = (void*)MM_KHEAP_BASE; + Heap_Extend(HEAP_INIT_SIZE); +} + +/** + * \brief Extend the size of the heap + */ +void *Heap_Extend(int Bytes) +{ + Uint i; + tHeapHead *head = gHeapEnd; + tHeapFoot *foot; + + // Bounds Check + if( (tVAddr)gHeapEnd == MM_KHEAP_MAX ) + return NULL; + + if( Bytes == 0 ) { + Log_Warning("Heap", "Heap_Extend called with Bytes=%i", Bytes); + return NULL; + } + + // Bounds Check + if( (tVAddr)gHeapEnd + ((Bytes+0xFFF)&~0xFFF) > MM_KHEAP_MAX ) { +// Bytes = MM_KHEAP_MAX - (tVAddr)gHeapEnd; + return NULL; + } + + // Heap expands in pages + for( i = 0; i < (Bytes+0xFFF) >> 12; i ++ ) + { + if( !MM_Allocate( (tVAddr)gHeapEnd+(i<<12) ) ) + { + Warning("OOM - Heap_Extend"); + return NULL; + } + } + + // Increas heap end + gHeapEnd = (Uint8*)gHeapEnd + (i << 12); + + // Create Block + head->Size = (Bytes+0xFFF)&~0xFFF; + head->Magic = MAGIC_FREE; + foot = (void*)( (Uint)gHeapEnd - sizeof(tHeapFoot) ); + foot->Head = head; + foot->Magic = MAGIC_FOOT; + + return Heap_Merge(head); // Merge with previous block +} + +/** + * \brief Merges two ajacent heap blocks + */ +void *Heap_Merge(tHeapHead *Head) +{ + tHeapFoot *foot; + tHeapFoot *thisFoot; + tHeapHead *head; + + //Log("Heap_Merge: (Head=%p)", Head); + + thisFoot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) ); + if((Uint)thisFoot > (Uint)gHeapEnd) return NULL; + + // Merge Left (Down) + foot = (void*)( (Uint)Head - sizeof(tHeapFoot) ); + if( ((Uint)foot < (Uint)gHeapEnd && (Uint)foot > (Uint)gHeapStart) + && foot->Head->Magic == MAGIC_FREE) { + foot->Head->Size += Head->Size; // Increase size + thisFoot->Head = foot->Head; // Change backlink + Head->Magic = 0; // Clear old head + Head->Size = 0; + Head = foot->Head; // Save new head address + foot->Head = NULL; // Clear central footer + foot->Magic = 0; + } + + // Merge Right (Upwards) + head = (void*)( (Uint)Head + Head->Size ); + if((Uint)head < (Uint)gHeapEnd && head->Magic == MAGIC_FREE) + { + Head->Size += head->Size; + foot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) ); + foot->Head = Head; // Update Backlink + thisFoot->Head = NULL; // Clear old footer + thisFoot->Magic = 0; + head->Size = 0; // Clear old header + head->Magic = 0; + } + + // Return new address + return Head; +} + +/** + * \param File Allocating source file + * \param Line Source line + * \param __Bytes Size of region to allocate + */ +void *Heap_Allocate(const char *File, int Line, size_t __Bytes) +{ + tHeapHead *head, *newhead; + tHeapFoot *foot, *newfoot; + tHeapHead *best = NULL; + Uint bestSize = 0; // Speed hack + size_t Bytes; + + if( __Bytes == 0 ) { + //return NULL; // TODO: Return a known un-mapped range. + return INVLPTR; + } + + // Get required size + #if POW2_SIZES + Bytes = __Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot); + Bytes = 1UUL << LOG2(__Bytes); + #else + Bytes = (__Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot) + MIN_SIZE-1) & ~(MIN_SIZE-1); + #endif + + // Lock Heap + Mutex_Acquire(&glHeap); + + // Traverse Heap + for( head = gHeapStart; + (Uint)head < (Uint)gHeapEnd; + head = (void*)((Uint)head + head->Size) + ) + { + // Alignment Check + #if POW2_SIZES + if( head->Size != 1UUL << LOG2(head->Size) ) { + #else + if( head->Size & (MIN_SIZE-1) ) { + #endif + Mutex_Release(&glHeap); // Release spinlock + #if WARNINGS + Log_Warning("Heap", "Size of heap address %p is invalid not aligned (0x%x)", head, head->Size); + Heap_Dump(); + #endif + return NULL; + } + + // Check if allocated + if(head->Magic == MAGIC_USED) continue; + // Error check + if(head->Magic != MAGIC_FREE) { + Mutex_Release(&glHeap); // Release spinlock + #if WARNINGS + Log_Warning("Heap", "Magic of heap address %p is invalid (%p = 0x%x)", + head, &head->Magic, head->Magic); + Heap_Dump(); + #endif + return NULL; + } + + // Size check + if(head->Size < Bytes) continue; + + // Perfect fit + if(head->Size == Bytes) { + head->Magic = MAGIC_USED; + head->File = File; + head->Line = Line; + head->ValidSize = __Bytes; + head->AllocateTime = now(); + Mutex_Release(&glHeap); // Release spinlock + #if DEBUG_TRACE + Debug("[Heap ] Malloc'd %p (%i bytes), returning to %p", + head->Data, head->Size, __builtin_return_address(0)); + #endif + return head->Data; + } + + // Break out of loop + #if FIRST_FIT + best = head; + bestSize = head->Size; + break; + #else + // or check if the block is the best size + if(bestSize > head->Size) { + best = head; + bestSize = head->Size; + } + #endif + } + + // If no block large enough is found, create one + if(!best) + { + best = Heap_Extend( Bytes ); + // Check for errors + if(!best) { + Mutex_Release(&glHeap); // Release spinlock + return NULL; + } + // Check size + if(best->Size == Bytes) { + best->Magic = MAGIC_USED; // Mark block as used + best->File = File; + best->Line = Line; + best->ValidSize = __Bytes; + best->AllocateTime = now(); + Mutex_Release(&glHeap); // Release spinlock + #if DEBUG_TRACE + Debug("[Heap ] Malloc'd %p (%i bytes), returning to %s:%i", best->Data, best->Size, File, Line); + #endif + return best->Data; + } + } + + // Split Block + newhead = (void*)( (Uint)best + Bytes ); + newfoot = (void*)( (Uint)best + Bytes - sizeof(tHeapFoot) ); + foot = (void*)( (Uint)best + best->Size - sizeof(tHeapFoot) ); + + newfoot->Head = best; // Create new footer + newfoot->Magic = MAGIC_FOOT; + newhead->Size = best->Size - Bytes; // Create new header + newhead->Magic = MAGIC_FREE; + foot->Head = newhead; // Update backlink in old footer + best->Size = Bytes; // Update size in old header + best->ValidSize = __Bytes; + best->Magic = MAGIC_USED; // Mark block as used + best->File = File; + best->Line = Line; + best->AllocateTime = now(); + + Mutex_Release(&glHeap); // Release spinlock + #if DEBUG_TRACE + Debug("[Heap ] Malloc'd %p (0x%x bytes), returning to %s:%i", + best->Data, best->Size, File, Line); + #endif + return best->Data; +} + +/** + * \brief Free an allocated memory block + */ +void Heap_Deallocate(void *Ptr) +{ + tHeapHead *head = (void*)( (Uint)Ptr - sizeof(tHeapHead) ); + tHeapFoot *foot; + + // INVLPTR is returned from Heap_Allocate when the allocation + // size is zero. + if( Ptr == INVLPTR ) return; + + #if DEBUG_TRACE + Debug("[Heap ] free: %p freed by %p (%i old)", Ptr, __builtin_return_address(0), now()-head->AllocateTime); + #endif + + // Alignment Check + if( (Uint)Ptr & (sizeof(Uint)-1) ) { + Log_Warning("Heap", "free - Passed a non-aligned address (%p)", Ptr); + return; + } + + // Sanity check + if((Uint)Ptr < (Uint)gHeapStart || (Uint)Ptr > (Uint)gHeapEnd) + { + Log_Warning("Heap", "free - Passed a non-heap address by %p (%p < %p < %p)\n", + __builtin_return_address(0), gHeapStart, Ptr, gHeapEnd); + return; + } + + // Check memory block - Header + head = (void*)( (Uint)Ptr - sizeof(tHeapHead) ); + if(head->Magic == MAGIC_FREE) { + Log_Warning("Heap", "free - Passed a freed block (%p) by %p", head, __builtin_return_address(0)); + return; + } + if(head->Magic != MAGIC_USED) { + Log_Warning("Heap", "free - Magic value is invalid (%p, 0x%x)", head, head->Magic); + Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line); + return; + } + + // Check memory block - Footer + foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) ); + if(foot->Head != head) { + Log_Warning("Heap", "free - Footer backlink is incorrect (%p, 0x%x)", head, foot->Head); + Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line); + return; + } + if(foot->Magic != MAGIC_FOOT) { + Log_Warning("Heap", "free - Footer magic is invalid (%p, %p = 0x%x)", head, &foot->Magic, foot->Magic); + Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line); + return; + } + + // Lock + Mutex_Acquire( &glHeap ); + + // Mark as free + head->Magic = MAGIC_FREE; + //head->File = NULL; + //head->Line = 0; + head->ValidSize = 0; + // Merge blocks + Heap_Merge( head ); + + // Release + Mutex_Release( &glHeap ); +} + +/** + * \brief Increase/Decrease the size of an allocation + * \param File Calling File + * \param Line Calling Line + * \param __ptr Old memory + * \param __size New Size + */ +void *Heap_Reallocate(const char *File, int Line, void *__ptr, size_t __size) +{ + tHeapHead *head = (void*)( (Uint)__ptr-sizeof(tHeapHead) ); + tHeapHead *nextHead; + tHeapFoot *foot; + Uint newSize = (__size + sizeof(tHeapFoot)+sizeof(tHeapHead)+MIN_SIZE-1)&~(MIN_SIZE-1); + + // Check for reallocating NULL + if(__ptr == NULL) return Heap_Allocate(File, Line, __size); + + // Check if resize is needed + if(newSize <= head->Size) return __ptr; + + // Check if next block is free + nextHead = (void*)( (Uint)head + head->Size ); + + // Extend into next block + if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize) + { + Uint size = nextHead->Size + head->Size; + foot = (void*)( (Uint)nextHead + nextHead->Size - sizeof(tHeapFoot) ); + // Exact Fit + if(size == newSize) { + head->Size = newSize; + head->ValidSize = __size; + head->File = File; + head->Line = Line; + foot->Head = head; + nextHead->Magic = 0; + nextHead->Size = 0; + return __ptr; + } + // Create a new heap block + nextHead = (void*)( (Uint)head + newSize ); + nextHead->Size = size - newSize; + nextHead->Magic = MAGIC_FREE; + foot->Head = nextHead; // Edit 2nd footer + head->Size = newSize; // Edit first header + head->File = File; + head->Line = Line; + head->ValidSize = __size; + // Create new footer + foot = (void*)( (Uint)head + newSize - sizeof(tHeapFoot) ); + foot->Head = head; + foot->Magic = MAGIC_FOOT; + return __ptr; + } + + // Extend downwards? + foot = (void*)( (Uint)head - sizeof(tHeapFoot) ); + nextHead = foot->Head; + if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize) + { + Uint size = nextHead->Size + head->Size; + // Inexact fit, split things up + if(size > newSize) + { + // TODO + Warning("[Heap ] TODO: Space efficient realloc when new size is smaller"); + } + + // Exact fit + if(size >= newSize) + { + Uint oldDataSize; + // Set 1st (new/lower) header + nextHead->Magic = MAGIC_USED; + nextHead->Size = newSize; + nextHead->File = File; + nextHead->Line = Line; + nextHead->ValidSize = __size; + // Get 2nd (old) footer + foot = (void*)( (Uint)nextHead + newSize ); + foot->Head = nextHead; + // Save old data size + oldDataSize = head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead); + // Clear old header + head->Size = 0; + head->Magic = 0; + // Copy data + memcpy(nextHead->Data, __ptr, oldDataSize); + // Return + return nextHead->Data; + } + // On to the expensive then + } + + // Well, darn + nextHead = Heap_Allocate( File, Line, __size ); + nextHead -= 1; + nextHead->File = File; + nextHead->Line = Line; + nextHead->ValidSize = __size; + + memcpy( + nextHead->Data, + __ptr, + head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead) + ); + + free(__ptr); + + return nextHead->Data; +} + +/** + * \fn void *Heap_AllocateZero(const char *File, int Line, size_t Bytes) + * \brief Allocate and Zero a buffer in memory + * \param File Allocating file + * \param Line Line of allocation + * \param Bytes Size of the allocation + */ +void *Heap_AllocateZero(const char *File, int Line, size_t Bytes) +{ + void *ret = Heap_Allocate(File, Line, Bytes); + if(ret == NULL) return NULL; + + memset( ret, 0, Bytes ); + + return ret; +} + +/** + * \fn int Heap_IsHeapAddr(void *Ptr) + * \brief Checks if an address is a heap pointer + */ +int Heap_IsHeapAddr(void *Ptr) +{ + tHeapHead *head; + if((Uint)Ptr < (Uint)gHeapStart) return 0; + if((Uint)Ptr > (Uint)gHeapEnd) return 0; + if((Uint)Ptr & (sizeof(Uint)-1)) return 0; + + head = (void*)( (Uint)Ptr - sizeof(tHeapHead) ); + if(head->Magic != MAGIC_USED && head->Magic != MAGIC_FREE) + return 0; + + return 1; +} + +/** + */ +void Heap_Validate(void) +{ + Heap_Dump(); +} + +#if WARNINGS +void Heap_Dump(void) +{ + tHeapHead *head, *badHead; + tHeapFoot *foot = NULL; + + head = gHeapStart; + while( (Uint)head < (Uint)gHeapEnd ) + { + foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) ); + #if VERBOSE_DUMP + Log_Log("Heap", "%p (0x%P): 0x%08x (%i) %4C", + head, MM_GetPhysAddr((tVAddr)head), head->Size, head->ValidSize, &head->Magic); + Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic); + if(head->File) { + Log_Log("Heap", "%sowned by %s:%i", + (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line); + } + #endif + + // Sanity Check Header + if(head->Size == 0) { + Log_Warning("Heap", "HALTED - Size is zero"); + break; + } + if(head->Size & (MIN_SIZE-1)) { + Log_Warning("Heap", "HALTED - Size is malaligned"); + break; + } + if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) { + Log_Warning("Heap", "HALTED - Head Magic is Bad"); + break; + } + + // Check footer + if(foot->Magic != MAGIC_FOOT) { + Log_Warning("Heap", "HALTED - Foot Magic is Bad"); + break; + } + if(head != foot->Head) { + Log_Warning("Heap", "HALTED - Footer backlink is invalid"); + break; + } + + #if VERBOSE_DUMP + Log_Log("Heap", ""); + #endif + + // All OK? Go to next + head = foot->NextHead; + } + + // If the heap is valid, ok! + if( (tVAddr)head == (tVAddr)gHeapEnd ) + return ; + + // Check for a bad return + if( (tVAddr)head >= (tVAddr)gHeapEnd ) + return ; + + #if !VERBOSE_DUMP + Log_Log("Heap", "%p (%P): 0x%08lx %i %4C", + head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic); + if(foot) + Log_Log("Heap", "Foot %p = {Head:%p,Magic:%4C}", foot, foot->Head, &foot->Magic); + if(head->File) { + Log_Log("Heap", "%sowned by %s:%i", + (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line); + } + Log_Log("Heap", ""); + #endif + + + badHead = head; + + // Work backwards + foot = (void*)( (tVAddr)gHeapEnd - sizeof(tHeapFoot) ); + Log_Log("Heap", "==== Going Backwards ==== (from %p)", foot); + head = foot->Head; + while( (tVAddr)head >= (tVAddr)badHead ) + { + Log_Log("Heap", "%p (%P): 0x%08lx %i %4C", + head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic); + Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic); + if(head->File) + Log_Log("Heap", "%sowned by %s:%i", + (head->Magic!=MAGIC_USED?"was ":""), + head->File, head->Line); + Log_Log("Heap", ""); + + // Sanity Check Header + if(head->Size == 0) { + Log_Warning("Heap", "HALTED - Size is zero"); + break; + } + if(head->Size & (MIN_SIZE-1)) { + Log_Warning("Heap", " - Size is malaligned (&0x%x)", ~(MIN_SIZE-1)); + break ; + } + if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) { + Log_Warning("Heap", "HALTED - Head Magic is Bad"); + break; + } + + // Check footer + if(foot->Magic != MAGIC_FOOT) { + Log_Warning("Heap", "HALTED - Foot Magic is Bad"); + break; + } + if(head != foot->Head) { + Log_Warning("Heap", "HALTED - Footer backlink is invalid"); + break; + } + + if(head == badHead) break; + + foot = (void*)( (tVAddr)head - sizeof(tHeapFoot) ); + head = foot->Head; + Log_Debug("Heap", "head=%p", head); + } + + Panic("Heap_Dump - Heap is corrupted, kernel panic!"); +} +#endif + +#if 1 +void Heap_Stats(void) +{ + tHeapHead *head; + int nBlocks = 0; + int nFree = 0; + int totalBytes = 0; + int freeBytes = 0; + int maxAlloc=0, minAlloc=-1; + int avgAlloc, frag, overhead; + + for(head = gHeapStart; + (Uint)head < (Uint)gHeapEnd; + head = (void*)( (Uint)head + head->Size ) + ) + { + nBlocks ++; + totalBytes += head->Size; + if( head->Magic == MAGIC_FREE ) + { + nFree ++; + freeBytes += head->Size; + } + else if( head->Magic == MAGIC_USED) { + if(maxAlloc < head->Size) maxAlloc = head->Size; + if(minAlloc == -1 || minAlloc > head->Size) + minAlloc = head->Size; + } + else { + Log_Warning("Heap", "Magic on %p invalid, skipping remainder of heap", head); + break; + } + + // Print the block info? + #if 1 + if( head->Magic == MAGIC_FREE ) + Log_Debug("Heap", "%p (%P) - 0x%x free", + head->Data, MM_GetPhysAddr((tVAddr)&head->Data), head->Size); + else + Log_Debug("Heap", "%p (%P) - 0x%x (%i) Owned by %s:%i (%lli ms old)", + head->Data, MM_GetPhysAddr((tVAddr)&head->Data), head->Size, head->ValidSize, head->File, head->Line, + now() - head->AllocateTime + ); + #endif + } + + Log_Log("Heap", "%i blocks (0x%x bytes)", nBlocks, totalBytes); + Log_Log("Heap", "%i free blocks (0x%x bytes)", nFree, freeBytes); + if(nBlocks != 0) + frag = (nFree-1)*10000/nBlocks; + else + frag = 0; + Log_Log("Heap", "%i.%02i%% Heap Fragmentation", frag/100, frag%100); + if(nBlocks <= nFree) + avgAlloc = 0; + else + avgAlloc = (totalBytes-freeBytes)/(nBlocks-nFree); + if(avgAlloc != 0) + overhead = (sizeof(tHeapFoot)+sizeof(tHeapHead))*10000/avgAlloc; + else + overhead = 0; + Log_Log("Heap", "Average allocation: %i bytes, Average Overhead: %i.%02i%%", + avgAlloc, overhead/100, overhead%100 + ); + Log_Log("Heap", "Smallest Block: %i bytes, Largest: %i bytes", + minAlloc, maxAlloc); + + // Scan and get distribution + #if 1 + if(nBlocks > 0) + { + struct { + Uint Size; + Uint Count; + } sizeCounts[nBlocks]; + int i; + + memset(sizeCounts, 0, nBlocks*sizeof(sizeCounts[0])); + + for(head = gHeapStart; + (Uint)head < (Uint)gHeapEnd; + head = (void*)( (Uint)head + head->Size ) + ) + { + for( i = 0; i < nBlocks; i ++ ) { + if( sizeCounts[i].Size == 0 ) + break; + if( sizeCounts[i].Size == head->Size ) + break; + } + // Should never reach this part (in a non-concurrent case) + if( i == nBlocks ) continue; + sizeCounts[i].Size = head->Size; + sizeCounts[i].Count ++; + #if 1 + //Log("Heap_Stats: %i %p - 0x%x bytes (%s) (%i)", nBlocks, head, + // head->Size, (head->Magic==MAGIC_FREE?"FREE":"used"), i + // ); + //Log("Heap_Stats: sizeCounts[%i] = {Size:0x%x, Count: %i}", i, + // sizeCounts[i].Size, sizeCounts[i].Count); + #endif + } + + for( i = 0; i < nBlocks && sizeCounts[i].Count; i ++ ) + { + Log("Heap_Stats: 0x%x - %i blocks", + sizeCounts[i].Size, sizeCounts[i].Count + ); + } + } + #endif +} +#endif + +// === EXPORTS === +EXPORT(Heap_Allocate); +EXPORT(Heap_AllocateZero); +EXPORT(Heap_Reallocate); +EXPORT(Heap_Deallocate); +EXPORT(Heap_IsHeapAddr); diff --git a/KernelLand/Kernel/include/acess.h b/KernelLand/Kernel/include/acess.h new file mode 100644 index 00000000..2cd2cff1 --- /dev/null +++ b/KernelLand/Kernel/include/acess.h @@ -0,0 +1,524 @@ +/* + * AcessOS Microkernel Version + * acess.h + */ +#ifndef _ACESS_H +#define _ACESS_H +/** + * \file acess.h + * \brief Acess2 Kernel API Core + */ + +//! NULL Pointer +#define NULL ((void*)0) +//! Pack a structure +#define PACKED __attribute__((packed)) +//! Mark a function as not returning +#define NORETURN __attribute__((noreturn)) +//! Mark a parameter as unused +#define UNUSED(x) UNUSED_##x __attribute__((unused)) +//! Get the offset of a member in a structure +#define offsetof(st, m) ((Uint)((char *)&((st *)(0))->m - (char *)0 )) + +/** + * \name Boolean constants + * \{ + */ +#define TRUE 1 +#define FALSE 0 +/** + * \} + */ + +#include +#include +#include "errno.h" + +// --- Types --- +typedef Uint32 tPID; //!< Process ID type +typedef Uint32 tTID; //!< Thread ID Type +typedef Uint32 tUID; //!< User ID Type +typedef Uint32 tGID; //!< Group ID Type +typedef Sint64 tTimestamp; //!< Timestamp (miliseconds since 00:00 1 Jan 1970) +typedef Sint64 tTime; //!< Same again +typedef struct sShortSpinlock tShortSpinlock; //!< Opaque (kinda) spinlock +typedef int bool; //!< Boolean type + +// --- Helper Macros --- +/** + * \name Helper Macros + * \{ + */ +#define CONCAT(x,y) x ## y +#define EXPAND_CONCAT(x,y) CONCAT(x,y) +#define STR(x) #x +#define EXPAND_STR(x) STR(x) + +extern char __buildnum[]; +#define BUILD_NUM ((int)(Uint)&__buildnum) +extern const char gsGitHash[]; + +#define VER2(major,minor) ((((major)&0xFF)<<8)|((minor)&0xFF)) +/** + * \} + */ + +//! \brief Error number +#define errno (*Threads_GetErrno()) + +// === CONSTANTS === +// --- Memory Flags -- +/** + * \name Memory Flags + * \{ + * \todo Move to mm_virt.h + */ +#define MM_PFLAG_RO 0x01 // Writes disallowed +#define MM_PFLAG_EXEC 0x02 // Allow execution +#define MM_PFLAG_NOPAGE 0x04 // Prevent from being paged out +#define MM_PFLAG_COW 0x08 // Copy-On-Write +#define MM_PFLAG_KERNEL 0x10 // Kernel-Only (Ring0) +/** + * \} + */ +// --- Interface Flags & Macros +/** + * \name Flags for Proc_Clone + * \{ + */ +//! Clone the entire process +#define CLONE_VM 0x10 +//! Don't copy user pages +#define CLONE_NOUSER 0x20 +/** + * \} + */ + +// === Types === +/** + * \brief Thread root function + * + * Function pointer prototype of a thread entrypoint. When it + * returns, Threads_Exit is called + */ +typedef void (*tThreadFunction)(void*); + +// === Kernel Export Macros === +/** + * \name Kernel exports + * \{ + */ +//! Kernel symbol definition +typedef struct sKernelSymbol { + const char *Name; //!< Symbolic name + tVAddr Value; //!< Value of the symbol +} tKernelSymbol; +//! Export a pointer symbol (function/array) +#define EXPORT(_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (tVAddr)_name} +//! Export a variable +#define EXPORTV(_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (tVAddr)&_name} +//! Export a symbol under another name +#define EXPORTAS(_sym,_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (tVAddr)_sym} +/** + * \} + */ + +// === FUNCTIONS === +// --- IRQs --- +/** + * \name IRQ hander registration + * \{ + */ +extern int IRQ_AddHandler(int Num, void (*Callback)(int, void*), void *Ptr); +extern void IRQ_RemHandler(int Handle); +/** + * \} + */ + +// --- Logging --- +/** + * \name Logging to kernel ring buffer + * \{ + */ +extern void Log_KernelPanic(const char *Ident, const char *Message, ...); +extern void Log_Panic(const char *Ident, const char *Message, ...); +extern void Log_Error(const char *Ident, const char *Message, ...); +extern void Log_Warning(const char *Ident, const char *Message, ...); +extern void Log_Notice(const char *Ident, const char *Message, ...); +extern void Log_Log(const char *Ident, const char *Message, ...); +extern void Log_Debug(const char *Ident, const char *Message, ...); +/** + * \} + */ + +// --- Debug --- +/** + * \name Debugging and Errors + * \{ + */ +extern void Debug_KernelPanic(void); //!< Initiate a kernel panic +extern void Panic(const char *Msg, ...); //!< Print a panic message (initiates a kernel panic) +extern void Warning(const char *Msg, ...); //!< Print a warning message +extern void LogF(const char *Fmt, ...); //!< Print a log message without a trailing newline +extern void Log(const char *Fmt, ...); //!< Print a log message +extern void Debug(const char *Fmt, ...); //!< Print a debug message (doesn't go to KTerm) +extern void LogV(const char *Fmt, va_list Args); //!< va_list Log message +extern void Debug_Enter(const char *FuncName, const char *ArgTypes, ...); +extern void Debug_Log(const char *FuncName, const char *Fmt, ...); +extern void Debug_Leave(const char *FuncName, char RetType, ...); +extern void Debug_HexDump(const char *Header, const void *Data, Uint Length); +#define UNIMPLEMENTED() Warning("'%s' unimplemented", __func__) +#if DEBUG +# define ENTER(_types...) Debug_Enter((char*)__func__, _types) +# define LOG(_fmt...) Debug_Log((char*)__func__, _fmt) +# define LEAVE(_t...) Debug_Leave((char*)__func__, _t) +# define LEAVE_RET(_t,_v...) do{LEAVE(_t,_v);return _v;}while(0) +# define LEAVE_RET0() do{LEAVE('-');return;}while(0) +#else +# define ENTER(...) +# define LOG(...) +# define LEAVE(...) +# define LEAVE_RET(_t,_v...) return (_v) +# define LEAVE_RET0() return +#endif +#if SANITY +# define ASSERT(expr) do{if(!(expr))Panic("%s: Assertion '"#expr"' failed",(char*)__func__);}while(0) +#else +# define ASSERT(expr) +#endif +/** + * \} + */ + +// --- IO --- +#if NO_IO_BUS +#define inb(a) (Log_Panic("Arch", STR(ARCHDIR)" does not support in*/out* (%s:%i)", __FILE__, __LINE__),0) +#define inw(a) inb(a) +#define ind(a) inb(a) +#define inq(a) inb(a) +#define outb(a,b) inb(a) +#define outw(a,b) inb(a) +#define outd(a,b) inb(a) +#define outq(a,b) inb(a) +#else +/** + * \name I/O Memory Access + * \{ + */ +extern void outb(Uint16 Port, Uint8 Data); +extern void outw(Uint16 Port, Uint16 Data); +extern void outd(Uint16 Port, Uint32 Data); +extern void outq(Uint16 Port, Uint64 Data); +extern Uint8 inb(Uint16 Port); +extern Uint16 inw(Uint16 Port); +extern Uint32 ind(Uint16 Port); +extern Uint64 inq(Uint16 Port); +/** + * \} + */ +#endif +// --- Memory Management --- +/** + * \name Memory Management + * \{ + * \todo Move to mm_virt.h + */ +/** + * \brief Allocate a physical page at \a VAddr + * \param VAddr Virtual Address to allocate at + * \return Physical address allocated + */ +extern tPAddr MM_Allocate(tVAddr VAddr) __attribute__ ((warn_unused_result)); +/** + * \brief Deallocate a page + * \param VAddr Virtual address to unmap + */ +extern void MM_Deallocate(tVAddr VAddr); +/** + * \brief Map a physical page at \a PAddr to \a VAddr + * \param VAddr Target virtual address + * \param PAddr Physical address to map + * \return Boolean Success + */ +extern int MM_Map(tVAddr VAddr, tPAddr PAddr); +/** + * \brief Get the physical address of \a Addr + * \param Addr Address of the page to get the physical address of + * \return Physical page mapped at \a Addr + */ +extern tPAddr MM_GetPhysAddr(tVAddr Addr); +/** + * \brief Set the access flags on a page + * \param VAddr Virtual address of the page + * \param Flags New flags value + * \param Mask Flags to set + */ +extern void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask); +/** + * \brief Get the flags on a flag + * \param VAddr Virtual address of page + * \return Flags value of the page + */ +extern Uint MM_GetFlags(tVAddr VAddr); +/** + * \brief Checks is a memory range is user accessable + * \param VAddr Base address to check + * \return 1 if the memory is all user-accessable, 0 otherwise + */ +#define MM_IsUser(VAddr) (!(MM_GetFlags((VAddr))&MM_PFLAG_KERNEL)) +/** + * \brief Temporarily map a page into the address space + * \param PAddr Physical addres to map + * \return Virtual address of page in memory + * \note There is only a limited ammount of slots avaliable + */ +extern tVAddr MM_MapTemp(tPAddr PAddr); +/** + * \brief Free a temporarily mapped page + * \param VAddr Allocate virtual addres of page + */ +extern void MM_FreeTemp(tVAddr VAddr); +/** + * \brief Map a physcal address range into the virtual address space + * \param PAddr Physical address to map in + * \param Number Number of pages to map + */ +extern tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number); +/** + * \brief Allocates DMA physical memory + * \param Pages Number of pages required + * \param MaxBits Maximum number of bits the physical address can have + * \param PhysAddr Pointer to the location to place the physical address allocated + * \return Virtual address allocate + */ +extern tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr); +/** + * \brief Unmaps an allocated hardware range + * \param VAddr Virtual address allocate by ::MM_MapHWPages or ::MM_AllocDMA + * \param Number Number of pages to free + */ +extern void MM_UnmapHWPages(tVAddr VAddr, Uint Number); +/** + * \brief Allocate a single physical page + * \return Physical address allocated + */ +extern tPAddr MM_AllocPhys(void); +/** + * \brief Allocate a contiguous range of physical pages + * \param Pages Number of pages to allocate + * \param MaxBits Maximum number of address bits allowed + * \return First physical address allocated + */ +extern tPAddr MM_AllocPhysRange(int Pages, int MaxBits); +/** + * \brief Reference a physical page + * \param PAddr Page to mark as referenced + */ +extern void MM_RefPhys(tPAddr PAddr); +/** + * \brief Dereference a physical page + * \param PAddr Page to dereference + */ +extern void MM_DerefPhys(tPAddr PAddr); +/** + * \brief Get the number of times a page has been referenced + * \param PAddr Address to check + * \return Reference count for the page + */ +extern int MM_GetRefCount(tPAddr PAddr); +/** + * \brief Set the node associated with a page + * \param PAddr Physical address of page + * \param Node Node pointer (tVFS_Node) + * \return Boolean failure + * \retval 0 Success + * \retval 1 Page not allocated + */ +extern int MM_SetPageNode(tPAddr PAddr, void *Node); +/** + * \brief Get the node associated with a page + * \param PAddr Physical address of page + * \param Node Node pointer (tVFS_Node) destination + * \return Boolean failure + * \retval 0 Success + * \retval 1 Page not allocated + */ +extern int MM_GetPageNode(tPAddr PAddr, void **Node); +/** + * \} + */ + +// --- Memory Manipulation --- +/** + * \name Memory Manipulation + * \{ + */ +extern int memcmp(const void *m1, const void *m2, size_t count); +extern void *memcpy(void *dest, const void *src, size_t count); +extern void *memcpyd(void *dest, const void *src, size_t count); +extern void *memmove(void *dest, const void *src, size_t len); +extern void *memset(void *dest, int val, size_t count); +extern void *memsetd(void *dest, Uint32 val, size_t count); +/** + * \} + */ +/** + * \name Memory Validation + * \{ + */ +extern int CheckString(const char *String); +extern int CheckMem(const void *Mem, int Num); +/** + * \} + */ + +// --- Endianness --- +/** + * \name Endianness Swapping + * \{ + */ +#ifdef __BIG_ENDIAN__ +#define LittleEndian16(_val) SwapEndian16(_val) +#define LittleEndian32(_val) SwapEndian32(_val) +#define BigEndian16(_val) (_val) +#define BigEndian32(_val) (_val) +#else +#define LittleEndian16(_val) (_val) +#define LittleEndian32(_val) (_val) +#define BigEndian16(_val) SwapEndian16(_val) +#define BigEndian32(_val) SwapEndian32(_val) +#endif +extern Uint16 SwapEndian16(Uint16 Val); +extern Uint32 SwapEndian32(Uint32 Val); +/** + * \} + */ + +// --- Strings --- +/** + * \name Strings + * \{ + */ +extern int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args); +extern int sprintf(char *__s, const char *__format, ...); +extern size_t strlen(const char *Str); +extern char *strcpy(char *__dest, const char *__src); +extern char *strncpy(char *__dest, const char *__src, size_t max); +extern char *strcat(char *__dest, const char *__src); +extern char *strncat(char *__dest, const char *__src, size_t n); +extern int strcmp(const char *__str1, const char *__str2); +extern int strncmp(const char *Str1, const char *Str2, size_t num); +extern int strucmp(const char *Str1, const char *Str2); +// strdup macro is defined in heap.h +extern char *_strdup(const char *File, int Line, const char *Str); +extern char **str_split(const char *__str, char __ch); +extern char *strchr(const char *__s, int __c); +extern int strpos(const char *Str, char Ch); +extern int strpos8(const char *str, Uint32 search); +extern void itoa(char *buf, Uint64 num, int base, int minLength, char pad); +extern int atoi(const char *string); +extern int ParseInt(const char *string, int *Val); +extern int ReadUTF8(const Uint8 *str, Uint32 *Val); +extern int WriteUTF8(Uint8 *str, Uint32 Val); +extern int ModUtil_SetIdent(char *Dest, const char *Value); +extern int ModUtil_LookupString(const char **Array, const char *Needle); + +extern Uint8 ByteSum(const void *Ptr, int Size); +extern int Hex(char *Dest, size_t Size, const Uint8 *SourceData); +extern int UnHex(Uint8 *Dest, size_t DestSize, const char *SourceString); +/** + * \} + */ + +/** + * \brief Get a random number + * \return Random number + * \note Current implementation is a linear congruency + */ +extern int rand(void); +/** + * \brief Call a function with a variable number of arguments + * \param Function Function address + * \param NArgs Number of entries in \a Args + * \param Args Array of arguments + * \return Integer from called Function + */ +extern int CallWithArgArray(void *Function, int NArgs, Uint *Args); + +// --- Heap --- +#include +/** + * \brief Magic heap allocation function + */ +extern void *alloca(size_t Size); + +// --- Modules --- +/** + * \name Modules + * \{ + */ +extern int Module_LoadMem(void *Buffer, Uint Length, const char *ArgStr); +extern int Module_LoadFile(const char *Path, const char *ArgStr); +/** + * \} + */ + +// --- Timing --- +/** + * \name Time and Timing + * \{ + */ +/** + * \brief Create a timestamp from a time + */ +extern tTime timestamp(int sec, int mins, int hrs, int day, int month, int year); +/** + * \brief Extract the date/time from a timestamp + */ +extern void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms); +/** + * \brief Gets the current timestamp (miliseconds since Midnight 1st January 1970) + */ +extern Sint64 now(void); +/** + * \} + */ + +// --- Threads --- +/** + * \name Threads and Processes + * \{ + */ +extern int Proc_SpawnWorker(void (*Fcn)(void*), void *Data); +extern int Proc_Spawn(const char *Path); +extern int Proc_SysSpawn(const char *Binary, const char **ArgV, const char **EnvP, int nFD, int *FDs); +extern int Proc_Execve(const char *File, const char **ArgV, const char **EnvP, int DataSize); +extern void Threads_Exit(int TID, int Status); +extern void Threads_Yield(void); +extern void Threads_Sleep(void); +extern int Threads_WakeTID(tTID Thread); +extern tPID Threads_GetPID(void); +extern tTID Threads_GetTID(void); +extern tUID Threads_GetUID(void); +extern tGID Threads_GetGID(void); +extern int SpawnTask(tThreadFunction Function, void *Arg); +extern int *Threads_GetErrno(void); +extern int Threads_SetName(const char *NewName); +/** + * \} + */ + +// --- Simple Math --- +//! Divide and round up +extern int DivUp(int num, int dem); +//! Divide and Modulo 64-bit unsigned integer +extern Uint64 DivMod64U(Uint64 Num, Uint64 Den, Uint64 *Rem); + +static inline int MIN(int a, int b) { return a < b ? a : b; } +static inline int MAX(int a, int b) { return a > b ? a : b; } + +#include +#include +#include + +#endif diff --git a/KernelLand/Kernel/include/adt.h b/KernelLand/Kernel/include/adt.h new file mode 100644 index 00000000..67e12665 --- /dev/null +++ b/KernelLand/Kernel/include/adt.h @@ -0,0 +1,48 @@ +/* + * Acess2 + * - Abstract Data Types + */ +#ifndef _ADT_H_ +#define _ADT_H_ + +/** + * \name Ring Buffers + * \{ + */ +typedef struct sRingBuffer +{ + size_t Start; //!< Start of data in ring buffer + size_t Length; //!< Number of data bytes in buffer + size_t Space; //!< Allocated space in buffer + tShortSpinlock Lock; //!< Lock to prevent collisions + char Data[]; //!< Buffer +} tRingBuffer; + +/** + * \brief Create a ring buffer \a Space bytes large + * \param Space Ammount of space to allocate within the buffer + * \return Pointer to the buffer structure + */ +extern tRingBuffer *RingBuffer_Create(size_t Space); +/** + * \brief Read at most \a Length bytes from the buffer + * \param Dest Destinaton buffer + * \param Buffer Source ring buffer + * \param Length Requested number of bytes + * \return Number of bytes read + */ +extern size_t RingBuffer_Read(void *Dest, tRingBuffer *Buffer, size_t Length); +/** + * \brief Write at most \a Length bytes to the buffer + * \param Buffer Destination ring buffer + * \param Source Source buffer + * \param Length Provided number of bytes + * \return Number of bytes written + */ +extern size_t RingBuffer_Write(tRingBuffer *Buffer, const void *Source, size_t Length); +/** + * \} + */ + + +#endif diff --git a/KernelLand/Kernel/include/api_drv_common.h b/KernelLand/Kernel/include/api_drv_common.h new file mode 100644 index 00000000..59bef5fe --- /dev/null +++ b/KernelLand/Kernel/include/api_drv_common.h @@ -0,0 +1,141 @@ +/** + * \file api_drv_common.h + * \brief Common Driver Interface Definitions + * \author John Hodge (thePowersGang) + * + * \section Introduction + * There are two ways Acess drivers can communicate with userspace + * applications, both are through the VFS. The first is by exposing a + * device as a file buffer, the second is by sending commands via the + * ioctl() system call. + * All drivers in Acess must at least conform to the specifcation in this + * file (even if it is just implementing eTplDrv_IOCtl.DRV_IOCTL_TYPE and + * returning eTplDrv_Type.DRV_TYPE_NULL, however, doing so is discouraged) + * + * \section ioctls Core IOCtl calls + * As said, the core Acess driver specifcation defines specific IOCtl calls + * that all drivers should implement. The four core IOCtls (defined in + * ::eTplDrv_IOCtl) allow another binary (wether it be a user-mode application + * or another driver) to tell what type of device a driver provides, the + * basic identifcation of the driver (4 character ID and BCD version number) + * and be able to use externally standardised calls that may not have + * standardised call numbers. + * NOTE: All ioctl calls WILL return -1 if the driver ran into an error + * of its own fault while executing the call. If the user was at fault + * (e.g. by passing a bad buffer) the call will return -2. + * + * \section types Driver Types + * When the eTplDrv_IOCtl.DRV_IOCTL_TYPE call is made, the driver should + * return the relevant entry in the ::eTplDrv_Type enumeration that describes + * what sub-specifcation (and hence, what device type) it implements. + * These device types are described in their own files, which are liked + * from their entries in ::eTplDrv_Type. + */ +#ifndef _API_DRV_COMMON_H +#define _API_DRV_COMMON_H + +/** + * \enum eTplDrv_IOCtl + * \brief Common IOCtl Calls + */ +enum eTplDrv_IOCtl { + /** + * ioctl(...) + * \brief Get driver type + * \return The relevant entry from ::eTplDrv_Type + */ + DRV_IOCTL_TYPE, + + /** + * ioctl(..., char *dest[32]) + * \brief Get driver identifier string + * \return 0 on no error + * + * This call sets the 32-byte array \a dest to the drivers 31 charater + * identifier. This identifier must be unique to the driver series. + */ + DRV_IOCTL_IDENT, + + /** + * ioctl(...) + * \brief Get driver version number + * \return 24-bit BCD version number (2.2.2) + * + * This call returns the 6-digit (2 major, 2 minor, 2 patch) version + * number of the driver. + */ + DRV_IOCTL_VERSION, + + /** + * ioctl(..., char *name) + * \brief Get a IOCtl call ID from a symbolic name + * \return ID number of the call, or 0 if not found + * + * This call allows user applications to not need to know the ID numbers + * of this driver's IOCtl calls by taking a string and returning the + * IOCtl call number associated with that method name. + */ + DRV_IOCTL_LOOKUP, + + /** + * \brief First non-reserved IOCtl number for driver extension + */ + DRV_IOCTL_USERMIN = 0x1000, +}; + +/** + * \brief eTplDrv_IOCtl.DRV_IOCTL_LOOKUP names for the core IOCtls + * These are the official lookup names of the core calls + */ +#define DRV_IOCTLNAMES "type", "ident", "version", "lookup" + +/** + * \brief Helper macro for the base IOCtl calls + * \param _type Type number from eTplDrv_Type to return + * \param _ident String of max 32-characters that identifies this driver + * \param _version Driver's 8.8.8 BCD version number + * \param _ioctls Pointer to the IOCtls string array + * \warning If you have DEBUG enabled in the calling file, this function + * will do LEAVE()s before returning, so make sure that the + * IOCtl function is ENTER()ed when using debug with this macro + * + * Usage: (Id is the IOCtl call ID) + * \code + * switch(Id) + * { + * BASE_IOCTLS(DRV_TYPE_MISC, "Ident", 0x100, csaIOCtls) + * // Your IOCtls go here, starting at index 4 + * } + * \endcode + */ +#define BASE_IOCTLS(_type, _ident, _version, _ioctls) \ + case DRV_IOCTL_TYPE: LEAVE('i', (_type)); return (_type);\ + case DRV_IOCTL_IDENT: {\ + int tmp = ModUtil_SetIdent(Data, (_ident));\ + LEAVE('i', tmp); return tmp;\ + }\ + case DRV_IOCTL_VERSION: LEAVE('x', (_version)); return (_version);\ + case DRV_IOCTL_LOOKUP:{\ + int tmp = ModUtil_LookupString( _ioctls, (const char*)Data );\ + LEAVE('i', tmp);\ + return tmp;\ + } + +/** + * \enum eTplDrv_Type + * \brief Driver Types returned by DRV_IOCTL_TYPE + */ +enum eTplDrv_Type { + DRV_TYPE_NULL, //!< NULL Type - Custom Interface + DRV_TYPE_MISC, //!< Miscelanious Compilant - Supports the core calls + DRV_TYPE_TERMINAL, //!< Terminal - see api_drv_terminal.h + DRV_TYPE_VIDEO, //!< Video - see api_drv_video.h + DRV_TYPE_SOUND, //!< Audio + DRV_TYPE_DISK, //!< Disk - see api_drv_disk.h + DRV_TYPE_KEYBOARD, //!< Keyboard - see api_drv_keyboard.h + DRV_TYPE_MOUSE, //!< Mouse + DRV_TYPE_JOYSTICK, //!< Joystick / Gamepad + DRV_TYPE_NETWORK //!< Network Device - see api_drv_network.h +}; + +#endif diff --git a/KernelLand/Kernel/include/api_drv_disk.h b/KernelLand/Kernel/include/api_drv_disk.h new file mode 100644 index 00000000..94ea2c94 --- /dev/null +++ b/KernelLand/Kernel/include/api_drv_disk.h @@ -0,0 +1,185 @@ +/** + * \file api_drv_disk.h + * \brief Disk Driver Interface Definitions + * \author John Hodge (thePowersGang) + * + * \section Nomeclature + * All addreses are 64-bit counts of bytes from the logical beginning of + * the disk unless explicitly stated. + * + * \section dirs VFS Layout + * Disk drivers have a flexible directory layout. The root directory can + * contain subdirectories, with the only conditions being that all nodes + * must support ::eTplDrv_IOCtl with DRV_IOCTL_TYPE returning DRV_TYPE_DISK. + * And all file nodes representing disk devices (or partitions) and implemeting + * ::eTplDisk_IOCtl fully + * + * \section files Files + * When a read or write occurs on a normal file in the disk driver it will + * read/write the represented device. The accesses need not be aligned to + * the block size, however aligned reads/writes should be handled specially + * to improve speed (this can be aided by using ::DrvUtil_ReadBlock and + * ::DrvUtil_WriteBlock) + */ +#ifndef _API_DRV_DISK_H +#define _API_DRV_DISK_H + +#include + +/** + * \enum eTplDisk_IOCtl + * \brief Common Disk IOCtl Calls + * \extends eTplDrv_IOCtl + */ +enum eTplDisk_IOCtl { + /** + * ioctl(..., void) + * \brief Get the block size + * \return Size of a hardware block for this device + */ + DISK_IOCTL_GETBLOCKSIZE = 4, + + /** + * ioctl(..., tTplDisk_CacheRegion *RegionInfo) + * \brief Sets the cache importantce and protocol for a section of + * memory. + * \param RegionInfo Pointer to a region information structure + * \return Boolean failure + */ + DISK_IOCTL_SETCACHEREGION, + + /** + * ioctl(..., Uint64 *Info[2]) + * \brief Asks the driver to precache a region of disk. + * \param Region 64-bit Address and Size pair describing the area to cache + * \return Number of blocks cached + */ + DISK_IOCTL_PRECACHE, + + /** + * ioclt(..., Uint64 *Region[2]) + * \brief Asks to driver to flush the region back to disk + * \param Region 64-bit Address and Size pair describing the area to flush + * \note If Region[0] == -1 then the entire disk's cache is flushed + * \return Number of blocks flushed (or 0 for entire disk) + */ + DISK_IOCTL_FLUSH +}; + +/** + * \brief Describes the cache parameters of a region on the disk + */ +typedef struct sTplDisk_CacheRegion +{ + Uint64 Base; //!< Base of cache region + Uint64 Length; //!< Size of cache region + /** + * \brief Cache Protocol & Flags + * + * The low 4 bits denot the cache protocol to be used by the + * region (see ::eTplDisk_CacheProtocols for a list). + * The high 4 bits denote flags to apply to the cache (see + * ::eTplDisk_CacheFlags) + */ + Uint8 Flags; + Uint8 Priority; //!< Lower is a higher proritory + /** + * \brief Maximum size of cache, in blocks + * \note If CacheSize is zero, the implemenation defined limit is used + */ + Uint16 CacheSize; +} tTplDisk_CacheRegion; + +/** + * \brief Cache protocols to use + */ +enum eTplDisk_CacheProtocols +{ + /** + * \brief Don't cache the region + */ + + DISK_CACHEPROTO_DONTCACHE, + /** + * \brief Most recently used blocks cached + * \note This is the default action for undefined regions + */ + DISK_CACHEPROTO_RECENTLYUSED, + /** + * \brief Cache the entire region in memory + * + * This is a faster version of setting Length to CacheSize*BlockSize + */ + DISK_CACHEPROTO_FULLCACHE, + + /** + * \brief Cache only on demand + * + * Only cache when the ::DISK_IOCTL_PRECACHE IOCtl is used + */ + DISK_CACHEPROTO_EXPLICIT +}; + +/** + * \brief Flags for the cache + */ +enum eTplDisk_CacheFlags +{ + /** + * \brief Write all changes to the region straight back to media + */ + DISK_CACHEFLAG_WRITETHROUGH = 0x10 +}; + +/** + * \brief IOCtl name strings + */ +#define DRV_DISK_IOCTLNAMES "get_block_size","set_cache_region","set_precache" + +/** + * \name Disk Driver Utilities + * \{ + */ + +/** + * \brief Callback function type used by DrvUtil_ReadBlock and DrvUtil_WriteBlock + * \param Address Zero based block number to read + * \param Count Number of blocks to read + * \param Buffer Destination for read blocks + * \param Argument Argument provided in ::DrvUtil_ReadBlock and ::DrvUtil_WriteBlock + */ +typedef Uint (*tDrvUtil_Read_Callback)(Uint64 Address, Uint Count, void *Buffer, Uint Argument); +typedef Uint (*tDrvUtil_Write_Callback)(Uint64 Address, Uint Count, const void *Buffer, Uint Argument); + +/** + * \brief Reads a range from a block device using aligned reads + * \param Start Base byte offset + * \param Length Number of bytes to read + * \param Buffer Destination for read data + * \param ReadBlocks Callback function to read a sequence of blocks + * \param BlockSize Size of an individual block + * \param Argument An argument to pass to \a ReadBlocks + * \return Number of bytes read + */ +extern Uint64 DrvUtil_ReadBlock(Uint64 Start, Uint64 Length, void *Buffer, + tDrvUtil_Read_Callback ReadBlocks, Uint64 BlockSize, Uint Argument); +/** + * \brief Writes a range to a block device using aligned writes + * \param Start Base byte offset + * \param Length Number of bytes to write + * \param Buffer Destination for read data + * \param ReadBlocks Callback function to read a sequence of blocks + * \param WriteBlocks Callback function to write a sequence of blocks + * \param BlockSize Size of an individual block + * \param Argument An argument to pass to \a ReadBlocks and \a WriteBlocks + * \return Number of bytes written + */ +extern Uint64 DrvUtil_WriteBlock(Uint64 Start, Uint64 Length, const void *Buffer, + tDrvUtil_Read_Callback ReadBlocks, tDrvUtil_Write_Callback WriteBlocks, + Uint64 BlockSize, Uint Argument); + +/** + * \} + */ + +#endif diff --git a/KernelLand/Kernel/include/api_drv_joystick.h b/KernelLand/Kernel/include/api_drv_joystick.h new file mode 100644 index 00000000..3ec8597b --- /dev/null +++ b/KernelLand/Kernel/include/api_drv_joystick.h @@ -0,0 +1,150 @@ +/** + * \file api_drv_joystick.h + * \brief Joystick Driver Interface Definitions + * \author John Hodge (thePowersGang) + * + * \section dirs VFS Layout + * Joystick drivers define a single VFS node, that acts as a fixed size file. + * Reads from this file return the current state, writes are ignored. + * + * \section File Structure + * The device file must begin with a valid sJoystick_FileHeader structure. + * The file header is followed by \a NAxies instances of sJoystick_Axis, one + * for each axis. + * This is followed by \a NButtons boolean values (represented using \a Uint8), + * each representing the state of a single button (where 0 is unpressed, + * 0xFF is fully depressed - intermediate values are valid in the case of + * variable-pressure buttons) + */ +#ifndef _API_DRV_JOYSTICK_H +#define _API_DRV_JOYSTICK_H + +#include + +/** + * \enum eTplJoystick_IOCtl + * \brief Common Joystick IOCtl Calls + * \extends eTplDrv_IOCtl + */ +enum eTplJoystick_IOCtl { + /** + * ioctl(..., tJoystick_Callback *Callback) + * \brief Sets the callback + * \note Can be called from kernel mode only + * + * Sets the callback that is called when a event occurs (button or axis + * change). This function pointer must be in kernel mode (although, + * kernel->user or kernel->ring3driver abstraction functions can be used) + * + * Axis events depend on the axis limit, if non-zero, the callback fires + * if the cursor position changes. Otherwise it fires when the axis value + * (cursor accelleration) changes. + */ + JOY_IOCTL_SETCALLBACK = 4, + + /** + * ioctl(..., int *Argument) + * \brief Set the argument passed as the first parameter to the callback + * \note Kernel mode only + */ + JOY_IOCTL_SETCALLBACKARG, + + /** + * ioctl(..., tJoystickNumValue *) + * \brief Set maximum value for sJoystick_Axis.CursorPos + * \note If \a Value is equal to -1 (all bits set), the value is not changed + */ + JOY_IOCTL_GETSETAXISLIMIT, + + /** + * ioctl(..., tJoystickNumValue *) + * \brief Set the value of sJoystick_Axis.CursorPos + * \note If \a Value is equal to -1 (all bits set), the value is not changed + */ + JOY_IOCTL_GETSETAXISPOSITION, + + /** + * ioctl(..., tJoystickNumValue *) + * \brief Set axis flags + * \note If \a Value is equal to -1 (all bits set), the value is not changed + * \todo Define flag values + */ + JOY_IOCTL_GETSETAXISFLAGS, + + /** + * ioctl(..., tJoystickNumValue *) + * \brief Set Button Flags + * \note If \a Value is equal to -1 (all bits set), the value is not changed + * \todo Define flag values + */ + JOY_IOCTL_GETSETBUTTONFLAGS, +}; + +/** + * \brief Symbolic names for Joystick IOCtls + */ +#define DRV_JOY_IOCTLNAMES "set_callback", "set_callback_arg", "getset_axis_limit", "getset_axis_position", \ + "getset_axis_flags", "getset_button_flags" + +// === TYPES === +typedef struct sJoystick_NumValue tJoystick_NumValue; +typedef struct sJoystick_FileHeader tJoystick_FileHeader; +typedef struct sJoystick_Axis tJoystick_Axis; + +/** + * \brief Number/Value pair for joystick IOCtls + */ +struct sJoystick_NumValue +{ + int Num; //!< Axis/Button number + int Value; //!< Value (see IOCtl defs for meaning) +}; + +/** + * \brief Callback type for JOY_IOCTL_SETCALLBACK + * \param Ident Ident value passed to JOY_IOCTL_SETCALLBACK + * + */ +typedef void (*tJoystick_Callback)(int Ident, int bIsAxis, int Num, int Delta); + +/** + * \struct sJoystick_FileHeader + * \brief Format of the joystick VFS node's first bytes + */ +struct sJoystick_FileHeader +{ + Uint16 NAxies; //!< Number of Axies + Uint16 NButtons; //!< Number of buttons +}; + +/** + * \brief Axis Definition in file data + * + * Describes the current state of an axis on the joystick. + * \a CursorPos is between zero and the current limit set by the + * JOY_IOCTL_GETSETAXISLIMIT IOCtl, while \a CurValue indicates the + * current position of the joystick axis. This is defined to be between + * \a MinValue and \a MaxValue. + */ +struct sJoystick_Axis +{ + Sint16 MinValue; //!< Minumum value for \a CurValue + Sint16 MaxValue; //!< Maximum value for \a CurValue + Sint16 CurValue; //!< Current value (joystick position) + Uint16 CursorPos; //!< Current state (cursor position) +}; + +/** + * \brief Macro to define a structure for a joystick's node + * \param _naxies Number of axies + * \param _nbuttons Number of buttons + * \note This just defines the structure, it's up to the driver to set the + * sJoystick_FileHeader.NAxies and sJoystick_FileHeader.NButtons fields. + */ +#define JOY_INFOSTRUCT(_naxies, _nbuttons) struct { \ + Uint16 NAxies, NButtons;\ + tJoystick_Axis Axies[_naxies];\ + Uint16 Buttons[_nbuttons];\ + } + +#endif diff --git a/KernelLand/Kernel/include/api_drv_keyboard.h b/KernelLand/Kernel/include/api_drv_keyboard.h new file mode 100644 index 00000000..57ca247e --- /dev/null +++ b/KernelLand/Kernel/include/api_drv_keyboard.h @@ -0,0 +1,141 @@ +/** + * \file api_drv_keyboard.h + * \brief Keyboard Driver Interface Definitions + * \author John Hodge (thePowersGang) + * + * \section dirs VFS Layout + * Keyboard drivers consist of only a single node, which is a normal file + * node with a size of zero. All reads and writes to this node are ignored + * (tVFS_Node.Read and tVFS_Node.Write are NULL) + */ +#ifndef _API_DRV_KEYBOARD_H +#define _API_DRV_KEYBOARD_H + +#include + +/** + * \enum eTplKeyboard_IOCtl + * \brief Common Keyboard IOCtl Calls + * \extends eTplDrv_IOCtl + */ +enum eTplKeyboard_IOCtl { + /** + * ioctl(..., int *Rate) + * \brief Get/Set Repeat Rate + * \param Rate New repeat rate (pointer) + * \return Current/New Repeat rate + * + * Gets/Set the repeat rate (actually the time in miliseconds between + * repeats) of a held down key. + * If the rate is set to zero, repeating will be disabled. + */ + KB_IOCTL_REPEATRATE = 4, + + /** + * ioctl(..., int *Delay) + * \brief Get/Set Repeat Delay + * \param Delay New repeat delay (pointer) + * \return Current/New repeat delay + * + * Gets/Set the time in miliseconds before a key starts repeating + * after a key is pressed. + * Setting the delay to a negative number will cause the function to + * return -1 + */ + KB_IOCTL_REPEATDELAY, + + + /** + * ioctl(..., tKeybardCallback *Callback) + * \brief Sets the callback + * \note Can be called from kernel mode only + * + * Sets the function to be called when a key event occurs (press, release + * or repeat). This function pointer must be in kernel mode (although, + * kernel->user or kernel->ring3driver abstraction functions can be used) + * + * This function is called when a key is pressed, repeated or released. + * If the raw scancode is to be included with the key event, it should precede + * the event. + */ + KB_IOCTL_SETCALLBACK +}; + +/** + * \brief Symbolic names for Keyboard IOCtls + */ +#define DRV_KEYBAORD_IOCTLNAMES "getset_repeat_rate", "getset_repeat_delay", "set_callback" + +/** + * \brief Callback type for KB_IOCTL_SETCALLBACK + * \param Key Key symbol (Unicode or eTplKeyboard_KeyCodes) + */ +typedef void (*tKeybardCallback)(Uint32 Key); + +/** + * \name Callback key flags + * \brief Flags for values passed to the callback + * \{ + */ +#define KEY_CODEPOINT_MASK 0x3FFFFFFF +#define KEY_ACTION_MASK 0xC0000000 +#define KEY_ACTION_PRESS 0x00000000 //!< Key pressed +#define KEY_ACTION_RELEASE 0x40000000 //!< Key released +#define KEY_ACTION_REFIRE 0x80000000 //!< Repeated key +#define KEY_ACTION_RAWSYM 0xC0000000 //!< Raw key symbol (comes before a press/repeat/release) +/** + * \} + */ + +/** + * \brief Symbolic key codes + * + * These key codes represent non-pritable characters and are placed above + * the Unicode character space. + * If the using driver recieves a key code with the 31st bit set, it means + * that that key has been released. + */ +enum eTplKeyboard_KeyCodes { + KEY_ESC = 0x1B, //!< Escape Character + + KEY_NP_MASK = 0x20000000, //! Mask for non-printable characters + + /** + * \name Special Keys + * \brief These keys are usually used on their own + * \{ + */ + KEY_CAPSLOCK, + KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, + KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, + KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, + KEY_NUMLOCK, KEY_SCROLLLOCK, + KEY_HOME, KEY_END, KEY_INS, KEY_DEL, + KEY_PAUSE, KEY_BREAK, + KEY_PGUP, KEY_PGDOWN, + KEY_KPENTER, KEY_KPSLASH, KEY_KPMINUS, KEY_KPPLUS, KEY_KPSTAR, + KEY_KPHOME, KEY_KPUP, KEY_KPPGUP, KEY_KPLEFT, KEY_KP5, KEY_KPRIGHT, + KEY_KPEND, KEY_KPDOWN, KEY_KPPGDN, KEY_KPINS, KEY_KPDEL, + KEY_LWIN, KEY_RWIN, + KEY_MENU, + /** + * \} + */ + + // Modifiers + /** + * \name Modifiers + * \brief These keye usually alter the character stream sent to the user + * \{ + */ + KEY_MODIFIERS = 0x30000000, + KEY_LCTRL, KEY_RCTRL, + KEY_LALT, KEY_RALT, + KEY_LSHIFT, KEY_RSHIFT, + /** + * \} + */ +}; + + +#endif diff --git a/KernelLand/Kernel/include/api_drv_network.h b/KernelLand/Kernel/include/api_drv_network.h new file mode 100644 index 00000000..6cb8c349 --- /dev/null +++ b/KernelLand/Kernel/include/api_drv_network.h @@ -0,0 +1,56 @@ +/** + * \file api_drv_network.h + * \brief Network Interface Driver Interface Definitions + * + * \section dirs VFS Layout + * All network drivers must have the following basic VFS structure + * The root of the driver will only contain files that are named from zero + * upwards that represent the present network adapters that this driver + * controls. All VFS nodes must implement ::eTplDrv_IOCtl with + * DRV_IOCTL_TYPE returning DRV_TYPE_NETWORK. + * The adapter nodes must also implement ::eTplNetwork_IOCtl fully + * (unless it is noted in the ::eTplNetwork_IOCtl documentation that a + * call is optional) + * + * \section files Adapter Files + * \subsection Reading + * When an adapter file is read from, the driver will block the reading + * thread until a packet arrives (if there is not already an unhandled + * packet in the queue) this will then be read into the destination buffer. + * If the packet does not fit in the buffer, the end of it is discarded. + * Likewise, if the packet does not completely fill the buffer, the call + * will still read to the buffer and then return the size of the packet. + * \subsection Writing + * When an adapter is written to, the data written is encoded as a packet + * and sent, if the data is not the correct size to be sent (if the packet + * is too small, or if it is too large) -1 should be returned and the packet + * will not be sent. + */ +#ifndef _API_DRV_NETWORK_H +#define _API_DRV_NETWORK_H + +#include + +/** + * \enum eTplNetwork_IOCtl + * \brief Common Network IOCtl Calls + * \extends eTplDrv_IOCtl + */ +enum eTplNetwork_IOCtl { + /** + * ioctl(..., Uint8 *MAC[6]) + * \brief Get the MAC address of the interface + * \return 1 on success, 0 if the file is the root, -1 on error + * + * Copies the six byte Media Access Control (MAC) address of the + * adapter to the \a MAC array. + */ + NET_IOCTL_GETMAC = 4 +}; + +/** + * \brief IOCtl name strings for use with eTplDrv_IOCtl.DRV_IOCTL_LOOKUP + */ +#define DRV_NETWORK_IOCTLNAMES "get_mac_addr" + +#endif diff --git a/KernelLand/Kernel/include/api_drv_terminal.h b/KernelLand/Kernel/include/api_drv_terminal.h new file mode 100644 index 00000000..3142d33c --- /dev/null +++ b/KernelLand/Kernel/include/api_drv_terminal.h @@ -0,0 +1,166 @@ +/** + * \file api_drv_terminal.h + * \brief Terminal Driver Interface Definitions +*/ +#ifndef _API_DRV_TERMINAL_H +#define _API_DRV_TERMINAL_H + +#include + +/** + * \brief Common Terminal IOCtl Calls + * \extends eTplDrv_IOCtl + */ +enum eTplTerminal_IOCtl { + /** + * ioctl(..., int *mode) + * \brief Get/Set the current video mode type + * \param mode Pointer to an integer with the new mode number (or NULL) + * If \a mode is non-NULL the current terminal mode is changed/updated + * to the mode indicated by \a *mode + * \note See ::eTplTerminal_Modes + * \return Current/new terminal mode + */ + TERM_IOCTL_MODETYPE = 4, + + /** + * ioctl(..., int *width) + * \brief Get/set the display width + * \param width Pointer to an integer containing the new width (or NULL) + * \return Current/new width + * + * If \a width is non-NULL the current width is updated (but is not + * applied until ::TERM_IOCTL_MODETYPE is called with \a mode non-NULL. + */ + TERM_IOCTL_WIDTH, + + /** + * ioctl(..., int *height) + * \brief Get/set the display height + * \param height Pointer to an integer containing the new height + * \return Current height + * + * If \a height is non-NULL the current height is updated (but is not + * applied until ::TERM_IOCTL_MODETYPE is called with a non-NULL \a mode. + */ + TERM_IOCTL_HEIGHT, + + /** + * ioctl(..., tTerm_IOCtl_Mode *info) + * \brief Queries the current driver about it's native modes + * \param info A pointer to a ::tTerm_IOCtl_Mode with \a ID set to + * the mode index (or NULL) + * \return Number of modes + * + * If \a info is NULL, the number of avaliable vative display modes + * is returned. These display modes will have sequential ID numbers + * from zero up to this value. + * + * \note The id field of \a info is not for use with ::TERM_IOCTL_MODETYPE + * This field is just for indexing the mode to get its information. + */ + TERM_IOCTL_QUERYMODE, + + /** + * ioctl(...) + * \brief Forces the current terminal to be shown + */ + TERM_IOCTL_FORCESHOW, + + /** + * ioctl(..., tVideo_IOCtl_Pos *pos) + * \brief Returns the current text cursor position + * \param pos New cursor position. If NULL, the position is not changed + * \return Cursor position (as X+Y*Width) + */ + TERM_IOCTL_GETSETCURSOR, + + /** + * ioctl(..., tVideo_IOCtl_Bitmap *Bmp) + * \brief Set the video cursor bitmap + * \param Bmp New bitmap (if NULL, the current bitmap is removed) + * \return Boolean failure + */ + TERM_IOCTL_SETCURSORBITMAP, +}; + +/** + * \brief Virtual Terminal Mode + * Describes a VTerm mode to the caller of ::TERM_IOCTL_QUERYMODE + */ +typedef struct sTerm_IOCtl_Mode +{ + short ID; //!< Zero Based index of mode + short DriverID; //!< Driver's ID number (from ::tVideo_IOCtl_Mode) + Uint16 Height; //!< Height + Uint16 Width; //!< Width + Uint8 Depth; //!< Bits per cell + struct { + unsigned bText: 1; //!< Text Mode marker + unsigned unused: 7; + }; +} tTerm_IOCtl_Mode; + +/** + * \brief Terminal Modes + */ +enum eTplTerminal_Modes { + /** + * \brief UTF-8 Text Mode + * Any writes to the terminal file are treated as UTF-8 encoded + * strings and reads will also return UTF-8 strings. + */ + TERM_MODE_TEXT, + + /** + * \brief 32bpp Framebuffer + * Writes to the terminal file will write to the framebuffer. + * Reads will return UTF-32 characters + */ + TERM_MODE_FB, + + /** + * \brief 32bpp 2D Accellerated mode + * Writes to the terminal file will be read as a command stream + * defined in ::eTplTerminal_2D_Commands + */ + TERM_MODE_2DACCEL, + + /** + * \brief OpenGL 2D/3D + * Writes to the terminal file will send 3D commands + * Reads will return UTF-32 characters + * \note May or may not stay in the spec + */ + TERM_MODE_3D, + + /** + * \brief Number of terminal modes + */ + NUM_TERM_MODES +}; + +/** + * \brief 2D Command IDs + * \todo Complete this structure + * + * Command IDs for when the terminal type is eTplTerminal_Modes.TERM_MODE_2DACCEL + */ +enum eTplTerminal_2D_Commands +{ + /** + * \brief No Operation - Used for padding + */ + TERM_2DCMD_NOP, + + /** + * (Uint16 X, Y, W, H, Uint32 Data[]) + * \brief Blits a bitmap to the display + * \param X,Y Coordinates of Top-Left corner + * \param W,H Dimensions + * \param Data 32-bpp pixel data + */ + TERM_2DCMD_PUSH +}; + +#endif diff --git a/KernelLand/Kernel/include/api_drv_video.h b/KernelLand/Kernel/include/api_drv_video.h new file mode 100644 index 00000000..ff9c395b --- /dev/null +++ b/KernelLand/Kernel/include/api_drv_video.h @@ -0,0 +1,484 @@ +/** + * \file api_drv_video.h + * \brief Video Driver Interface Definitions + * \note For AcessOS Version 1 + * + * Video drivers extend the common driver interface api_drv_common.h + * and must support the IOCtl numbers defined in this file to be + * compatable with Acess (drivers may implement more IOCtls above + * DRV_IOCTL_USERMIN). + * + * \section IOCtls + * As said, a compatable driver must implement these calls correctly, + * but they may choose not to allow direct user access to the framebuffer. + * + * \section Screen Contents + * Writes to the driver's file while in component colour modes + * must correspond to a change of the contents of the screen. The framebuffer + * must start at offset 0 in the file. + * Reading from the screen must either return zero, or read from the + * framebuffer. + * + * \section Mode Support + * All video drivers must support text output for every resolution (hardware + * accelerated or software), and at least the _BLIT and _FILL 2D operations + * (these may be implemented specifically for text mode). + */ +#ifndef _API_DRV_VIDEO_H +#define _API_DRV_VIDEO_H + +#include + +/** + * \enum eTplVideo_IOCtl + * \brief Common Video IOCtl Calls + * \extends eTplDrv_IOCtl + */ +enum eTplVideo_IOCtl { + /** + * ioctl(..., int *mode) + * \brief Get/Set Mode + * \return Current mode ID or -1 on error + * + * If \a mode is non-NULL, the current video mode is set to \a *mode. + * This updated ID is then returned to the user. + */ + VIDEO_IOCTL_GETSETMODE = 4, + + /** + * ioctl(..., tVideo_IOCtl_Mode *info) + * \brief Find a matching mode + * \return 1 if a mode was found, 0 otherwise + * + * Using avaliable modes matching the \a bpp and \a flags fields + * set the \a id, \a width and \a heights fields to the closest + * matching mode. + */ + VIDEO_IOCTL_FINDMODE, + + /** + * ioctl(..., tVideo_IOCtl_Mode *info) + * \brief Get mode info + * \return 1 if the mode exists, 0 otherwise + * + * Set \a info's fields to the mode specified by the \a id field. + */ + VIDEO_IOCTL_MODEINFO, + + /** + * ioctl(..., int *NewFormat) + * \brief Switches between Text, Framebuffer and 3D modes + * \param NewFormat Pointer to the new format code (see eTplVideo_BufFormats) + * \return Original format + * + * Enabes and disables the video text mode, changing the behavior of + * writes to the device file. + */ + VIDEO_IOCTL_SETBUFFORMAT, + + /** + * ioctl(..., tVideo_IOCtl_Pos *pos) + * \brief Sets the cursor position + * \return Boolean success + * + * Set the text mode cursor position (if it is supported) + * If the \a pos is set to (-1,-1) the cursor is hidden, otherwise + * \a pos MUST be within the current screen size (as given by the + * current mode's tVideo_IOCtl_Mode.width and tVideo_IOCtl_Mode.height + * fields). + */ + VIDEO_IOCTL_SETCURSOR, + + /** + * ioctl(..., tVideo_IOCtl_Bitmap *Image) + * \brief Sets the cursor image + * \return Boolean success + * + * Sets the graphics mode cursor image + */ + VIDEO_IOCTL_SETCURSORBITMAP +}; + +/** + * \brief Symbolic names for Video IOCtls (#4 onwards) + */ +#define DRV_VIDEO_IOCTLNAMES "getset_mode", "find_mode", "mode_info", "set_buf_format", "set_cursor", "set_cursor_bitmap" + +/** + * \brief Mode Structure used in IOCtl Calls + * + * Defines a video mode supported by (or requested of) this driver (depending + * on what ioctl call is used) + */ +typedef struct sVideo_IOCtl_Mode +{ + short id; //!< Mode ID + Uint16 width; //!< Width + Uint16 height; //!< Height + Uint8 bpp; //!< Bits per pixel + Uint8 flags; //!< Mode Flags (none defined, should be zero) +} tVideo_IOCtl_Mode; + +/** + * \brief Buffer Format Codes + */ +enum eTplVideo_BufFormats +{ + /** + * \brief Text Mode + * + * The device file presents itself as an array of ::tVT_Char + * each describing a character cell on the screen. + * These cells are each \a giVT_CharWidth pixels wide and + * \a giVT_CharHeight high. + */ + VIDEO_BUFFMT_TEXT, + /** + * \brief Framebuffer Mode + * + * The device file presents as an array of 32-bpp pixels describing + * the entire screen. The format of a single pixel is in xRGB format + * (top 8 bits ignored, next 8 bits red, next 8 bits green and + * the bottom 8 bits blue) + */ + VIDEO_BUFFMT_FRAMEBUFFER, + /** + * \brief 2D Accelerated Mode + * + * The device file acts as a character device, accepting a stream of + * commands described in eTplVideo_2DCommands when written to. + */ + VIDEO_BUFFMT_2DSTREAM, + /** + * \brief 3D Accelerated Mode + * + * The device file acts as a character device, accepting a stream of + * commands described in eTplVideo_3DCommands when written to. + */ + VIDEO_BUFFMT_3DSTREAM +}; + +/** + * \brief 2D Accellerated Video Commands + * + * Commands passed in the command stream for ::VIDEO_BUFFMT_2DSTREAM + */ +enum eTplVideo_2DCommands +{ + /** + * \brief No Operation + */ + VIDEO_2DOP_NOP, + /** + * \brief Fill a region + * \param X Uint16 - Leftmost pixels of the region + * \param Y Uint16 - Topmost pixels of the region + * \param W Uint16 - Width of the region + * \param H Uint16 - Height of the region + * \param Colour Uint32 - Value to fill with + */ + VIDEO_2DOP_FILL, + /** + * \brief Copy a region from one part of the framebuffer to another + * \param DestX Uint16 - Leftmost pixels of the destination + * \param DestY Uint16 - Topmost pixels of the destination + * \param SrcX Uint16 - Leftmost pixels of the source + * \param SrcY Uint16 - Topmost pixels of the source + * \param Width Uint16 - Width of the region + * \param Height Uint16 - Height of the region + */ + VIDEO_2DOP_BLIT, + + + /** + * \brief Copy a region from video memory to the framebuffer + */ + VIDEO_2DOP_BLITBUF, + + /** + * \brief Copy and scale a region from video memory to the framebuffer + */ + VIDEO_2DOP_BLITSCALEBUF, + + NUM_VIDEO_2DOPS +}; + +/** + * \brief Describes a position in the video framebuffer + */ +typedef struct sVideo_IOCtl_Pos +{ + Sint16 x; //!< X Coordinate + Sint16 y; //!< Y Coordinate +} tVideo_IOCtl_Pos; + +/** + * \brief Bitmap object (out of band image) + */ +typedef struct sVideo_IOCtl_Bitmap +{ + Sint16 W; //!< Width of image + Sint16 H; //!< Height of image + Sint16 XOfs; //!< X Offset of center + Sint16 YOfs; //!< Y Offset of center + Uint32 Data[]; //!< Image data (ARGB array) +} tVideo_IOCtl_Bitmap; + +/** + * \brief Virtual Terminal Representation of a character + */ +typedef struct sVT_Char +{ + Uint32 Ch; //!< UTF-32 Character + union { + struct { + Uint16 BGCol; //!< 12-bit Foreground Colour + Uint16 FGCol; //!< 12-bit Background Colour + }; + Uint32 Colour; //!< Compound colour for ease of access + }; +} tVT_Char; + +/** + * \name Basic builtin colour definitions + * \{ + */ +#define VT_COL_BLACK 0x0000 +#define VT_COL_GREY 0x0888 +#define VT_COL_LTGREY 0x0CCC +#define VT_COL_WHITE 0x0FFF +/** + * \} + */ + +//! \brief Defines the width of a rendered character +extern int giVT_CharWidth; +//! \brief Defines the height of a rendered character +extern int giVT_CharHeight; +/** + * \name Font rendering + * \{ + */ +/** + * \brief Driver helper that renders a character to a buffer + * \param Codepoint Unicode character to render + * \param Buffer Buffer to render to + * \param Depth Bit depth of the destination buffer + * \param Pitch Number of bytes per line + * \param BGC 32-bit Background Colour + * \param FGC 32-bit Foreground Colour + * + * This function is provided to help video drivers to support a simple + * text mode by keeping the character rendering abstracted from the driver, + * easing the driver development and reducing code duplication. + */ +extern void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Depth, int Pitch, Uint32 BGC, Uint32 FGC); +/** + * \fn Uint32 VT_Colour12to24(Uint16 Col12) + * \brief Converts a colour from 12bpp to 24bpp + * \param Col12 12-bpp input colour + * \return Expanded 32-bpp (24-bit colour) version of \a Col12 + */ +extern Uint32 VT_Colour12to24(Uint16 Col12); +/** + * \brief Converts a colour from 12bpp to 14bpp + * \param Col12 12-bpp input colour + * \return 15 bits per pixel value + */ +extern Uint16 VT_Colour12to15(Uint16 Col12); +/** + * \brief Converts a colour from 12bpp to 32bpp + * \param Col12 12-bpp input colour + * \param Depth Desired bit depth + * \return \a Depth bit number, denoting Col12 + * + * Expands the source colour into a \a Depth bits per pixel representation. + * The colours are expanded with preference to Green, Blue and Red in that order + * (so, green gets the first spare pixel, blue gets the next, and red never gets + * the spare). \n + * The final bit of each component is used to fill the lower bits of the output. + */ +extern Uint32 VT_Colour12toN(Uint16 Col12, int Depth); +/** + * \} + */ + +/** + * \brief Maximum cursor width for using the DrvUtil software cursor + */ +#define DRVUTIL_MAX_CURSOR_W 32 +/** + * \brief Maximum cursor width for using the DrvUtil software cursor + */ +#define DRVUTIL_MAX_CURSOR_H 32 + +/** + * \brief Framebuffer information used by all DrvUtil_Video functions + */ +typedef struct sDrvUtil_Video_BufInfo +{ + /** + * \name Framebuffer state + * \{ + */ + /** + * \brief Framebuffer virtual address + */ + void *Framebuffer; + /** + * \brief Bytes between the start of each line + */ + int Pitch; + /** + * \brief Number of pixels in each line + */ + short Width; + /** + * \brief Total number of lines + */ + short Height; + /** + * \brief Bit depth of the framebuffer + */ + short Depth; + /* + * \} + */ + + /** + * \brief Buffer write format + */ + short BufferFormat; + + /** + * \name Software cursor controls + * \{ + */ + /** + * \brief X coordinate of the cursor + */ + short CursorX; + /** + * \brief Y coordinate of the cursor + */ + short CursorY; + + /** + * \brief Cursor bitmap + */ + tVideo_IOCtl_Bitmap *CursorBitmap; + /* + * \} + */ + + /* + * \name Internal fields + * \{ + */ + + /** + * \brief Buffer to store the area under the cursor + */ + void *CursorSaveBuf; + + int CursorReadX; //!< X offset in cursor bitmap corresponding to \a CursorDestX + int CursorReadY; //!< Same as \a CursorReadX but for Y + int CursorRenderW; //!< Width of rendered cursor + int CursorRenderH; //!< Height of rendered cursor + int CursorDestX; //!< X coordinate Destination for rendered cursor + int CursorDestY; //!< Y coordinate destination for rendered cursor + + /* + * \} + */ +} tDrvUtil_Video_BufInfo; + +/** + * \brief Handlers for eTplVideo_2DCommands + */ +typedef struct sDrvUtil_Video_2DHandlers +{ + /** + * \brief No Operation, Ignored + * \see VIDEO_2DOP_NOP + */ + void *Nop; + /** + * \brief Fill a buffer region + * \param X Lefthand edge + * \param Y Top edge + * \param W Width + * \param H Height + * \param Colour Colour to fill with + * \see VIDEO_2DOP_FILL + */ + void (*Fill)(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour); + /** + * \brief Fill a buffer region + * \param DestX Lefthand edge of destination + * \param DestY Top edge of destination + * \param SrcX Lefthand edge of source + * \param SrcY Top edge of source + * \param W Width + * \param H Height + * \see VIDEO_2DOP_BLIT + */ + void (*Blit)(void *Ent, Uint16 DestX, Uint16 DestY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H); +} tDrvUtil_Video_2DHandlers; + +/** + * \brief Handle a 2D operation stream for a driver + * \param Ent Value to pass to handlers + * \param Buffer Stream buffer + * \param Length Length of stream + * \param Handlers Handlers to use for the stream + * \param SizeofHandlers Size of \a tDrvUtil_Video_2DHandlers according + * to the driver. Used as version control and error avoidence. + */ +extern int DrvUtil_Video_2DStream(void *Ent, const void *Buffer, int Length, + tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers); + +/** + * \brief Perform write operations to a LFB + * \param FBInfo Framebuffer descriptor, see type for details + * \param Offset Offset provided by VFS call + * \param Length Length provided by VFS call + * \param Src Data from VFS call + * \return Number of bytes written + * + * Handles all write modes in software, using the VT font calls for rendering. + * \note Calls the cursor clear and redraw if the cursor area is touched + */ +extern int DrvUtil_Video_WriteLFB(tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, const void *Src); + +/** + * \name Software cursor rendering + * \{ + */ +/** + * \brief Set the cursor bitmap for a buffer + * \param Buf Framebuffer descriptor + * \param Bitmap New cursor bitmap + */ +extern int DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap); +/** + * \brief Render the cursor at (\a X, \a Y) + * \param Buf Framebuffer descriptor, see type for details + * \param X X coord of the cursor + * \param Y Y coord of the cursor + */ +extern void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y); +/** + * \brief Removes the rendered cursor from the screen + * \param Buf Framebuffer descriptor, see type for details + */ +extern void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf); + +/** + * \brief Text mode cursor image + */ +extern tVideo_IOCtl_Bitmap gDrvUtil_TextModeCursor; +/** + * \} + */ +#endif diff --git a/KernelLand/Kernel/include/apidoc/arch_x86.h b/KernelLand/Kernel/include/apidoc/arch_x86.h new file mode 100644 index 00000000..7818c8c5 --- /dev/null +++ b/KernelLand/Kernel/include/apidoc/arch_x86.h @@ -0,0 +1,38 @@ +/** + * \file apidoc/arch_x86.h + * \brief x86(-64) Specific Functions + * \author John Hodge (thePowersGang) + * + * \section toc Table of Contents + * - \ref portio "Port IO" + * - \ref dma "DMA - Direct Memory Access" + * + * \section portio Port IO + * The x86 architecture has two memory spaces, the first is the system + * memory accessable using standard loads and stores. The second is the + * 16-bit IO Bus. This bus is accessed using the \a in and \a out opcodes + * and is used to configure devices attached to the system. + * A driver should not use \a in and \a out directly, but instead use + * the provided \a in* and \a out* functions to access the IO Bus. + * This allows the kernel to run a driver in userspace if requested without + * the binary needing to be altered. + * + * \section dma DMA - Direct Memory Access + */ + +/** + * \name IO Bus Access + * \{ + */ +extern Uint8 inb(Uint16 Port); //!< Read 1 byte from the IO Bus +extern Uint16 inw(Uint16 Port); //!< Read 2 bytes from the IO Bus +extern Uint32 inl(Uint16 Port); //!< Read 4 bytes from the IO Bus +extern Uint64 inq(Uint16 Port); //!< Read 8 bytes from the IO Bus\ + +extern void outb(Uint16 Port, Uint8 Value); //!< Write 1 byte to the IO Bus +extern void outw(Uint16 Port, Uint16 Value); //!< Write 2 bytes to the IO Bus +extern void outl(Uint16 Port, Uint32 Value); //!< Write 4 bytes to the IO Bus +extern void outq(Uint16 Port, Uint64 Value); //!< Write 8 bytes to the IO Bus +/** + * \} + */ diff --git a/KernelLand/Kernel/include/apidoc_mainpage.h b/KernelLand/Kernel/include/apidoc_mainpage.h new file mode 100644 index 00000000..0060bafa --- /dev/null +++ b/KernelLand/Kernel/include/apidoc_mainpage.h @@ -0,0 +1,87 @@ +/** + * \file apidoc_mainpage.h + * \brief API Documentation Home Page + * \author John Hodge (thePowersGang) + * + * \mainpage Acess 2 Kernel API Documentation + * + * \section intro Introduction + * These documents attempt to describe the standard Acess 2 (and hopefully + * future versions) Kernel mode API. + * The documentation covers filesystem drivers, binary formats and the + * various device driver interface standards. + * + * \section index "Sections" + * - \ref modules.h "Module Definitions" + * - Describes how a module is defined in Acess + * - \ref binary.h "Binary Formats" + * - Explains how to register a new binary format with the kernel + * - \ref vfs.h "VFS - The Virtual File System" + * - The VFS is the core of Acess's driver architecture + * - \ref drivers "Device Drivers" + * - Describes how drivers should use the VFS to expose themselves to the + * user. + * - Drivers for specific types of hardware must behave in the specific + * way described here. + * + * \page drivers Device Drivers + * + * \section toc Contents + * - \ref drvintro "Introduction" + * - \ref drv_misc "Miscelanious Devices" + * - \ref drv_video "Video Drivers" + * + * \section drvintro Introduction + * All Acess2 device drivers communicate with user-level (and other parts + * of the greater kernel) via the VFS. They register themselves in a similar + * way to how filesystem drivers do, however instead of registering with + * the VFS core, they register with a special filesystem driver called the + * \ref fs_devfs.h "Device Filesystem" (devfs). The DevFS provides the + * ::DevFS_AddDevice function that takes a ::tDevFS_Driver structure as + * an agument. This structure specifies the driver's name and its root + * VFS node. This node is used to provide the user access to the + * driver's functions via IOCtl calls and Reading or Writing to the driver + * file. Drivers are also able to expose a readonly buffer by using + * \ref fs_sysfs.h "ProcDev", usually to provide state information or device + * capabilities for the the user. + * + * The device driver interfaces are all based on the core specifcation + * in api_drv_common.h (Common Device Driver definitions). + * The following subsections define the various specific types of driver + * interfaces. These definitions only define the bare minimum of what the + * driver must implement, if the driver author so wants to, they can add + * IOCtl calls and/or files (where allowed by the type specifcation) to + * their device's VFS layout. + * + * \subsection drv_misc Miscelanious Devices + * If a device type does not have a specifcation for it, the driver can + * identify itself as a miscelanious device by returning DRV_TYPE_MISC + * from \ref DRV_IOCTL_TYPE. + * A misc device must at least implement the IOCtl calls defined in the + * \ref api_drv_common.h "Common Device Driver definitions", allowing it + * to be identified easily by the user and for interfacing programs to + * utilise the DRV_IOCTL_LOOKUP call. + * + * \subsection drv_video Video Devices + * Video drivers are based on a framebuffer model (unless in 3D mode, + * which is not yet fully standardised, so should be ignored). + * The driver will contain only one VFS node, that exposes the video + * framebuffer (this may not be the true framebuffer, to allow for double-buffering) + * to the user. See the full documentation in api_drv_video.h for the + * complete specifcation. + * + * \subsection drv_disk Disk/Storage Devices + * Storage devices present themselves as a linear collection of bytes. + * Reads and writes to the device need not be aligned to the stated block + * size, but it is suggested that users of a storage device file align + * their accesses to block boundaries. + * The functions DrvUtil_ReadBlock and DrvUtil_WriteBlock are provided + * to storage drivers to assist in handling non-alinged reads and writes. + * + * \see api_drv_common.h Common Spec. + * \see api_drv_video.h Video Device Spec. + * \see api_drv_keyboard.h Keyboard Device Spec. + * \see api_drv_disk.h Disk/Storage Device Spec. + * \see api_drv_network.h Network Device Spec. + * \see api_drv_terminal.h Virtual Terminal Spec. + */ diff --git a/KernelLand/Kernel/include/binary.h b/KernelLand/Kernel/include/binary.h new file mode 100644 index 00000000..c94a1b09 --- /dev/null +++ b/KernelLand/Kernel/include/binary.h @@ -0,0 +1,173 @@ +/** + * \file binary.h + * \brief Binary Loader Definitions + * \author John Hodge (thePowersGang) + */ +#ifndef _BINARY_H +#define _BINARY_H + +// === TYPES === +/** + * \brief Representation of a section in a binary file + * + * Tells the binary loader where the page data resides on disk and where + * to load it to (relative to the binary base). Once the data is read, + * the \a Physical field contains the physical address of the page. + */ +typedef struct sBinarySection +{ + Uint64 Offset; //!< File offset of the section + tVAddr Virtual; //!< Virtual load address + size_t FileSize; //!< Number of bytes to load from the file + size_t MemSize; //!< Number of bytes in memory + Uint Flags; //!< Load Flags +} tBinarySection; + +/** + * \brief Flags for ::tBinarySection.Flags + * \name Binary Section Flags + * \{ + */ +//! \brief Read-only +#define BIN_SECTFLAG_RO 0x0001 +//! \brief Executable +#define BIN_SECTFLAG_EXEC 0x0002 +/** + * \} + */ + +/** + * \brief Defines a binary file + * + * This structure defines and maintains the state of a binary during and + * after loading. + * Before the binary is loaded into memory (when it has just been returned + * from tBinaryType.Load) the \a Pages array will contain the file offsets + * to the page data in the \a Physical fields (or -1 for uninitialised + * data) and the \a Size fields define how much data is stored in-file + * for the page (to allow partial pages to be loaded from disk) + * Once the binary is loaded (NOTE: Drivers do not need to know about this, + * it is here for information only) the \a Physical fields now contain the + * physical addresses of the pages filled with the data. The \a Virtual + * fields contain the preferred virtual address of the pages (a given + * process may have these pages mapped to a different location). + */ +typedef struct sBinary +{ + struct sBinary *Next; //!< Pointer used by the kernel + + tMount MountID; //!< Mount ID + tInode Inode; //!< Inode (Used for fast reopen) + + /** + * \brief Interpreter used to load the file + * \note This can be either requested by the individual file, or a per-driver option + */ + const char *Interpreter; + /** + * \brief Entrypoint of the binary (at requested base); + */ + tVAddr Entry; + /** + * \brief File's requested load base + */ + tVAddr Base; + /** + * \brief Number of times this binary has been mapped + */ + int ReferenceCount; + /** + * \brief Number of sections defined in the file + */ + int NumSections; + /** + * \brief Array of sections defined by this binary + * \note Contains \a NumSections entries + */ + tBinarySection LoadSections[]; +} tBinary; + +/** + * \brief Binary type definition + * + * This structure is used to define a loader for a specific binary type + * so that the kernel's core binary loader can understand that type. + * The tBinaryType.Relocate and tBinaryType.GetSymbol need only be non-NULL + * if the binary type is to be used for kernel modules, otherwise it will + * only be able to load binaries for user space. + */ +typedef struct sBinaryType +{ + /** + * \brief Pointer used by the kernel + * \note Should not be touched by the driver (initialise to NULL) + */ + struct sBinaryType *Next; + /** + * \brief Identifying DWord + * + * If he first 32-bits of the file match this value (when ANDed with + * tBinaryType.Mask), this binary loader will be used to load the file. + */ + Uint32 Ident; + Uint32 Mask; //!< Mask value for tBinaryType.Ident + const char *Name; //!< Name of this executable type (for debug purpouses) + /** + * \brief Read a binary from a file + * \param FD VFS File handle to file to load + * \return Pointer to a ::tBinary that describes how to load the file + * + * This function reads a binary file and returns a ::tBinary pointer + * that tells the core binary loader how to read the data from the file + * and where to map it to. + */ + tBinary *(*Load)(int FD); + + /** + * \brief Prepares a mapped binary for execution at this address + * \param Base Binary loaded in memory + * \return Boolean Success + * \note This pointer can be NULL, but then the binary cannot be used + * to load a kernel module. + * + * tBinaryType.Relocate takes a binary that was loaded according to + * tBinaryType.Load and prepares it to run at the address it is + * loaded to, attempting to satisfy any external unresolved symbols + * required, if a symbol cannot be located, the function will return + * zero. + */ + int (*Relocate)(void *Base); + + /** + * \brief Gets a symbol's address from a loaded binary + * \note The binary pointed to by \a Base may not have been through + * tBinaryType.Relocate at this time, so the driver should + * accomodate this. + */ + int (*GetSymbol)(void *Base, const char *Name, Uint *Dest); +} tBinaryType; + +/** + * \brief Registers an interpreter path with the binary loader + * \param Path Path to the requested interpreter (need not be a "true" path) + * \return Pointer to the cached string + * + * Speeds up checking if the intepreter is loaded in the kernel by allowing + * the search to use pointer comparisons instead of string comparisons. + */ +extern char *Binary_RegInterp(char *Path); + +/** + * \brief Registers a binary type with the kernel's loader + * \param Type Pointer to the loader's type structure + * \return Boolean success + * \note The structure \a Type must be persistant (usually it will be a + * constant global variable) + * + * This function tells the binary loader about a new file type, and gives + * it the functions to read the type into a ::tBinary structure, relocate + * it and to find the value of symbols defined within the binary. + */ +extern int Binary_RegisterType(tBinaryType *Type); + +#endif diff --git a/KernelLand/Kernel/include/binary_ext.h b/KernelLand/Kernel/include/binary_ext.h new file mode 100644 index 00000000..b2fc89d8 --- /dev/null +++ b/KernelLand/Kernel/include/binary_ext.h @@ -0,0 +1,17 @@ +/* + * Acess 2 + * binary_ext.h + * - Exported Symbols from the binary loader + */ +#ifndef _BINARY_EXT_H +#define _BINARY_EXT_H + +// === FUNCTIONS === +extern void *Binary_LoadFile(const char *Path); +extern void *Binary_LoadKernel(const char *Path); +extern Uint Binary_Relocate(void *Mem); +extern void Binary_Unload(void *Base); +extern int Binary_GetSymbol(const char *Name, Uint *Dest); +extern Uint Binary_FindSymbol(void *Base, const char *Name, Uint *Val); + +#endif diff --git a/KernelLand/Kernel/include/drv_pci.h b/KernelLand/Kernel/include/drv_pci.h new file mode 100644 index 00000000..33c4a3ec --- /dev/null +++ b/KernelLand/Kernel/include/drv_pci.h @@ -0,0 +1,61 @@ +/** + * \file drv_pci.h + * \brief PCI Bus Driver + * \author John Hodge (thePowersGang) + */ +#ifndef _DRV_PCI_H +#define _DRV_PCI_H + +/** + * \brief PCI Class Codes + */ +enum ePCIClasses +{ + PCI_CLASS_PRE20 = 0x00, + PCI_CLASS_STORAGE, + PCI_CLASS_NETWORK, + PCI_CLASS_DISPLAY, + PCI_CLASS_MULTIMEDIA, + PCI_CLASS_MEMORY, + PCI_CLASS_BRIDGE, + PCI_CLASS_COMM, + PCI_CLASS_PREPH, + PCI_CLASS_INPUT, + PCI_CLASS_DOCKING, + PCI_CLASS_PROCESSORS, + PCI_CLASS_SERIALBUS, + PCI_CLASS_MISC = 0xFF +}; + +enum ePCIOverClasses +{ + PCI_OC_PCIBRIDGE = 0x060400, + PCI_OC_SCSI = 0x010000 +}; + +typedef int tPCIDev; + +/** + * \brief Count PCI Devices + * + * Counts the number of devices with specified Vendor and Device IDs + */ +extern int PCI_CountDevices(Uint16 VendorID, Uint16 DeviceID); +extern tPCIDev PCI_GetDevice(Uint16 VendorID, Uint16 DeviceID, int index); +/** + * \param ClassCode (Class:SubClass:PI) + */ +extern tPCIDev PCI_GetDeviceByClass(Uint32 ClassCode, Uint32 Mask, tPCIDev prev); + +extern int PCI_GetDeviceInfo(tPCIDev id, Uint16 *Vendor, Uint16 *Device, Uint32 *Class); +extern int PCI_GetDeviceVersion(tPCIDev id, Uint8 *Revision); +extern int PCI_GetDeviceSubsys(tPCIDev id, Uint16 *SubsystemVendor, Uint16 *SubsystemID); + +extern Uint32 PCI_ConfigRead(tPCIDev id, int Offset, int Size); +extern void PCI_ConfigWrite(tPCIDev id, int Offset, int Size, Uint32 Value); + +extern Uint8 PCI_GetIRQ(tPCIDev id); +extern Uint32 PCI_GetBAR(tPCIDev id, int BAR); +//extern Uint16 PCI_AssignPort(tPCIDev id, int bar, int count); + +#endif diff --git a/KernelLand/Kernel/include/drv_pci_int.h b/KernelLand/Kernel/include/drv_pci_int.h new file mode 100644 index 00000000..f1681417 --- /dev/null +++ b/KernelLand/Kernel/include/drv_pci_int.h @@ -0,0 +1,17 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * drv_pci_int.h + * - PCI internal definitions + */ +#ifndef _DRV_PCI_INT_H +#define _DRV_PCI_INT_H + +#include + +extern Uint32 PCI_CfgReadDWord(Uint32 Addr); +extern void PCI_CfgWriteDWord(Uint32 Addr, Uint32 data); + +#endif + diff --git a/KernelLand/Kernel/include/errno.h b/KernelLand/Kernel/include/errno.h new file mode 100644 index 00000000..3652f19f --- /dev/null +++ b/KernelLand/Kernel/include/errno.h @@ -0,0 +1,30 @@ +/* + * Acess2 + * errno.h + */ +#ifndef _ERRNO_H +#define _ERRNO_H + +enum eErrorNums +{ + EOK, + + ENOSYS, // Invalid Instruction + EINVAL, // Invalid Paramater + ENOMEM, // No free memory + EACCES, // Not permitted + ENOTFOUND, // Item not found + EREADONLY, // Read only + ENOTIMPL, // Not implemented + ENOENT, // No entry? + EEXIST, // Already exists + ENFILE, // Too many open files + ENOTDIR, // Not a directory + + EALREADY, // Operation was a NOP + EINTERNAL, // Internal Error + + NUM_ERRS +}; + +#endif diff --git a/KernelLand/Kernel/include/events.h b/KernelLand/Kernel/include/events.h new file mode 100644 index 00000000..675c3dbc --- /dev/null +++ b/KernelLand/Kernel/include/events.h @@ -0,0 +1,23 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * events.h + * - Thread Events + */ +#ifndef _EVENTS_H_ +#define _EVENTS_H_ + +#include + +#define THREAD_EVENT_VFS 0x00000001 +#define THREAD_EVENT_IPCMSG 0x00000002 +#define THREAD_EVENT_SIGNAL 0x00000004 +#define THREAD_EVENT_TIMER 0x00000008 + +// === FUNCTIONS === +extern void Threads_PostEvent(tThread *Thread, Uint32 EventMask); +extern Uint32 Threads_WaitEvents(Uint32 EventMask); + +#endif + diff --git a/KernelLand/Kernel/include/fs_devfs.h b/KernelLand/Kernel/include/fs_devfs.h new file mode 100644 index 00000000..c099fc6c --- /dev/null +++ b/KernelLand/Kernel/include/fs_devfs.h @@ -0,0 +1,35 @@ +/** + * \file fs_devfs.h + * \brief Acess Device Filesystem interface + * \author John Hodge (thePowersGang) + */ +#ifndef _FS_DEVFS_H +#define _FS_DEVFS_H +#include + +// === TYPES === +/** + * \brief DevFS driver definition + */ +typedef struct sDevFS_Driver +{ + struct sDevFS_Driver *Next; //!< Set to NULL by drivers (used internally) + const char *Name; //!< Name of the driver file/folder (must be unique) + tVFS_Node RootNode; //!< Root node of driver +} tDevFS_Driver; + +// === FUNCTIONS === +/** + * \fn int DevFS_AddDevice(tDevFS_Driver *Device) + * \brief Registers a device in the Device Filesystem + * \param Device Pointer to a persistant structure that represents the driver + * \return Boolean success + */ +extern int DevFS_AddDevice(tDevFS_Driver *Device); + +/** + * \brief Unregisters a device with the Device Filesystem + */ +extern void DevFS_DelDevice(tDevFS_Driver *Device); + +#endif diff --git a/KernelLand/Kernel/include/fs_sysfs.h b/KernelLand/Kernel/include/fs_sysfs.h new file mode 100644 index 00000000..aac64d91 --- /dev/null +++ b/KernelLand/Kernel/include/fs_sysfs.h @@ -0,0 +1,41 @@ +/* + * Acess2 + * - SysFS Export Header + */ +/** + * \file fs_sysfs.h + * \brief ProcDev/SysFS Interface + * \author John Hodge (thePowersGang) + * + * + */ +#ifndef _FS_SYSFS_H_ +#define _FS_SYSFS_H_ + +/** + * \brief Registers a file in the SysFS tree + * \param Path Path relative to the SysFS root (no .. or .) + * \param Data File buffer address + * \param Length Length of the file buffer + * \return An ID number to refer to the file, or -1 on error + * \note \a Data must be maintained until ::SysFS_UpdateFile is called + * with a different buffer, or ::SysFS_RemoveFile is called. + */ +extern int SysFS_RegisterFile(const char *Path, const char *Data, int Length); + +/** + * \brief Updates the size/pointer associated with a SysFD file + * \param ID Number returned by ::SysFS_RegisterFile + * \param Data New buffer address + * \param Length New length of the file + * \return Boolean Success + */ +extern int SysFS_UpdateFile(int ID, const char *Data, int Length); + +/** + * \brief Removes a file from the SysFS tree + * \param ID Number returned by ::SysFS_RegisterFile + */ +extern int SysFS_RemoveFile(int ID); + +#endif diff --git a/KernelLand/Kernel/include/hal_proc.h b/KernelLand/Kernel/include/hal_proc.h new file mode 100644 index 00000000..0f21a1c7 --- /dev/null +++ b/KernelLand/Kernel/include/hal_proc.h @@ -0,0 +1,94 @@ +/** + * Acess2 + * - By John Hodge (thePowersGang) + * + * include/hal_proc.h + * - HAL Process management functions + * + */ +#ifndef _HAL_PROC_H_ +#define _HAL_PROC_H_ +/** + * \file hal_proc.h + * \brief Achitecture defined thread/process management functions + */ + +#include + +/** + * \brief Initialise the architecture dependent side of threading + */ +extern void ArchThreads_Init(void); +/** + * \brief Start preemptive multithreading (if needed) + */ +extern void Proc_Start(void); +/** + * \brief Called just before a thread is freed + */ +extern void Proc_ClearThread(tThread *Thread); +/** + * \brief Called just before a process is freed + */ +extern void Proc_ClearProcess(tProcess *Process); +/** + * \brief Get the ID of this CPU + * \return Zero based CPU ID + */ +extern int GetCPUNum(void); +/** + * \brief Create a copy of the current process + * \param Flags Options for the clone + * \return ID of the new thread/process + */ +extern tTID Proc_Clone(Uint Flags); +/** + * \brief Create a new kernel thread for the process + * \param Fnc Thread root function + * \param Ptr Argument to pass the root function + * \return ID of new thread + */ +extern tTID Proc_NewKThread( void (*Fnc)(void*), void *Ptr ); +/** + * \brief Start a user task + * \param Entrypoint User entrypoint + * \param Base Base of executable (argument for ld-acess) + * \param ArgC Number of arguments when the program was invoked + * \param ArgV Heap allocated arguments and environment (two NULL terminated lists) + * \param DataSize Size of the \a ArgV buffer in bytes + * \note This function should free \a ArgV + */ +extern void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize) NORETURN; +/** + * \brief Call the fault handler for a thread + * \param Thread Thread that is at fault :) + */ +extern void Proc_CallFaultHandler(tThread *Thread); +/** + * \brief Dump the CPU state for a thread + */ +extern void Proc_DumpThreadCPUState(tThread *Thread); +/** + * \brief Select a new task and run it, suspending this + */ +extern void Proc_Reschedule(void); + +/** + * \brief Clear the user's memory space back to the minimum required to run + */ +extern void MM_ClearUser(void); +/** + * \brief Dump the address space to the debug channel + * \param Start First address + * \param End Last address + */ +extern void MM_DumpTables(tVAddr Start, tVAddr End); + +/** + * \brief Check if a buffer is valid (and all user if originally user) + * \param Addr Base address + * \param Size Size of the buffer in bytes + * \return Boolean valid (0: invalid, non-0: Valid) + */ +extern int MM_IsValidBuffer(tVAddr Addr, size_t Size); +#endif diff --git a/KernelLand/Kernel/include/heap.h b/KernelLand/Kernel/include/heap.h new file mode 100644 index 00000000..b058e8b4 --- /dev/null +++ b/KernelLand/Kernel/include/heap.h @@ -0,0 +1,25 @@ +/** + * Acess2 Kernel + * heap.h + * - Dynamic Memory Allocation exports + */ + +#ifndef _HEAP_H_ +#define _HEAP_H_ + +extern void *Heap_Allocate(const char *File, int Line, size_t Bytes); +extern void *Heap_AllocateZero(const char *File, int Line, size_t Bytes); +extern void *Heap_Reallocate(const char *File, int Line, void *Ptr, size_t Bytes); +extern void Heap_Deallocate(void *Ptr); +extern int Heap_IsHeapAddr(void *Ptr); +extern void Heap_Validate(void); + +#define malloc(size) Heap_Allocate(_MODULE_NAME_"/"__FILE__, __LINE__, (size)) +#define calloc(num,size) Heap_AllocateZero(_MODULE_NAME_"/"__FILE__, __LINE__, (num)*(size)) +#define realloc(ptr,size) Heap_Reallocate(_MODULE_NAME_"/"__FILE__, __LINE__, (ptr), (size)) +#define free(ptr) Heap_Deallocate((ptr)) +#define IsHeap(ptr) Heap_IsHeapAddr((ptr)) + +#define strdup(Str) _strdup(_MODULE_NAME_"/"__FILE__, __LINE__, (Str)) + +#endif diff --git a/KernelLand/Kernel/include/heap_int.h b/KernelLand/Kernel/include/heap_int.h new file mode 100644 index 00000000..12a709b3 --- /dev/null +++ b/KernelLand/Kernel/include/heap_int.h @@ -0,0 +1,25 @@ +/* + * AcessOS Microkernel Version + * heap_int.h + * - Internal Heap Header + */ +#ifndef _HEAP_INT_H +#define _HEAP_INT_H + +typedef struct { + Uint Size; + int ValidSize; + const char *File; + int Line; + Uint Magic; + tTime AllocateTime; + char Data[]; +} tHeapHead; + +typedef struct { + Uint Magic; + tHeapHead *Head; + tHeapHead NextHead[]; // Array to make it act like an element, but have no size and refer to the next block +} tHeapFoot; + +#endif diff --git a/KernelLand/Kernel/include/init.h b/KernelLand/Kernel/include/init.h new file mode 100644 index 00000000..9f2fc108 --- /dev/null +++ b/KernelLand/Kernel/include/init.h @@ -0,0 +1,11 @@ +/* + * AcessOS Microkernel Version + * init.h + */ +#ifndef _INIT_H +#define _INIT_H + +extern void Arch_LoadBootModules(void); +extern void StartupPrint(const char *String); + +#endif diff --git a/KernelLand/Kernel/include/iocache.h b/KernelLand/Kernel/include/iocache.h new file mode 100644 index 00000000..f008a2d0 --- /dev/null +++ b/KernelLand/Kernel/include/iocache.h @@ -0,0 +1,120 @@ +/* + * Acess2 Kernel + * - IO Cache + * + * By thePowersGang (John Hodge) + */ +/** + * \file iocache.h + * \brief I/O Caching Helper Subsystem + * + * The IO Cache abstracts caching of disk sectors away from the device + * driver to reduce code duplication and allow a central location for + * disk cache management that can be flushed simply by the kernel, without + * having to ask each driver to do it indivitually. + */ +#ifndef _IOCHACHE_H_ +#define _IOCHACHE_H_ + +// === TYPES === +/** + * \brief IO Cache Handle + */ +typedef struct sIOCache tIOCache; +/** + * \brief Write Callback + * + * Called to write a sector back to the device + */ +typedef int (*tIOCache_WriteCallback)(Uint32 ID, Uint64 Sector, void *Buffer); + +// === CONSTANTS === +/** + * \brief I/O Cache handling modes + */ +enum eIOCache_Modess { + /** + * \brief Writeback + * + * Transparently writes data straight to the device when the cache + * is written to. + */ + IOCACHE_WRITEBACK, + /** + * \brief Delay Write + * + * Only writes when instructed to (by ::IOCache_Flush) or when a + * cached sector is being reallocated. + */ + IOCACHE_DELAYWRITE, + /** + * \brief Virtual - No Writes + * + * Changes to the cache contents are only reflected in memory, + * any calls to ::IOCache_Flush will silently return without doing + * anything and if a sector is reallocated, all changes will be lost + */ + IOCACHE_VIRTUAL +}; + +// === FUNCTIONS === +/** + * \brief Creates a new IO Cache + * \param Write Function to call to write a sector to the device + * \param ID ID to pass to \a Write + * \param SectorSize Size of a cached sector + * \param CacheSize Maximum number of objects that can be in the cache at one time + */ +tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize ); + +/** + * \brief Reads from a cached sector + * \param Cache Cache handle returned by ::IOCache_Create + * \param Sector Sector's ID number + * \param Buffer Destination for the data read + * \return 1 if the data was read, 0 if the sector is not cached, -1 on error + */ + int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer ); + +/** + * \brief Adds a sector to the cache + * \param Cache Cache handle returned by ::IOCache_Create + * \param Sector Sector's ID number + * \param Buffer Data to cache + * \return 1 on success, 0 if the sector is already cached, -1 on error + */ + int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer ); + +/** + * \brief Writes to a cached sector + * \param Cache Cache handle returned by ::IOCache_Create + * \param Sector Sector's ID number + * \param Buffer Data to write to the cache + * \return 1 if the data was read, 0 if the sector is not cached, -1 on error + * + * If the sector is in the cache, it is updated. + * Wether the Write callback is called depends on the selected caching + * behaviour. + */ + int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer ); + +/** + * \brief Flush altered sectors out to the device + * \param Cache Cache handle returned by ::IOCache_Create + * + * This will call the cache's write callback on each altered sector + * to flush the write cache. + */ +void IOCache_Flush( tIOCache *Cache ); + +/** + * \brief Flushes the cache and then removes it + * \param Cache Cache handle returned by ::IOCache_Create + * \note After this is called \a Cache is no longer valid + * + * IOCache_Destroy writes all changed sectors to the device and then + * deallocates the cache for other systems to use. + */ +void IOCache_Destroy( tIOCache *Cache ); + +#endif diff --git a/KernelLand/Kernel/include/lib/keyvalue.h b/KernelLand/Kernel/include/lib/keyvalue.h new file mode 100644 index 00000000..ef37d5b3 --- /dev/null +++ b/KernelLand/Kernel/include/lib/keyvalue.h @@ -0,0 +1,43 @@ +/* + * Acess2 Kernel + * - By John Hodge + * + * include/keyvalue.h + * - Key/Value pair parsing + */ +#ifndef _ACESS_KEYVALUE_H_ +#define _ACESS_KEYVALUE_H_ + +typedef struct sKeyVal_ParseRules tKeyVal_ParseRules; +typedef struct sKeyVal_int_Rule tKeyVal_int_Rule; +typedef void (*tKeyVal_UnkCb)(char *String); +typedef void (*tKeyVal_KeyCb)(const char *Key, char *Value); + +/** + * \brief Handling rule for a key + */ +struct sKeyVal_int_Rule +{ + const char *Key; + const char *Type; // Acess printf format, with 'F' being a tKeyVal_KeyCb + void *Data; +}; + +struct sKeyVal_ParseRules +{ + /** + * \brief Function to call when no match is found + */ + tKeyVal_UnkCb Unknown; + tKeyVal_int_Rule Rules[]; +}; + +/** + * \brief Parse a NULL terminated list of strings as Key/Value pairs + * \param Rules Parsing rules + * \param Strings Input string list + */ +extern int KeyVal_ParseNull(tKeyVal_ParseRules *Rules, char **Strings); + +#endif + diff --git a/KernelLand/Kernel/include/mboot.h b/KernelLand/Kernel/include/mboot.h new file mode 100644 index 00000000..c7f33ddb --- /dev/null +++ b/KernelLand/Kernel/include/mboot.h @@ -0,0 +1,38 @@ +/* + * AcessOS Microkernel Version + * mboot.h + */ +#ifndef _MBOOT_H +#define _MBOOT_H + +#define MULTIBOOT_MAGIC 0x2BADB002 + +// === TYPES === +typedef struct { + Uint32 Flags; + Uint32 LowMem; + Uint32 HighMem; + Uint32 BootDevice; + Uint32 CommandLine; + Uint32 ModuleCount; + Uint32 Modules; + Uint32 SymbolInfo[4]; // #32 UNUSED + Uint32 MMapLength; + Uint32 MMapAddr; // #40 +} tMBoot_Info; + +typedef struct { + Uint32 Start; + Uint32 End; + Uint32 String; + Uint32 Resvd; +} tMBoot_Module; + +typedef struct { + Uint32 Size; // (May be at offset -4) + Uint64 Base; + Uint64 Length; + Uint32 Type; //1:RAM,Else Reserved +} __attribute__ ((packed)) tMBoot_MMapEnt; + +#endif diff --git a/KernelLand/Kernel/include/modules.h b/KernelLand/Kernel/include/modules.h new file mode 100644 index 00000000..d7281336 --- /dev/null +++ b/KernelLand/Kernel/include/modules.h @@ -0,0 +1,123 @@ +/* + * AcessOS 2 + * - Module Loader + */ +/** + * \file modules.h + * \brief Module Handling and Loader Definitions + * \author John Hodge (thePowersGang) + * + * This file serves two pourposes. First it defines the format for native + * Acess2 modules and the functions to create them. + * Second, it defines the structure and register function for new module + * loaders, allowing Acess to understand many different module / driver + * formats. + * + * Modules are defined by including this file in the module's main source + * file and using the ::MODULE_DEFINE macro to create the module header. + * + * To register a new module loader with the kernel, the loader module must + * create and populate an instance of ::tModuleLoader then pass it to + * ::Module_RegisterLoader + */ +#ifndef _MODULE_H +#define _MODULE_H + +/** + * \brief Module header magic value + */ +#define MODULE_MAGIC ('A'|('M'<<8)|('D'<<16)|('\2'<<24)) + +/** + * \def MODULE_ARCH_ID + * \brief Architecture ID + */ +// IA32 - Architecture 1 +#if ARCHDIR == x86 +# define MODULE_ARCH_ID 1 +// IA64 - Architecture 2 +#elif ARCHDIR == x86_64 +# define MODULE_ARCH_ID 2 +#else +# error "Unknown architecture when determining MODULE_ARCH_ID ('" #ARCHDIR "')" +#endif + +/** + * \brief Define a module + * \param _flags Module Flags + * \param _ver Module Version + * \param _ident Unique Module Name + * \param _entry Module initialiser / entrypoint + * \param _deinit Module cleanup / unloader + * \param _deps NULL terminated list of this's module's dependencies + * Contains the identifiers of the required modules. + */ +#define MODULE_DEFINE(_flags,_ver,_ident,_entry,_deinit,_deps...) \ + const char *EXPAND_CONCAT(_DriverDeps_,_ident)[]={_deps};\ + tModule __attribute__ ((section ("KMODULES"),unused))\ + EXPAND_CONCAT(_DriverInfo_,_ident)=\ + {MODULE_MAGIC,MODULE_ARCH_ID,_flags,_ver,NULL,EXPAND_STR(_ident),\ + _entry,_deinit,EXPAND_CONCAT(_DriverDeps_,_ident)} + +/** + * \brief Module header + * \note There is no reason for a module to touch this structure beyond + * using ::MODULE_DEFINE to create it. + */ +typedef struct sModule +{ + Uint32 Magic; //!< Identifying magic value (See ::MODULE_MAGIC) + Uint8 Arch; //!< Achitecture ID (See ::MODULE_ARCH_ID) + Uint8 Flags; //!< Module Flags + Uint16 Version; //!< Module Version in Major.Minor 8.8 form + struct sModule *Next; //!< Next module in list (not to be touched by the driver) + const char *Name; //!< Module Name/Identifier + int (*Init)(char **Arguments); //!< Module initialiser / entrypoint + void (*Deinit)(void); //!< Cleanup Function + const char **Dependencies; //!< NULL terminated list of dependencies +} PACKED tModule; + +/** + * \brief Return values for tModule.Init + */ +enum eModuleErrors +{ + MODULE_ERR_OK, //!< No Error + MODULE_ERR_MISC, //!< Misc Error + MODULE_ERR_NOTNEEDED, //!< Module not needed + MODULE_ERR_MALLOC, //!< Error with malloc/realloc/calloc + + MODULE_ERR_BADMODULE, //!< Bad module (only used by loader) + MODULE_ERR_MAX //!< Maximum defined error code +}; + +/** + * \brief Module Loader definition + * + * Allows a module to extend the loader to recognise other module types + * E.g. EDI, UDI, Windows, Linux, ... + */ +typedef struct sModuleLoader +{ + struct sModuleLoader *Next; //!< Kernel Only - Next loader in list + char *Name; //!< Friendly name for the loader + int (*Detector)(void *Base); //!< Simple detector function + int (*Loader)(void *Base); //!< Initialises the module + int (*Unloader)(void *Base); //!< Calls module's cleanup +} PACKED tModuleLoader; + +/** + * \brief Registers a tModuleLoader with the kernel + * \param Loader Pointer to loader structure (must be persistent) + * \return Boolean Success + */ +extern int Module_RegisterLoader(tModuleLoader *Loader); + +/** + * \brief Initialises (if needed) a named module + * \param Name Module name to initialise + * \return -1 on not existing, 0 if the module initialised (or if it was already initialised) + */ +extern int Module_EnsureLoaded(const char *Name); + +#endif diff --git a/KernelLand/Kernel/include/mutex.h b/KernelLand/Kernel/include/mutex.h new file mode 100644 index 00000000..326dbd2c --- /dev/null +++ b/KernelLand/Kernel/include/mutex.h @@ -0,0 +1,48 @@ +/* + * Acess2 Kernel + * mutex.h + * - Mutual Exclusion syncronisation primitive + */ +#ifndef _MUTEX_H +#define _MUTEX_H + +#include + +typedef struct sMutex tMutex; + +struct sMutex +{ + tShortSpinlock Protector; //!< Protector for the lock strucure + const char *Name; //!< Human-readable name + struct sThread *volatile Owner; //!< Owner of the lock (set upon getting the lock) + struct sThread *Waiting; //!< Waiting threads + struct sThread *LastWaiting; //!< Waiting threads +}; + +/** + * \brief Acquire a heavy mutex + * \param Mutex Mutex to acquire + * \return zero on success, -1 if terminated + * + * This type of mutex checks if the mutex is avaliable, and acquires it + * if it is. Otherwise, the current thread is added to the mutex's wait + * queue and the thread suspends. When the holder of the mutex completes, + * the oldest thread (top thread) on the queue is given the lock and + * restarted. + */ +extern int Mutex_Acquire(tMutex *Mutex); + +/** + * \brief Release a held mutex + * \param Mutex Mutex to release + * \note Releasing a non-held mutex has no effect + */ +extern void Mutex_Release(tMutex *Mutex); + +/** + * \brief Is this mutex locked? + * \param Mutex Mutex pointer + */ +extern int Mutex_IsLocked(tMutex *Mutex); + +#endif diff --git a/KernelLand/Kernel/include/semaphore.h b/KernelLand/Kernel/include/semaphore.h new file mode 100644 index 00000000..db0b279b --- /dev/null +++ b/KernelLand/Kernel/include/semaphore.h @@ -0,0 +1,70 @@ +/* + * Acess2 Kernel + * semaphore.h + * - Semaphore syncronisation primitive + */ +#ifndef _SEMAPHORE_H +#define _SEMAPHORE_H + +#include + +/** + * \brief Semaphore typedef + */ +typedef struct sSemaphore tSemaphore; + +/** + * \brief Semaphore structure + */ +struct sSemaphore { + tShortSpinlock Protector; //!< Protector for the lock strucure + const char *ModName; //!< Human-readable module name + const char *Name; //!< Human-readable name + volatile int Value; //!< Current value + volatile int MaxValue; //!< Maximum value (signal will wait if it will go over this) + + struct sThread *Waiting; //!< Waiting threads + struct sThread *LastWaiting; //!< Waiting threads + struct sThread *Signaling; //!< Waiting threads (from Semaphore_Signal) + struct sThread *LastSignaling; //!< Last waiting thread (from Semaphore_Signal) +}; + +/** + * \brief Initialise the semaphore + * \param Sem Semaphore structure to initialsie + * \param InitValue Initial value of the semaphore + * \param MaxValue Maximum value for the semaphore + * \param Module Module name + * \param Name Symbolic name + * \note Not always needed, as initialising to 0 is valid, but it is preferred + * if all semaphores have \a Name set + * + * \note \a Module and \a Name must be avaliable for as long as the semaphore is used + */ +extern void Semaphore_Init(tSemaphore *Sem, int InitValue, int MaxValue, const char *Module, const char *Name); +/** + * \brief Acquire items from the semaphore + * \param Sem Semaphore structure to use + * \param MaxToTake Maximum number of items to take off the list (if zero, as much as possible is taken) + * \return Number of items fetched + * \retval 0 Semaphore interrupted (signal/message) + * \retval -1 Unspecified error + */ +extern int Semaphore_Wait(tSemaphore *Sem, int MaxToTake); +/** + * \brief Add an "item" to the semaphore + * \param Sem Semaphore to use + * \param AmmountToAdd Number of items to add + * \return Actual number of items added + * \retval 0 Semaphore interrupted + * \retval -1 Unspecified error + */ +extern int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd); +/** + * \brief Get the number of items waiting in a semaphore + * \param Sem Semaphore to use + * \return Number of waiting items + */ +extern int Semaphore_GetValue(tSemaphore *Sem); + +#endif diff --git a/KernelLand/Kernel/include/signal.h b/KernelLand/Kernel/include/signal.h new file mode 100644 index 00000000..d97d6dcb --- /dev/null +++ b/KernelLand/Kernel/include/signal.h @@ -0,0 +1,16 @@ +/* + * Acess2 Kernel + * Signal List + */ +#ifndef _SIGNAL_H_ +#define _SIGNAL_H_ + +enum eSignals { + SIGKILL, + SIGSTOP, + SIGCONT, + SIGCHLD, + NSIG +}; + +#endif diff --git a/KernelLand/Kernel/include/syscalls.h b/KernelLand/Kernel/include/syscalls.h new file mode 100644 index 00000000..5ce28149 --- /dev/null +++ b/KernelLand/Kernel/include/syscalls.h @@ -0,0 +1,160 @@ +/* + * Acess2 + * syscalls.h + * - System Call List + * + * NOTE: Generated from Kernel/syscalls.lst + */ +#ifndef _SYSCALLS_H +#define _SYSCALLS_H + +#define SYS_EXIT 0 // Kill this thread +#define SYS_CLONE 1 // Create a new thread +#define SYS_KILL 2 // Send a signal +#define SYS_SETFAULTHANDLER 3 // Set signal Handler +#define SYS_YIELD 4 // Yield remainder of timestamp +#define SYS_SLEEP 5 // Sleep until messaged or signaled +#define SYS_WAITEVENT 6 // Wait for an event +#define SYS_WAITTID 7 // Wait for a thread to do something +#define SYS_SETNAME 8 // Sets the name of the current thread +#define SYS_GETNAME 9 // Gets the name of a thread +#define SYS_GETTID 10 // Get current thread ID +#define SYS_GETPID 11 // Get current thread group ID +#define SYS_SETPRI 12 // Set process priority +#define SYS_SENDMSG 13 // Send an IPC message +#define SYS_GETMSG 14 // Recieve an IPC message +#define SYS_GETTIME 15 // Get the current timestamp +#define SYS_SPAWN 16 // Spawn a new process +#define SYS_EXECVE 17 // Replace the current process +#define SYS_LOADBIN 18 // Load a binary into the current address space +#define SYS_UNLOADBIN 19 // Unload a loaded binary +#define SYS_LOADMOD 20 // Load a module into the kernel +#define SYS_GETPHYS 32 // Get the physical address of a page +#define SYS_MAP 33 // Map a physical address +#define SYS_ALLOCATE 34 // Allocate a page +#define SYS_UNMAP 35 // Unmap a page +#define SYS_PREALLOC 36 // Preallocate a page +#define SYS_SETFLAGS 37 // Set a page's flags +#define SYS_SHAREWITH 38 // Share a page with another thread +#define SYS_GETUID 39 // Get current User ID +#define SYS_GETGID 40 // Get current Group ID +#define SYS_SETUID 41 // Set current user ID +#define SYS_SETGID 42 // Set current Group ID +#define SYS_OPEN 64 // Open a file +#define SYS_REOPEN 65 // Close a file and reuse its handle +#define SYS_CLOSE 66 // Close a file +#define SYS_READ 67 // Read from an open file +#define SYS_WRITE 68 // Write to an open file +#define SYS_IOCTL 69 // Perform an IOCtl Call +#define SYS_SEEK 70 // Seek to a new position in the file +#define SYS_READDIR 71 // Read from an open directory +#define SYS_OPENCHILD 72 // Open a child entry in a directory +#define SYS_GETACL 73 // Get an ACL Value +#define SYS_SETACL 74 // Set an ACL Value +#define SYS_FINFO 75 // Get file information +#define SYS_MKDIR 76 // Create a new directory +#define SYS_LINK 77 // Create a new link to a file +#define SYS_SYMLINK 78 // Create a symbolic link +#define SYS_UNLINK 79 // Delete a file +#define SYS_TELL 80 // Return the current file position +#define SYS_CHDIR 81 // Change current directory +#define SYS_GETCWD 82 // Get current directory +#define SYS_MOUNT 83 // Mount a filesystem +#define SYS_SELECT 84 // Wait for file handles + +#define NUM_SYSCALLS 85 +#define SYS_DEBUG 0x100 + +#ifndef __ASSEMBLER__ +static const char *cSYSCALL_NAMES[] = { + "SYS_EXIT", + "SYS_CLONE", + "SYS_KILL", + "SYS_SETFAULTHANDLER", + "SYS_YIELD", + "SYS_SLEEP", + "SYS_WAITEVENT", + "SYS_WAITTID", + "SYS_SETNAME", + "SYS_GETNAME", + "SYS_GETTID", + "SYS_GETPID", + "SYS_SETPRI", + "SYS_SENDMSG", + "SYS_GETMSG", + "SYS_GETTIME", + "SYS_SPAWN", + "SYS_EXECVE", + "SYS_LOADBIN", + "SYS_UNLOADBIN", + "SYS_LOADMOD", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "SYS_GETPHYS", + "SYS_MAP", + "SYS_ALLOCATE", + "SYS_UNMAP", + "SYS_PREALLOC", + "SYS_SETFLAGS", + "SYS_SHAREWITH", + "SYS_GETUID", + "SYS_GETGID", + "SYS_SETUID", + "SYS_SETGID", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "SYS_OPEN", + "SYS_REOPEN", + "SYS_CLOSE", + "SYS_READ", + "SYS_WRITE", + "SYS_IOCTL", + "SYS_SEEK", + "SYS_READDIR", + "SYS_OPENCHILD", + "SYS_GETACL", + "SYS_SETACL", + "SYS_FINFO", + "SYS_MKDIR", + "SYS_LINK", + "SYS_SYMLINK", + "SYS_UNLINK", + "SYS_TELL", + "SYS_CHDIR", + "SYS_GETCWD", + "SYS_MOUNT", + "SYS_SELECT", + + "" +}; +#endif + +#endif diff --git a/KernelLand/Kernel/include/syscalls.inc.asm b/KernelLand/Kernel/include/syscalls.inc.asm new file mode 100644 index 00000000..149fe373 --- /dev/null +++ b/KernelLand/Kernel/include/syscalls.inc.asm @@ -0,0 +1,57 @@ +; Acess2 +; System Calls List +; + +%define SYS_EXIT 0 ;Kill this thread +%define SYS_CLONE 1 ;Create a new thread +%define SYS_KILL 2 ;Send a signal +%define SYS_SETFAULTHANDLER 3 ;Set signal Handler +%define SYS_YIELD 4 ;Yield remainder of timestamp +%define SYS_SLEEP 5 ;Sleep until messaged or signaled +%define SYS_WAITEVENT 6 ;Wait for an event +%define SYS_WAITTID 7 ;Wait for a thread to do something +%define SYS_SETNAME 8 ;Sets the name of the current thread +%define SYS_GETNAME 9 ;Gets the name of a thread +%define SYS_GETTID 10 ;Get current thread ID +%define SYS_GETPID 11 ;Get current thread group ID +%define SYS_SETPRI 12 ;Set process priority +%define SYS_SENDMSG 13 ;Send an IPC message +%define SYS_GETMSG 14 ;Recieve an IPC message +%define SYS_GETTIME 15 ;Get the current timestamp +%define SYS_SPAWN 16 ;Spawn a new process +%define SYS_EXECVE 17 ;Replace the current process +%define SYS_LOADBIN 18 ;Load a binary into the current address space +%define SYS_UNLOADBIN 19 ;Unload a loaded binary +%define SYS_LOADMOD 20 ;Load a module into the kernel +%define SYS_GETPHYS 32 ;Get the physical address of a page +%define SYS_MAP 33 ;Map a physical address +%define SYS_ALLOCATE 34 ;Allocate a page +%define SYS_UNMAP 35 ;Unmap a page +%define SYS_PREALLOC 36 ;Preallocate a page +%define SYS_SETFLAGS 37 ;Set a page's flags +%define SYS_SHAREWITH 38 ;Share a page with another thread +%define SYS_GETUID 39 ;Get current User ID +%define SYS_GETGID 40 ;Get current Group ID +%define SYS_SETUID 41 ;Set current user ID +%define SYS_SETGID 42 ;Set current Group ID +%define SYS_OPEN 64 ;Open a file +%define SYS_REOPEN 65 ;Close a file and reuse its handle +%define SYS_CLOSE 66 ;Close a file +%define SYS_READ 67 ;Read from an open file +%define SYS_WRITE 68 ;Write to an open file +%define SYS_IOCTL 69 ;Perform an IOCtl Call +%define SYS_SEEK 70 ;Seek to a new position in the file +%define SYS_READDIR 71 ;Read from an open directory +%define SYS_OPENCHILD 72 ;Open a child entry in a directory +%define SYS_GETACL 73 ;Get an ACL Value +%define SYS_SETACL 74 ;Set an ACL Value +%define SYS_FINFO 75 ;Get file information +%define SYS_MKDIR 76 ;Create a new directory +%define SYS_LINK 77 ;Create a new link to a file +%define SYS_SYMLINK 78 ;Create a symbolic link +%define SYS_UNLINK 79 ;Delete a file +%define SYS_TELL 80 ;Return the current file position +%define SYS_CHDIR 81 ;Change current directory +%define SYS_GETCWD 82 ;Get current directory +%define SYS_MOUNT 83 ;Mount a filesystem +%define SYS_SELECT 84 ;Wait for file handles diff --git a/KernelLand/Kernel/include/threads.h b/KernelLand/Kernel/include/threads.h new file mode 100644 index 00000000..9ff7b629 --- /dev/null +++ b/KernelLand/Kernel/include/threads.h @@ -0,0 +1,40 @@ +/* + * Acess2 Kernel + */ +#ifndef _THREADS_H_ +#define _THREADS_H_ + +#include +#include +//#include + +enum eFaultNumbers +{ + FAULT_MISC, + FAULT_PAGE, + FAULT_ACCESS, + FAULT_DIV0, + FAULT_OPCODE, + FAULT_FLOAT +}; + +#define GETMSG_IGNORE ((void*)-1) + +typedef struct sThread tThread; + +// === FUNCTIONS === +extern void Threads_SetFaultHandler(Uint Handler); + +extern int Threads_SetUID(tUID ID); +extern int Threads_SetGID(tUID ID); +extern tTID Threads_WaitTID(int TID, int *Status); + + +extern int *Threads_GetMaxFD(void); +extern char **Threads_GetCWD(void); +extern char **Threads_GetChroot(void); + +extern int Proc_SendMessage(Uint Dest, int Length, void *Data); +extern int Proc_GetMessage(Uint *Source, void *Buffer); + +#endif diff --git a/KernelLand/Kernel/include/threads_int.h b/KernelLand/Kernel/include/threads_int.h new file mode 100644 index 00000000..66bb8c16 --- /dev/null +++ b/KernelLand/Kernel/include/threads_int.h @@ -0,0 +1,142 @@ +/* + * Internal Threading header + * - Only for use by stuff that needs access to the thread type. + */ +#ifndef _THREADS_INT_H_ +#define _THREADS_INT_H_ + +#include +#include + + +typedef struct sProcess tProcess; + +/** + * \brief IPC Message + */ +typedef struct sMessage +{ + struct sMessage *Next; //!< Next message in thread's inbox + tTID Source; //!< Source thread ID + Uint Length; //!< Length of message data in bytes + Uint8 Data[]; //!< Message data +} tMsg; + +/** + * \brief Process state + */ +struct sProcess +{ + tPID PID; + int nThreads; + + tUID UID; //!< User ID + tGID GID; //!< User and Group + tMemoryState MemState; + + int MaxFD; + char *CurrentWorkingDir; + char *RootDir; +}; + +/** + * \brief Core threading structure + * + */ +struct sThread +{ + // --- threads.c's + /** + * \brief Next thread in current list + * \note Required to be first for linked list hacks to work + */ + struct sThread *Next; + struct sThread *GlobalNext; //!< Next thread in global list + struct sThread *GlobalPrev; //!< Previous thread in global list + tShortSpinlock IsLocked; //!< Thread's spinlock + volatile int Status; //!< Thread Status + void *WaitPointer; //!< What (Mutex/Thread/other) is the thread waiting on + int RetStatus; //!< Return Status + + tTID TID; //!< Thread ID + struct sProcess *Process; //!< Thread Group / Process + struct sThread *Parent; //!< Parent Thread + char *ThreadName; //!< Name of thread + + // --- arch/proc.c's responsibility + //! Kernel Stack Base + tVAddr KernelStack; + + //! State on task switch + tTaskState SavedState; + + // --- threads.c's + int CurFaultNum; //!< Current fault number, 0: none + tVAddr FaultHandler; //!< Fault Handler + + tMsg * volatile Messages; //!< Message Queue + tMsg *LastMessage; //!< Last Message (speeds up insertion) + + int Quantum, Remaining; //!< Quantum Size and remaining timesteps + int Priority; //!< Priority - 0: Realtime, higher means less time + + int _errno; + + volatile int CurCPU; + + bool bInstrTrace; + + // --- event.c + Uint32 EventState; +}; + + +enum { + THREAD_STAT_NULL, // Invalid process + THREAD_STAT_ACTIVE, // Running and schedulable process + THREAD_STAT_SLEEPING, // Message Sleep + THREAD_STAT_MUTEXSLEEP, // Mutex Sleep + THREAD_STAT_SEMAPHORESLEEP, // Semaphore Sleep + THREAD_STAT_QUEUESLEEP, // Queue + THREAD_STAT_EVENTSLEEP, // Event sleep + THREAD_STAT_WAITING, // ??? (Waiting for a thread) + THREAD_STAT_PREINIT, // Being created + THREAD_STAT_ZOMBIE, // Died/Killed, but parent not informed + THREAD_STAT_DEAD, // Awaiting burial (free) + THREAD_STAT_BURIED // If it's still on the list here, something's wrong +}; +static const char * const casTHREAD_STAT[] = { + "THREAD_STAT_NULL", + "THREAD_STAT_ACTIVE", + "THREAD_STAT_SLEEPING", + "THREAD_STAT_MUTEXSLEEP", + "THREAD_STAT_SEMAPHORESLEEP", + "THREAD_STAT_QUEUESLEEP", + "THREAD_STAT_EVENTSLEEP", + "THREAD_STAT_WAITING", + "THREAD_STAT_PREINIT", + "THREAD_STAT_ZOMBIE", + "THREAD_STAT_DEAD", + "THREAD_STAT_BURIED" +}; + +// === GLOBALS === +extern BOOL gaThreads_NoTaskSwitch[MAX_CPUS]; +extern tShortSpinlock glThreadListLock; + +// === FUNCTIONS === +extern tThread *Proc_GetCurThread(void); + +extern tThread *Threads_GetThread(Uint TID); +extern void Threads_SetPriority(tThread *Thread, int Pri); +extern int Threads_Wake(tThread *Thread); +extern void Threads_Kill(tThread *Thread, int Status); +extern void Threads_AddActive(tThread *Thread); +extern tThread *Threads_RemActive(void); +extern void Threads_Delete(tThread *Thread); +extern tThread *Threads_GetNextToRun(int CPU, tThread *Last); + +extern tThread *Threads_CloneTCB(Uint Flags); +extern tThread *Threads_CloneThreadZero(void); + +#endif diff --git a/KernelLand/Kernel/include/timers.h b/KernelLand/Kernel/include/timers.h new file mode 100644 index 00000000..22dd5c6a --- /dev/null +++ b/KernelLand/Kernel/include/timers.h @@ -0,0 +1,39 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * timers.h + * - Kernel timers + */ +#ifndef _KERNEL_TIMERS_H_ +#define _KERNEL_TIMERS_H_ +/** + * \file timers.h + * \brief Kernel timers + */ + +typedef struct sTimer tTimer; + +/** + * \brief Timer callback function + */ +typedef void (tTimerCallback)(void *); + +/** + * \brief Creates a one-shot timer + * \param Delta Period of the timer + * \param Callback Function to call each time + * \param Argument Argument to pass to the callback + */ +extern tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument); +/** + * \brief Removed an active timer + */ +extern void Time_RemoveTimer(tTimer *Timer); +/** + * \brief Wait for a period of milliseconds + */ +extern void Time_Delay(int Delay); + +#endif + diff --git a/KernelLand/Kernel/include/tpl_mm_phys_bitmap.h b/KernelLand/Kernel/include/tpl_mm_phys_bitmap.h new file mode 100644 index 00000000..39444d94 --- /dev/null +++ b/KernelLand/Kernel/include/tpl_mm_phys_bitmap.h @@ -0,0 +1,435 @@ +/* + * Acess2 Core + * + * include/tpl_mm_phys_bitmap.h + * Physical Memory Manager Template + */ +/* + * Bitmap Edition + * + * Uses 4.125+PtrSize bytes per page + */ + +#define MM_PAGE_REFCOUNTS MM_PMM_BASE +#define MM_PAGE_NODES (MM_PMM_BASE+(MM_MAXPHYSPAGE*sizeof(Uint32))) +#define MM_PAGE_BITMAP (MM_PAGE_NODES+(MM_MAXPHYSPAGE*sizeof(void*))) + +#define PAGE_BITMAP_FREE(__pg) (gaPageBitmaps[(__pg)/32] & (1LL << ((__pg)&31))) +#define PAGE_BITMAP_SETFREE(__pg) do{gaPageBitmaps[(__pg)/32] |= (1LL << ((__pg)&31));}while(0) +#define PAGE_BITMAP_SETUSED(__pg) do{gaPageBitmaps[(__pg)/32] &= ~(1LL << ((__pg)&31));}while(0) + +// === PROTOTYPES === +//void MM_InitPhys_Multiboot(tMBoot_Info *MBoot); +//tPAddr MM_AllocPhysRange(int Num, int Bits); +//tPAddr MM_AllocPhys(void); +//void MM_RefPhys(tPAddr PAddr); +//void MM_DerefPhys(tPAddr PAddr); + int MM_int_GetRangeID( tPAddr Addr ); + int MM_int_GetMapEntry( void *Data, int Index, tPAddr *Start, tPAddr *Length ); +void MM_Tpl_InitPhys(int MaxRAMPage, void *MemoryMap); + +// === GLOBALS === +tMutex glPhysicalPages; +void **gapPageNodes = (void*)MM_PAGE_NODES; //!< Associated VFS Node for each page +Uint32 *gaiPageReferences = (void*)MM_PAGE_REFCOUNTS; // Reference Counts +Uint32 *gaPageBitmaps = (void*)MM_PAGE_BITMAP; // Used bitmap (1 == avail) +Uint64 giMaxPhysPage = 0; // Maximum Physical page + int gbPMM_Init = 0; + int giPhysFirstFree; + int giPhysLastFree; + int giPhysNumFree; + +// === CODE === +/** + * \brief Initialise the physical memory manager with a passed memory map + */ +void MM_Tpl_InitPhys(int MaxRAMPage, void *MemoryMap) +{ + int mapIndex = 0; + tPAddr rangeStart, rangeLen; + + if( MM_PAGE_BITMAP + (MM_MAXPHYSPAGE/8) > MM_PMM_END ) { + Log_KernelPanic("PMM", "Config Error, PMM cannot fit data in allocated range"); + } + + giMaxPhysPage = MaxRAMPage; + +// for( i = 0; i < MM_RANGE_MAX; i ++ ) +// gaiPhysRangeFirstFree[i] = -1; + giPhysFirstFree = -1; + + while( MM_int_GetMapEntry(MemoryMap, mapIndex++, &rangeStart, &rangeLen) ) + { + tVAddr bitmap_page; + + LOG("Range %i, %P to %P", mapIndex-1, rangeStart, rangeLen); + rangeStart /= PAGE_SIZE; + rangeLen /= PAGE_SIZE; + + giPhysNumFree += rangeLen; + + LOG("rangeStart = 0x%x, rangeLen = 0x%x", rangeStart, rangeLen); + + if( giPhysFirstFree == -1 || giPhysFirstFree > rangeStart ) + giPhysFirstFree = rangeStart; + + if( giPhysLastFree < rangeStart + rangeLen ) + giPhysLastFree = rangeStart + rangeLen; + + LOG("giPhysFirstFree = 0x%x, giPhysLastFree = 0x%x", giPhysFirstFree, giPhysLastFree); + + bitmap_page = (tVAddr)&gaPageBitmaps[rangeStart/32]; + bitmap_page &= ~(PAGE_SIZE-1); + + // Only need to allocate bitmaps + if( !MM_GetPhysAddr( bitmap_page ) ) { + if( !MM_Allocate( bitmap_page ) ) { + Log_KernelPanic("PMM", "Out of memory during init, this is bad"); + return ; + } +// memset( (void*)bitmap_page, 0, (rangeStart/8) & ~(PAGE_SIZE-1) ); + memset( (void*)bitmap_page, 0, PAGE_SIZE ); + } + + // Align to 32 pages + for( ; (rangeStart & 31) && rangeLen > 0; rangeStart++, rangeLen-- ) { + gaPageBitmaps[rangeStart / 32] |= 1 << (rangeStart&31); + LOG("gaPageBitmaps[%i] = 0x%x", rangeStart/32, gaPageBitmaps[rangeStart/32]); + } + // Mark blocks of 32 as avail + for( ; rangeLen > 31; rangeStart += 32, rangeLen -= 32 ) { + gaPageBitmaps[rangeStart / 32] = -1; + } + // Mark the tail + for( ; rangeLen > 0; rangeStart ++, rangeLen -- ) { + gaPageBitmaps[rangeStart / 32] |= 1 << (rangeStart&31); + } + } + + gbPMM_Init = 1; + + LOG("giPhysFirstFree = 0x%x, giPhysLastFree = 0x%x", giPhysFirstFree, giPhysLastFree); + LEAVE('-'); +} + +/** + * \brief Allocate a contiguous range of physical pages with a maximum + * bit size of \a MaxBits + * \param Pages Number of pages to allocate + * \param MaxBits Maximum size of the physical address + * \note If \a MaxBits is <= 0, any sized address is used (with preference + * to higher addresses) + */ +tPAddr MM_AllocPhysRange(int Pages, int MaxBits) +{ + tPAddr addr, ret; + int nFree = 0, i; + + ENTER("iPages iBits", Pages, MaxBits); + + Mutex_Acquire(&glPhysicalPages); + + // Check if there is enough in the range + if(giPhysNumFree >= Pages) + { + LOG("{0x%x -> 0x%x}", giPhysFirstFree, giPhysLastFree); + // Do a cheap scan, scanning upwards from the first free page in + // the range + nFree = 0; + addr = giPhysFirstFree; + while( addr <= giPhysLastFree ) + { + #if USE_SUPER_BITMAP + // Check the super bitmap + if( gaSuperBitmap[addr / (32*32)] == 0 ) + { + LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr); + nFree = 0; + addr += (32*32); + addr &= ~(32*32-1); // (1LL << 6+6) - 1 + continue; + } + #endif + LOG("gaPageBitmaps[%i] = 0x%x", addr/32, gaPageBitmaps[addr/32]); + // Check page block (32 pages) + if( gaPageBitmaps[addr / 32] == 0) { + LOG("nFree = %i = 0 (block) (0x%x)", nFree, addr); + nFree = 0; + addr += 32; + addr &= ~31; + continue; + } + // Check individual page + if( !(gaPageBitmaps[addr / 32] & (1LL << (addr & 31))) ) + { + LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr); + nFree = 0; + addr ++; + continue; + } + nFree ++; + addr ++; + LOG("nFree(%i) == %i (1x%x)", nFree, Pages, addr); + if(nFree == Pages) + break; + } + LOG("nFree = %i", nFree); + // If we don't find a contiguous block, nFree will not be equal + // to Num, so we set it to zero and do the expensive lookup. + if(nFree != Pages) nFree = 0; + } + + if( !nFree ) + { +#if 0 + // Oops. ok, let's do an expensive check (scan down the list + // until a free range is found) + nFree = 1; + addr = gaiPhysRangeLastFree[ rangeID ]; + // TODO +#endif + Mutex_Release(&glPhysicalPages); + // TODO: Page out + // ATM. Just Warning + Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Pages); + Log_Warning("PMM", + "Out of memory (unable to fulfil request for %i pages)", + Pages + ); + LEAVE('i', 0); + return 0; + } + LOG("nFree = %i, addr = 0x%08x", nFree, (addr-Pages) << 12); + + // Mark pages as allocated + addr -= Pages; + for( i = 0; i < Pages; i++, addr++ ) + { + // Mark as used + PAGE_BITMAP_SETUSED(addr); + // Maintain first possible free + giPhysNumFree --; + if(addr == giPhysFirstFree) + giPhysFirstFree += 1; + + LOG("if( MM_GetPhysAddr( %p ) )", &gaiPageReferences[addr]); + // Mark as referenced if the reference count page is valid + if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[addr] ) ) { + gaiPageReferences[addr] = 1; + } + } + ret = addr - Pages; // Save the return address + LOG("ret = %x", ret); + + #if TRACE_ALLOCS + LogF("MM_AllocPhysRange: %P (%i pages)\n", ret, Pages); + if(Pages > 1) { + LogF(" also"); + for(i = 1; i < Pages; i++) + LogF(" %P", ret+i); + LogF("\n"); + } + #endif + + #if USE_SUPER_BITMAP + // Update super bitmap + Pages += addr & (32-1); + addr &= ~(32-1); + Pages = (Pages + (32-1)) & ~(32-1); + for( i = 0; i < Pages/32; i++ ) + { + if( gaPageBitmaps[ addr / 32 ] + 1 == 0 ) + gaSuperBitmap[addr / (32*32)] |= 1LL << ((addr / 32) & 31); + } + #endif + + Mutex_Release(&glPhysicalPages); + LEAVE('x', ret << 12); + return ret << 12; +} + +/** + * \brief Allocate a single physical page, with no preference as to address size. + */ +tPAddr MM_AllocPhys(void) +{ + int i; + + if( !gbPMM_Init ) + { + // Hack to allow allocation during setup + for(i = 0; i < NUM_STATIC_ALLOC; i++) { + if( gaiStaticAllocPages[i] ) { + tPAddr ret = gaiStaticAllocPages[i]; + gaiStaticAllocPages[i] = 0; + Log("MM_AllocPhys: Return %x, static alloc %i", ret, i); + return ret; + } + } + + tPAddr ret = 0; + for( ret = 0; ret < giMaxPhysPage; ret ++ ) + { + if( !MM_GetPhysAddr( (tVAddr)&gaPageBitmaps[ret/32] ) ) { + ret += PAGE_SIZE*8; + continue ; + } + if( gaPageBitmaps[ret/32] == 0 ) { + ret += 32-1; + continue ; + } + if( gaPageBitmaps[ret/32] & (1 << (ret&31)) ) { + gaPageBitmaps[ret/32] &= ~(1 << (ret&31)); + return ret * PAGE_SIZE; + } + } + Log_Error("PMM", "MM_AllocPhys failed duing init"); + return 0; + } + #if TRACE_ALLOCS + Log("AllocPhys by %p", __builtin_return_address(0)); + #endif + + return MM_AllocPhysRange(1, -1); +} + +/** + * \brief Reference a physical page + */ +void MM_RefPhys(tPAddr PAddr) +{ + tPAddr page = PAddr / PAGE_SIZE; + tVAddr refpage = (tVAddr)&gaiPageReferences[page] & ~(PAGE_SIZE-1); + + if( page >= giMaxPhysPage ) return ; + + if( PAGE_BITMAP_FREE(page) ) + { + // Allocate + PAGE_BITMAP_SETUSED(page); + #if USE_SUPER_BITMAP + if( gaPageBitmaps[page / 32] == 0 ) + gaSuperBitmap[page / (32*32)] &= ~(1LL << ((page / 32) & 31)); + #endif + if( MM_GetPhysAddr( refpage ) ) + gaiPageReferences[page] = 1; + } + else + { + // Reference again + if( !MM_GetPhysAddr( refpage ) ) + { + int pages_per_page, basepage, i; + if( MM_Allocate(refpage) == 0 ) { + // Out of memory, can this be resolved? + // TODO: Reclaim memory + Log_Error("PMM", "Out of memory (MM_RefPhys)"); + return ; + } + pages_per_page = PAGE_SIZE/sizeof(*gaiPageReferences); + basepage = page & ~(pages_per_page-1); + for( i = 0; i < pages_per_page; i ++ ) { + if( PAGE_BITMAP_FREE(basepage+i) ) + gaiPageReferences[basepage+i] = 0; + else + gaiPageReferences[basepage+i] = 1; + } + gaiPageReferences[page] = 2; + } + else + gaiPageReferences[ page ] ++; + } +} + +int MM_GetRefCount(tPAddr PAddr) +{ + PAddr >>= 12; + if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[PAddr] ) ) { + return gaiPageReferences[PAddr]; + } + + if( gaPageBitmaps[ PAddr / 32 ] & (1LL << (PAddr&31)) ) { + return 1; + } + + return 0; +} + +/** + * \brief Dereference a physical page + */ +void MM_DerefPhys(tPAddr PAddr) +{ + Uint64 page = PAddr >> 12; + + if( PAddr >> 12 > giMaxPhysPage ) return ; + + ENTER("PPAddr", PAddr); + + if( MM_GetPhysAddr( (tVAddr)&gaiPageReferences[page] ) ) + { + if( gaiPageReferences[page] > 0 ) + gaiPageReferences[ page ] --; + if( gaiPageReferences[ page ] == 0 ) { + gaPageBitmaps[ page / 32 ] |= 1 << (page&31); + // TODO: Catch when all pages in this range have been dereferenced + } + } + else + gaPageBitmaps[ page / 32 ] |= 1 << (page&31); + // Clear node if needed + if( MM_GetPhysAddr( (tVAddr)&gapPageNodes[page] ) ) { + gapPageNodes[page] = NULL; + // TODO: Catch when all pages in this range are not using nodes + } + + // Update the free counts if the page was freed + if( gaPageBitmaps[ page / 32 ] & (1LL << (page&31)) ) + { + giPhysNumFree ++; + if( giPhysFirstFree == -1 || giPhysFirstFree > page ) + giPhysFirstFree = page; + if( giPhysLastFree < page ) + giPhysLastFree = page; + } + + #if USE_SUPER_BITMAP + // If the bitmap entry is not zero, set the bit free in the super bitmap + if(gaPageBitmaps[ page / 32 ] != 0 ) { + gaSuperBitmap[page / (32*32)] |= 1LL << ((page / 32) & 31); + } + #endif + LEAVE('-'); +} + +int MM_SetPageNode(tPAddr PAddr, void *Node) +{ + tPAddr page = PAddr >> 12; + tVAddr node_page = ((tVAddr)&gapPageNodes[page]) & ~(PAGE_SIZE-1); + + if( !MM_GetRefCount(PAddr) ) return 1; + + if( !MM_GetPhysAddr(node_page) ) { + if( !MM_Allocate(node_page) ) + return -1; + memset( (void*)node_page, 0, PAGE_SIZE ); + } + + gapPageNodes[page] = Node; + return 0; +} + +int MM_GetPageNode(tPAddr PAddr, void **Node) +{ + if( !MM_GetRefCount(PAddr) ) return 1; + PAddr >>= 12; + + if( !MM_GetPhysAddr( (tVAddr)&gapPageNodes[PAddr] ) ) { + *Node = NULL; + return 0; + } + + *Node = gapPageNodes[PAddr]; + return 0; +} + diff --git a/KernelLand/Kernel/include/tpl_mm_phys_stack.h b/KernelLand/Kernel/include/tpl_mm_phys_stack.h new file mode 100644 index 00000000..46ceb97d --- /dev/null +++ b/KernelLand/Kernel/include/tpl_mm_phys_stack.h @@ -0,0 +1,322 @@ +/* + * Acess2 Core + * + * include/tpl_mm_phys.h + * Physical Memory Manager Template + */ +#define DEBUG 0 + +/** + * \file tpl_mm_phys_stack.h + * \brief Template physical memory manager + * + * An extensible physical memory manager + * + * Usage: Requires NUM_MM_PHYS_RANGES to be set to the number of address + * "classes" wanted. + * MAX_PHYS_PAGES - Used to calculate structure sizes + * PADDR_TYPE + */ + +// === PROTOTYPES === +//void MM_InitPhys_Multiboot(tMBoot_Info *MBoot); +//tPAddr MM_AllocPhysRange(int Num, int Bits); +//tPAddr MM_AllocPhys(void); +//void MM_RefPhys(tPAddr PAddr); +//void MM_DerefPhys(tPAddr PAddr); + int MM_int_GetRangeID( tPAddr Addr ); + int MM_int_IsPhysUnavail( tPageNum Page ); + +// === GLOBALS === +tMutex glPhysicalPages; +//Uint64 *gaPageStacks[NUM_MM_PHYS_RANGES]; // Page stacks + int giPageStackSizes[NUM_MM_PHYS_RANGES]; // Points to the first unused slot +Uint32 *gaiPageReferences = (void*)MM_PAGE_COUNTS; // Reference Counts +Uint64 giMaxPhysPage = 0; // Maximum Physical page + +// === CODE === +/** + * \brief Initialise the physical memory map using a Multiboot 1 map + */ +void MM_Tpl_InitPhys(int MaxRAMPage) +{ + const int PAGE_SIZE = 0x1000; + const int pagesPerPageOnStack = PAGE_SIZE / sizeof(gaPageStack[0]); + int i; + tPageNum page; + +// ENTER("pMBoot=%p", MBoot); + + giMaxPhysPage = MaxRAMPage; + + tPAddr page = 0; + for( i = 0; i < NUM_MM_PHYS_RANGES; i ++ ) + { + for( ; page < giPageRangeMax[i] && page < giMaxPhysPage; page ++ ) + { + int rangeSize; + + rangeSize = MM_int_IsPhysUnavail(page); + if( rangeSize > 0 ) { + page += rangeSize; + continue; + } + // Page is avaliable for use + + // Check if the page stack is allocated + tVAddr stack_page = &gaPageStacks[i][giPageStackSizes[i]&~pagesPerPageOnStack]; + if( !MM_GetPhysAddr( stack_page ) ) { + MM_Map( stack_page, page*PAGE_SIZE ); + } + else { + gaPageStacks[i][ giPageStackSizes[i] ] = page; + giPageStackSizes[i] ++; + } + } + } + + LEAVE('-'); +} + +/** + * \brief Allocate a contiguous range of physical pages with a maximum + * bit size of \a MaxBits + * \param Pages Number of pages to allocate + * \param MaxBits Maximum size of the physical address + * \note If \a MaxBits is <= 0, any sized address is used (with preference + * to higher addresses) + */ +tPAddr MM_AllocPhysRange(int Pages, int MaxBits) +{ + tPAddr addr, ret; + int rangeID; + int nFree = 0, i; + + ENTER("iPages iBits", Pages, MaxBits); + + if( MaxBits <= 0 || MaxBits >= 64 ) // Speedup for the common case + rangeID = MM_PHYS_MAX; + else + rangeID = MM_int_GetRangeID( (1LL << MaxBits) - 1 ); + + LOG("rangeID = %i", rangeID); + + Mutex_Acquire(&glPhysicalPages); + + // Check if the range actually has any free pages + while(giPageStackSizes[rangeID] == 0 && rangeID) + rangeID --; + + LOG("rangeID = %i", rangeID); + + // What the? Oh, man. No free pages + if(giPageStackSizes[rangeID] == 0) { + Mutex_Release(&glPhysicalPages); + // TODO: Page out / attack the cache + // ATM. Just Warning + Warning(" MM_AllocPhysRange: Out of free pages"); + Log_Warning("Arch", + "Out of memory (unable to fulfil request for %i pages), zero remaining", + Pages + ); + LEAVE('i', 0); + return 0; + } + + // Check if there is enough in the range + if(giPhysRangeFree[rangeID] >= Pages) + { + LOG("{%i,0x%x -> 0x%x}", + giPhysRangeFree[rangeID], + giPhysRangeFirst[rangeID], giPhysRangeLast[rangeID] + ); + // Do a cheap scan, scanning upwards from the first free page in + // the range + nFree = 0; + addr = giPhysRangeFirst[ rangeID ]; + while( addr <= giPhysRangeLast[ rangeID ] ) + { + //Log(" MM_AllocPhysRange: addr = 0x%x", addr); + // Check the super bitmap + if( gaSuperBitmap[addr >> (6+6)] + 1 == 0 ) { + LOG("nFree = %i = 0 (super) (0x%x)", nFree, addr); + nFree = 0; + addr += 1LL << (6+6); + addr &= ~0xFFF; // (1LL << 6+6) - 1 + continue; + } + // Check page block (64 pages) + if( gaMainBitmap[addr >> 6] + 1 == 0) { + LOG("nFree = %i = 0 (main) (0x%x)", nFree, addr); + nFree = 0; + addr += 1LL << (6); + addr &= ~0x3F; + continue; + } + // Check individual page + if( gaMainBitmap[addr >> 6] & (1LL << (addr & 63)) ) { + LOG("nFree = %i = 0 (page) (0x%x)", nFree, addr); + nFree = 0; + addr ++; + continue; + } + nFree ++; + addr ++; + LOG("nFree(%i) == %i (0x%x)", nFree, Pages, addr); + if(nFree == Pages) + break; + } + LOG("nFree = %i", nFree); + // If we don't find a contiguous block, nFree will not be equal + // to Num, so we set it to zero and do the expensive lookup. + if(nFree != Pages) nFree = 0; + } + + if( !nFree ) + { + // Oops. ok, let's do an expensive check (scan down the list + // until a free range is found) + nFree = 1; + addr = giPhysRangeLast[ rangeID ]; + // TODO + Mutex_Release(&glPhysicalPages); + // TODO: Page out + // ATM. Just Warning + Warning(" MM_AllocPhysRange: Out of memory (unable to fulfil request for %i pages)", Pages); + Log_Warning("Arch", + "Out of memory (unable to fulfil request for %i pages)", + Pages + ); + LEAVE('i', 0); + return 0; + } + LOG("nFree = %i, addr = 0x%08x", nFree, addr); + + // Mark pages as allocated + addr -= Pages; + for( i = 0; i < Pages; i++, addr++ ) + { + gaMainBitmap[addr >> 6] |= 1LL << (addr & 63); + rangeID = MM_int_GetRangeID(addr << 12); + giPhysRangeFree[ rangeID ] --; + LOG("%x == %x", addr, giPhysRangeFirst[ rangeID ]); + if(addr == giPhysRangeFirst[ rangeID ]) + giPhysRangeFirst[ rangeID ] += 1; + } + ret = addr; // Save the return address + + // Update super bitmap + Pages += addr & (64-1); + addr &= ~(64-1); + Pages = (Pages + (64-1)) & ~(64-1); + for( i = 0; i < Pages/64; i++ ) + { + if( gaMainBitmap[ addr >> 6 ] + 1 == 0 ) + gaSuperBitmap[addr>>12] |= 1LL << ((addr >> 6) & 63); + } + + Mutex_Release(&glPhysicalPages); + LEAVE('x', ret << 12); + return ret << 12; +} + +/** + * \brief Allocate a single physical page, with no preference as to address + * size. + */ +tPAddr MM_AllocPhys(void) +{ + int i; + + // Hack to allow allocation during setup + for(i = 0; i < NUM_STATIC_ALLOC; i++) { + if( gaiStaticAllocPages[i] ) { + tPAddr ret = gaiStaticAllocPages[i]; + gaiStaticAllocPages[i] = 0; + Log("MM_AllocPhys: Return %x, static alloc %i", ret, i); + return ret; + } + } + + return MM_AllocPhysRange(1, -1); +} + +/** + * \brief Reference a physical page + */ +void MM_RefPhys(tPAddr PAddr) +{ + Uint64 page = PAddr >> 12; + + if( PAddr >> 12 > giMaxPhysPage ) return ; + + if( gaMainBitmap[ page >> 6 ] & (1LL << (page&63)) ) + { + // Reference again + gaMultiBitmap[ page >> 6 ] |= 1LL << (page&63); + gaiPageReferences[ page ] ++; + } + else + { + // Allocate + gaMainBitmap[page >> 6] |= 1LL << (page&63); + if( gaMainBitmap[page >> 6 ] + 1 == 0 ) + gaSuperBitmap[page>> 12] |= 1LL << ((page >> 6) & 63); + } +} + +/** + * \brief Dereference a physical page + */ +void MM_DerefPhys(tPAddr PAddr) +{ + Uint64 page = PAddr >> 12; + + if( PAddr >> 12 > giMaxPhysPage ) return ; + + if( gaMultiBitmap[ page >> 6 ] & (1LL << (page&63)) ) { + gaiPageReferences[ page ] --; + if( gaiPageReferences[ page ] == 1 ) + gaMultiBitmap[ page >> 6 ] &= ~(1LL << (page&63)); + if( gaiPageReferences[ page ] == 0 ) + gaMainBitmap[ page >> 6 ] &= ~(1LL << (page&63)); + } + else + gaMainBitmap[ page >> 6 ] &= ~(1LL << (page&63)); + + // Update the free counts if the page was freed + if( !(gaMainBitmap[ page >> 6 ] & (1LL << (page&63))) ) + { + int rangeID; + rangeID = MM_int_GetRangeID( PAddr ); + giPhysRangeFree[ rangeID ] ++; + if( giPhysRangeFirst[rangeID] > page ) + giPhysRangeFirst[rangeID] = page; + if( giPhysRangeLast[rangeID] < page ) + giPhysRangeLast[rangeID] = page; + } + + // If the bitmap entry is not -1, unset the bit in the super bitmap + if(gaMainBitmap[ page >> 6 ] + 1 != 0 ) { + gaSuperBitmap[page >> 12] &= ~(1LL << ((page >> 6) & 63)); + } +} + +/** + * \brief Takes a physical address and returns the ID of its range + * \param Addr Physical address of page + * \return Range ID from eMMPhys_Ranges + */ +int MM_int_GetRangeID( tPAddr Addr ) +{ + if(Addr >> 32) + return MM_PHYS_MAX; + else if(Addr >> 24) + return MM_PHYS_32BIT; + else if(Addr >> 20) + return MM_PHYS_24BIT; + else if(Addr >> 16) + return MM_PHYS_20BIT; + else + return MM_PHYS_16BIT; +} diff --git a/KernelLand/Kernel/include/vfs.h b/KernelLand/Kernel/include/vfs.h new file mode 100644 index 00000000..1aa073b2 --- /dev/null +++ b/KernelLand/Kernel/include/vfs.h @@ -0,0 +1,503 @@ +/* + * Acess2 + * VFS Common Header + */ +/** + * \file vfs.h + * \brief Acess VFS Layer + * + * The Acess Virtual File System (VFS) provides abstraction of multiple + * physical filesystems, network storage and devices (both hardware and + * virtual) to the user. + * + * The core of the VFS is the concept of a \ref tVFS_Node "VFS Node". + * A VFS Node represents a "file" in the VFS tree, this can be any sort + * of file (an ordinary file, a directory, a symbolic link or a device) + * depending on the bits set in the \ref tVFS_Node.Flags Flags field. + * - For more information see "VFS Node Flags" + */ +#ifndef _VFS_H +#define _VFS_H + +#include + +/** + * \brief Thread list datatype for VFS_Select + */ +typedef struct sVFS_SelectList tVFS_SelectList; + +typedef struct sVFS_NodeType tVFS_NodeType; + +/** + * \name tVFS_Node Flags + * \brief Flag values for tVFS_Node.Flags + * \{ + */ +//! \todo Is this still needed +#define VFS_FFLAG_READONLY 0x01 //!< Readonly File +/** + * \brief Directory Flag + * + * This flag marks the tVFS_Node as describing a directory, and says + * that the tVFS_Node.FindDir, tVFS_Node.ReadDir, tVFS_Node.MkNod and + * tVFS_Node.Relink function pointers are valid. + * For a directory the tVFS_Node.Size field contains the number of files + * within the directory, or -1 for undetermined. + */ +#define VFS_FFLAG_DIRECTORY 0x02 +/** + * \brief Symbolic Link Flag + * + * Marks a file as a symbolic link + */ +#define VFS_FFLAG_SYMLINK 0x04 +/** + * \brief Set User ID Flag + * + * Allows an executable file to change it's executing user to the file's + * owner. + * In the case of a directory, it means that all immediate children will + * inherit the UID of the parent. + */ +#define VFS_FFLAG_SETUID 0x08 +/** + * \brief Set Group ID Flag + * + * Allows an executable file to change it's executing group to the file's + * owning group. + * In the case of a directory, it means that all immediate children will + * inherit the GID of the parent. + */ +#define VFS_FFLAG_SETGID 0x10 + +/** + * \brief "Don't do Write-Back" Flag + * + * Stops the VFS from calling tVFS_Node.Write on dirty pages when a region + * is unmapped. Nice for read-only files and memory-only files (or + * pseudo-readonly filesystems) + */ +#define VFS_FFLAG_NOWRITEBACK +/** + * \} + */ + +/** + * \brief Represents a node (file or directory) in the VFS tree + * + * This structure provides the VFS with the functions required to read/write + * the file (or directory) that it represents. + */ +typedef struct sVFS_Node +{ + /** + * \name Identifiers + * \brief Fields used by the driver to identify what data this node + * corresponds to. + * \{ + */ + Uint64 Inode; //!< Inode ID - Must identify the node uniquely if tVFS_Driver.GetNodeFromINode is non-NULL + Uint ImplInt; //!< Implementation Usable Integer + void *ImplPtr; //!< Implementation Usable Pointer + /** + * \} + */ + + /** + * \name Node State + * \brief Stores the misc information about the node + * \{ + */ + int ReferenceCount; //!< Number of times the node is used + + Uint64 Size; //!< File Size + + Uint32 Flags; //!< File Flags + + /** + * \brief Pointer to cached data (FS Specific) + * \note The Inode_* functions will free when the node is uncached + * this if needed + */ + void *Data; + + /** + * \brief Node mutex + * \note Provided for the Filesystem driver's use + */ + tMutex Lock; + + /** + * \} + */ + + /** + * \name Times + * \{ + */ + Sint64 ATime; //!< Last Accessed Time + Sint64 MTime; //!< Last Modified Time + Sint64 CTime; //!< Creation Time + /** + * \} + */ + + /** + * \name Access control + * \{ + */ + tUID UID; //!< ID of Owning User + tGID GID; //!< ID of Owning Group + + int NumACLs; //!< Number of ACL entries + tVFS_ACL *ACLs; //!< Access Controll List pointer + /** + * \} + */ + + /** + * \name VFS_Select() fields + * \note Used by the VFS internals, drivers should use VFS_Mark* + * \{ + */ + int DataAvaliable; + tVFS_SelectList *ReadThreads; //!< Threads waiting to read + int BufferFull; + tVFS_SelectList *WriteThreads; //!< Threads waiting to write + int ErrorOccurred; + tVFS_SelectList *ErrorThreads; //!< Threads waiting for an error + /** + * \} + */ + + /** + * \name VFS_MMap() fields + * \{ + */ + void *MMapInfo; + /** + * \} + */ + + /** + * \brief Functions associated with the node + */ + tVFS_NodeType *Type; +} tVFS_Node; + +/** + * \brief Functions for a specific node type + */ +struct sVFS_NodeType +{ + /** + * \brief Debug name for the type + */ + const char *TypeName; + + /** + * \name Common Functions + * \brief Functions that are used no matter the value of .Flags + * \{ + */ + /** + * \brief Reference the node + * \param Node Pointer to this node + */ + void (*Reference)(struct sVFS_Node *Node); + /** + * \brief Close (dereference) the node + * \param Node Pointer to this node + * + * Usually .Close is used to write any changes to the node back to + * the persistent storage. + */ + void (*Close)(struct sVFS_Node *Node); + + /** + * \brief Send an IO Control + * \param Node Pointer to this node + * \param Id IOCtl call number + * \param Data Pointer to data to pass to the driver + * \return Implementation defined + */ + int (*IOCtl)(struct sVFS_Node *Node, int Id, void *Data); + + /** + * \} + */ + + /** + * \name Buffer Functions + * \brief Functions for accessing a buffer-type file (normal file or + * symbolic link) + * \{ + */ + + /** + * \brief Read from the file + * \param Node Pointer to this node + * \param Offset Byte offset in the file + * \param Length Number of bytes to read + * \param Buffer Destination for read data + * \return Number of bytes read + */ + Uint64 (*Read)(struct sVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + /** + * \brief Write to the file + * \param Node Pointer to this node + * \param Offset Byte offser in the file + * \param Length Number of bytes to write + * \param Buffer Source of written data + * \return Number of bytes read + */ + Uint64 (*Write)(struct sVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); + + /** + * \brief Map a region of a file into memory + * \param Node Pointer to this node + * \param Offset Start of the region (page aligned) + * \param Length Length of the region (page aligned) + * \param Dest Location to which to map + * \return Boolean Failure + * \note If NULL, the VFS implements it using .Read + */ + int (*MMap)(struct sVFS_Node *Node, Uint64 Offset, int Length, void *Dest); + + /** + * \} + */ + + /** + * \name Directory Functions + * \{ + */ + /** + * \brief Find an directory entry by name + * \param Node Pointer to this node + * \param Name Name of the file wanted + * \return Pointer to the requested node or NULL if it cannot be found + * \note The node returned must be accessable until ::tVFS_Node.Close + * is called and ReferenceCount reaches zero. + */ + struct sVFS_Node *(*FindDir)(struct sVFS_Node *Node, const char *Name); + + /** + * \brief Read from a directory + * \param Node Pointer to this node + * \param Pos Offset in the directory + * \return Pointer to the name of the item on the heap (will be freed + * by the caller). If the directory end has been reached, NULL + * will be returned. + * If an item is required to be skipped either &::NULLNode, + * ::VFS_SKIP or ::VFS_SKIPN(0...1023) will be returned. + */ + char *(*ReadDir)(struct sVFS_Node *Node, int Pos); + + /** + * \brief Create a node in a directory + * \param Node Pointer to this node + * \param Name Name of the new child + * \param Flags Flags to apply to the new child (directory or symlink) + * \return Zero on Success, non-zero on error (see errno.h) + */ + int (*MkNod)(struct sVFS_Node *Node, const char *Name, Uint Flags); + + /** + * \brief Relink (Rename/Remove) a file/directory + * \param Node Pointer to this node + * \param OldName Name of the item to move/delete + * \param NewName New name (or NULL if unlinking is wanted) + * \return Zero on Success, non-zero on error (see errno.h) + */ + int (*Relink)(struct sVFS_Node *Node, const char *OldName, const char *NewName); + + /** + * \brief Link a node to a name + * \param Node Pointer to this node (directory) + * \param Child Node to create a new link to + * \param NewName Name for the new link + * \retur Zeron on success, non-zero on error (see errno.h) + */ + int (*Link)(struct sVFS_Node *Node, struct sVFS_Node *Child, const char *NewName); + + /** + * \} + */ +}; + +/** + * \brief VFS Driver (Filesystem) Definition + */ +typedef struct sVFS_Driver +{ + /** + * \brief Unique Identifier for this filesystem type + */ + const char *Name; + /** + * \brief Flags applying to this driver + */ + Uint Flags; + + /** + * \brief Callback to mount a device + */ + tVFS_Node *(*InitDevice)(const char *Device, const char **Options); + /** + * \brief Callback to unmount a device + */ + void (*Unmount)(tVFS_Node *Node); + /** + * \brief Retrieve a VFS node from an inode + */ + tVFS_Node *(*GetNodeFromINode)(tVFS_Node *RootNode, Uint64 InodeValue); + /** + * \brief Used internally (next driver in the chain) + */ + struct sVFS_Driver *Next; +} tVFS_Driver; + +// === GLOBALS === +//! \brief Maximum number of elements that can be skipped in one return +#define VFS_MAXSKIP ((void*)1024) +//! \brief Skip a single entry in readdir +#define VFS_SKIP ((void*)1) +//! \brief Skip \a n entries in readdir +#define VFS_SKIPN(n) ((void*)(n)) + +extern tVFS_Node NULLNode; //!< NULL VFS Node (Ignored/Skipped) +/** + * \name Static ACLs + * \brief Simple ACLs to aid writing drivers + * \{ + */ +extern tVFS_ACL gVFS_ACL_EveryoneRWX; //!< Everyone Read/Write/Execute +extern tVFS_ACL gVFS_ACL_EveryoneRW; //!< Everyone Read/Write +extern tVFS_ACL gVFS_ACL_EveryoneRX; //!< Everyone Read/Execute +extern tVFS_ACL gVFS_ACL_EveryoneRO; //!< Everyone Read only +/** + * \} + */ + +// === FUNCTIONS === +/** + * \fn int VFS_AddDriver(tVFS_Driver *Info) + * \brief Registers the driver with the DevFS layer + * \param Info Driver information structure + */ +extern int VFS_AddDriver(tVFS_Driver *Info); +/** + * \brief Get the information structure of a driver given its name + * \param Name Name of filesystem driver to find + */ +extern tVFS_Driver *VFS_GetFSByName(const char *Name); + + +/** + * \brief Prepare a node for use + */ +extern void VFS_InitNode(tVFS_Node *Node); + +/** + * \brief Clean up a node, ready for deletion + * \note This should ALWAYS be called before a node is freed, as it + * cleans up VFS internal structures. + */ +extern void VFS_CleanupNode(tVFS_Node *Node); + +/** + * \fn tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group) + * \brief Transforms Unix Permssions into Acess ACLs + * \param Mode Unix RWXrwxRWX mask + * \param Owner UID of the file's owner + * \param Group GID of the file's owning group + * \return An array of 3 Acess ACLs + */ +extern tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group); + +/** + * \brief Flags fro \a TypeFlag of VFS_SelectNode + * \{ + */ +#define VFS_SELECT_READ 0x01 +#define VFS_SELECT_WRITE 0x02 +#define VFS_SELECT_ERROR 0x04 +/** + * \} + */ + +/** + * \brief Wait for an event on a node + * \param Node Node to wait on + * \param Type Type of wait + * \param Timeout Time to wait (NULL for infinite wait) + * \param Name Name to show in debug output + * \return Number of nodes that actioned (0 or 1) + */ +extern int VFS_SelectNode(tVFS_Node *Node, int Type, tTime *Timeout, const char *Name); + +/** + * \brief Change the full flag on a node + */ +extern int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull); +/** + * \brief Alter the space avaliable flag on a node + */ +extern int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable); +/** + * \brief Alter the error flags on a node + */ +extern int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState); + +// --- Node Cache -- +/** + * \name Node Cache + * \brief Functions to allow a node to be cached in memory by the VFS + * + * These functions store a node for the driver, to prevent it from having + * to re-generate the node on each call to FindDir. It also allows for + * fast cleanup when a filesystem is unmounted. + * \{ + */ +/** + * \fn int Inode_GetHandle(void) + * \brief Gets a unique handle to the Node Cache + * \return A unique handle for use for the rest of the Inode_* functions + */ +extern int Inode_GetHandle(void); +/** + * \fn tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode) + * \brief Gets an inode from the node cache + * \param Handle A handle returned by Inode_GetHandle() + * \param Inode Value of the Inode field of the ::tVFS_Node you want + * \return A pointer to the cached node + */ +extern tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode); +/** + * \fn tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node) + * \brief Caches a node in the Node Cache + * \param Handle A handle returned by Inode_GetHandle() + * \param Node A pointer to the node to be cached (a copy is taken) + * \return A pointer to the node in the node cache + */ +extern tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node); +/** + * \fn int Inode_UncacheNode(int Handle, Uint64 Inode) + * \brief Dereferences (and removes if needed) a node from the cache + * \param Handle A handle returned by Inode_GetHandle() + * \param Inode Value of the Inode field of the ::tVFS_Node you want to remove + */ +extern void Inode_UncacheNode(int Handle, Uint64 Inode); +/** + * \fn void Inode_ClearCache(int Handle) + * \brief Clears the cache for a handle + * \param Handle A handle returned by Inode_GetHandle() + */ +extern void Inode_ClearCache(int Handle); + +/** + * \} + */ + +#endif diff --git a/KernelLand/Kernel/include/vfs_ext.h b/KernelLand/Kernel/include/vfs_ext.h new file mode 100644 index 00000000..b4d5e2ad --- /dev/null +++ b/KernelLand/Kernel/include/vfs_ext.h @@ -0,0 +1,390 @@ +/** + * \file vfs_ext.h + * \brief Exported VFS Definitions + * \author John Hodge (thePowersGang) + */ +#ifndef _VFS_EXT_H +#define _VFS_EXT_H + +//! Inode number type +typedef Uint64 tInode; + +//! Mountpoint identifier type +typedef Uint32 tMount; + +// === CONSTANTS === +//! Maximum size of a Memory Path generated by VFS_GetMemPath +#define VFS_MEMPATH_SIZE (3 + (BITS/4)*2) +/** + * \name Flags for VFS_Open + * \{ + */ +//! Open for execution +#define VFS_OPENFLAG_EXEC 0x01 +//! Open for reading +#define VFS_OPENFLAG_READ 0x02 +//! Open for writing +#define VFS_OPENFLAG_WRITE 0x04 +//! Do not resolve the final symbolic link +#define VFS_OPENFLAG_NOLINK 0x40 +//! Create the file if it doesn't exist +#define VFS_OPENFLAG_CREATE 0x80 +//! Open as a user +#define VFS_OPENFLAG_USER 0x8000 +/** + * \} + */ +//! Marks a VFS handle as belonging to the kernel +#define VFS_KERNEL_FLAG 0x40000000 + +//! Architectual maximum number of file descriptors +#define MAX_FILE_DESCS 128 + +/** + * \brief VFS_Seek directions + */ +enum eVFS_SeekDirs +{ + SEEK_SET = 1, //!< Set the current file offset + SEEK_CUR = 0, //!< Seek relative to the current position + SEEK_END = -1 //!< Seek from the end of the file backwards +}; + +/** + * \name ACL Permissions + * \{ + */ +/** + * \brief Readable + */ +#define VFS_PERM_READ 0x00000001 +/** + * \brief Writeable + */ +#define VFS_PERM_WRITE 0x00000002 +/** + * \brief Append allowed + */ +#define VFS_PERM_APPEND 0x00000004 +/** + * \brief Executable + */ +#define VFS_PERM_EXECUTE 0x00000008 +/** + * \brief All permissions granted + */ +#define VFS_PERM_ALL 0x7FFFFFFF // Mask for permissions +/** + * \brief Denies instead of granting permissions + * \note Denials take precedence + */ +#define VFS_PERM_DENY 0x80000000 // Inverts permissions +/** + * \} + */ + +/** + * \brief MMap protection flags + * \{ + */ +#define MMAP_PROT_READ 0x001 //!< Readable memory +#define MMAP_PROT_WRITE 0x002 //!< Writable memory +#define MMAP_PROT_EXEC 0x004 //!< Executable memory +/** + * \} + */ + +/** + * \brief MMap mapping flags + * \{ + */ +#define MMAP_MAP_SHARED 0x001 //!< Shared with all other users of the FD +#define MMAP_MAP_PRIVATE 0x002 //!< Local (COW) copy +#define MMAP_MAP_FIXED 0x004 //!< Load to a fixed address +#define MMAP_MAP_ANONYMOUS 0x008 //!< Not associated with a FD +/** + * \} + */ + +// -- System Call Structures --- +/** + * \brief ACL Defintion Structure + */ +typedef struct sVFS_ACL +{ + struct { + unsigned Group: 1; //!< Group (as opposed to user) flag + unsigned ID: 31; //!< ID of Group/User (-1 for nobody/world) + }; + struct { + unsigned Inv: 1; //!< Invert Permissions + unsigned Perms: 31; //!< Permission Flags + }; +} tVFS_ACL; + +/** + * \brief SYS_FINFO structure + */ +typedef struct sFInfo +{ + tMount mount; //!< Mountpoint ID + tInode inode; //!< Inode + Uint32 uid; //!< Owning User ID + Uint32 gid; //!< Owning Group ID + Uint32 flags; //!< File flags + Uint64 size; //!< File Size + Sint64 atime; //!< Last Accessed time + Sint64 mtime; //!< Last modified time + Sint64 ctime; //!< Creation time + Sint32 numacls; //!< Total number of ACL entries + tVFS_ACL acls[]; //!< ACL buffer (size is passed in the \a MaxACLs argument to VFS_FInfo) +} PACKED tFInfo; + +/** + * \brief fd_set for select() + */ +typedef struct +{ + //! Bitmap of set file descriptors + Uint16 flags[MAX_FILE_DESCS/16]; +} fd_set; + +/** + * \brief Clear a descriptor flag in a fd_set + * \param fd File descriptor to clear + * \param fdsetp Set to modify + */ +#define FD_CLR(fd, fdsetp) ((fdsetp)->flags[(fd)/16]&=~(1<<((fd)%16))) +/** + * \brief Set a descriptor flag in a fd_set + * \param fd File descriptor to set + * \param fdsetp Set to modify + */ +#define FD_SET(fd, fdsetp) ((fdsetp)->flags[(fd)/16]|=~(1<<((fd)%16))) +/** + * \brief Test a descriptor flag in a fd_set + * \param fd File descriptor to test + * \param fdsetp Set to modify + */ +#define FD_ISSET(fd, fdsetp) ((fdsetp)->flags[(fd)/16]&(1<<((fd)%16))) + +// === FUNCTIONS === +/** + * \brief Initialise the VFS (called by system.c) + * \return Boolean Success + */ +extern int VFS_Init(void); + +/** + * \brief Open a file + * \param Path Absolute or relative path to the file + * \param Flags Flags defining how to open the file + * \return VFS Handle (an integer) or -1 if an error occured + * \note Calls VFS_OpenEx(Path, Flags, 0) + */ +extern int VFS_Open(const char *Path, Uint Flags); +/** + * \brief Open a file + * \param Path Absolute or relative path to the file + * \param Flags Flags defining how to open the file + * \param Mode Mode for newly created file (POSIX compatability) + * \return VFS Handle (an integer) or -1 if an error occured + */ +extern int VFS_OpenEx(const char *Path, Uint Flags, Uint Mode); +/** + * \brief Opens a file via an open directory + * \note The file to open must be a direct child of the parent + * \param FD Parent Directory + * \param Name Child name + * \param Mode Open mode + * \return File handle (same as returned from VFS_Open) + */ +extern int VFS_OpenChild(int FD, const char *Name, Uint Mode); +/** + * \brief Opens a file given a mount ID and an inode number + * \param Mount Mount ID returned by FInfo + * \param Inode Inode number from FInfo + * \param Mode Open mode (see VFS_Open) + * \return File handle (same as VFS_Open) + */ +extern int VFS_OpenInode(Uint32 Mount, Uint64 Inode, int Mode); + +/** + * \brief Close a currently open file + * \param FD Handle returned by ::VFS_Open + */ +extern void VFS_Close(int FD); + +/** + * \brief Get file information from an open file + * \param FD File handle returned by ::VFS_Open + * \param Dest Destination for the read information + * \param MaxACLs Number of ACL slots allocated in the \a Dest structure + * \return Boolean Success + * + * If the \a NumACLs is smaller than the number of ACLs the node has, only + * \a NumACLs will be copied into \a Dest, but the tFInfo.numacls field + * will be set to the true ammount of ACLs. It is up to the user to do with + * this information how they like. + */ +extern int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs); +/** + * \brief Gets the permissions appling to a user/group. + * \param FD File handle returned by ::VFS_Open + * \param Dest ACL information structure to edit + * \return Boolean success + * + * This function sets the tVFS_ACL.Inv and tVFS_ACL.Perms fields to what + * permissions the user/group specied in tVFS_ACL.ID has on the file. + */ +extern int VFS_GetACL(int FD, tVFS_ACL *Dest); +/** + * \brief Changes the user's current working directory + * \param Dest New working directory (either absolute or relative to the current) + * \return Boolean Success + */ +extern int VFS_ChDir(const char *Dest); +/** + * \brief Change the current virtual root for the user + * \param New New virtual root (same as ::VFS_ChDir but cannot go + * above the current virtual root) + * \return Boolean success + */ +extern int VFS_ChRoot(const char *New); + +/** + * \brief Change the location of the current file pointer + * \param FD File handle returned by ::VFS_Open + * \param Offset Offset within the file to go to + * \param Whence A direction from ::eVFS_SeekDirs + * \return Boolean success + */ +extern int VFS_Seek(int FD, Sint64 Offset, int Whence); +/** + * \brief Returns the current file pointer + * \param FD File handle returned by ::VFS_Open + * \return Current file position + */ +extern Uint64 VFS_Tell(int FD); + +/** + * \brief Reads data from a file + * \param FD File handle returned by ::VFS_Open + * \param Length Number of bytes to read from the file + * \param Buffer Destination of read data + * \return Number of read bytes + */ +extern Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer); +/** + * \brief Writes data to a file + * \param FD File handle returned by ::VFS_Open + * \param Length Number of bytes to write to the file + * \param Buffer Source of written data + * \return Number of bytes written + */ +extern Uint64 VFS_Write(int FD, Uint64 Length, const void *Buffer); + +/** + * \brief Reads from a specific offset in the file + * \param FD File handle returned by ::VFS_Open + * \param Offset Byte offset in the file + * \param Length Number of bytes to read from the file + * \param Buffer Source of read data + * \return Number of bytes read + */ +extern Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer); +/** + * \brief Writes to a specific offset in the file + * \param FD File handle returned by ::VFS_Open + * \param Offset Byte offset in the file + * \param Length Number of bytes to write to the file + * \param Buffer Source of written data + * \return Number of bytes written + */ +extern Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer); + +/** + * \brief Sends an IOCtl request to the driver + * \param FD File handle returned by ::VFS_Open + * \param ID IOCtl call ID (driver specific) + * \param Buffer Data pointer to send to the driver + * \return Driver specific response + */ +extern int VFS_IOCtl(int FD, int ID, void *Buffer); + +/** + * \brief Creates a VFS Memory path from a pointer and size + * \param Dest Destination for the created path + * \param Base Base of the memory file + * \param Length Length of the memory buffer + * \note A maximum of VFS_MEMPATH_SIZE bytes will be required in \a Dest + */ +extern void VFS_GetMemPath(char *Dest, void *Base, Uint Length); +/** + * \brief Gets the canoical (true) path of a file + * \param Path File path (may contain symlinks, relative elements etc.) + * \return Absolute path with no symlinks + */ +extern char *VFS_GetTruePath(const char *Path); + +/** + * \brief Mounts a filesystem + * \param Device Device to mount + * \param MountPoint Location to mount + * \param Filesystem Filesystem to use + * \param Options Options string to pass the driver + * \return 1 on succes, -1 on error + */ +extern int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options); +/** + * \brief Create a new directory + * \param Path Path to new directory (absolute or relative) + * \return Boolean success + * \note The parent of the directory must exist + */ +extern int VFS_MkDir(const char *Path); +/** + * \brief Create a symbolic link + * \param Name Name of the symbolic link + * \param Link File the symlink points to + * \return Boolean success (\a Link is not tested for existence) + */ +extern int VFS_Symlink(const char *Name, const char *Link); +/** + * \brief Read from a directory + * \param FD File handle returned by ::VFS_Open + * \param Dest Destination array for the file name (max 255 bytes) + * \return Boolean Success + */ +extern int VFS_ReadDir(int FD, char *Dest); +/** + * \brief Wait for an aciton on a file descriptor + * \param MaxHandle Maximum set handle in \a *Handles + * \param ReadHandles Handles to wait for data on + * \param WriteHandles Handles to wait to write to + * \param ErrHandles Handle to wait for errors on + * \param Timeout Timeout for select() (if null, there is no timeout), if zero select() is non blocking + * \param ExtraEvents Extra event set to wait on + * \param IsKernel Use kernel handles as opposed to user handles + * \return Number of handles that actioned + */ +extern int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel); + +/** + * \brief Map a file into memory + * \param DestHint Suggested place for read data + * \param Length Size of area to map + * \param Protection Protection type (see `man mmap`) + * \param Flags Mapping flags + * \param FD File descriptor to load from + * \param Offset Start of region + */ +extern void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset); + +/** + * \brief Unmap memory allocated by VFS_MMap + * \param Addr Address of data to unmap + * \param Length Length of data + */ +extern int VFS_MUnmap(void *Addr, size_t Length); +#endif diff --git a/KernelLand/Kernel/include/vfs_int.h b/KernelLand/Kernel/include/vfs_int.h new file mode 100644 index 00000000..a8eadb6d --- /dev/null +++ b/KernelLand/Kernel/include/vfs_int.h @@ -0,0 +1,68 @@ +/* + * Acess Micro - VFS Server Ver 1 + */ +#ifndef _VFS_INT_H +#define _VFS_INT_H + +#include "vfs.h" + +// === TYPES === +typedef struct sVFS_Mount { + struct sVFS_Mount *Next; + char *MountPoint; + size_t MountPointLen; + Uint32 Identifier; + char *Device; + char *Options; + tVFS_Driver *Filesystem; + tVFS_Node *RootNode; + char StrData[]; +} tVFS_Mount; + +typedef struct sVFS_Handle { + tVFS_Node *Node; + tVFS_Mount *Mount; + Uint64 Position; + Uint Mode; +} tVFS_Handle; + +typedef struct sVFS_Proc { + struct sVFS_Proc *Next; + int ID; + int CwdLen; + Uint UID, GID; + char *Cwd; + int MaxHandles; + tVFS_Handle Handles[]; +} tVFS_Proc; + +typedef struct sVFS_MMapPage { + Uint64 FileOffset; + tPAddr PAddr; +} tVFS_MMapPage; + +// === GLOBALS === +extern tVFS_Mount *gVFS_Mounts; + +// === PROTOTYPES === +// --- open.c --- +extern char *VFS_GetAbsPath(const char *Path); +extern tVFS_Node *VFS_ParsePath(const char *Path, char **TruePath, tVFS_Mount **MountPoint); +extern tVFS_Handle *VFS_GetHandle(int FD); +// --- acls.c --- +extern int VFS_CheckACL(tVFS_Node *Node, Uint Permissions); +// --- mount.c --- +extern tVFS_Mount *VFS_GetMountByIdent(Uint32 MountID); +// --- dir.c --- +extern int VFS_MkNod(const char *Path, Uint Flags); + + +// --- VFS Helpers --- +static inline void _CloseNode(tVFS_Node *Node) +{ + if(Node && Node->Type && Node->Type->Close) + Node->Type->Close( Node ); +} + + +#endif diff --git a/KernelLand/Kernel/include/vfs_ramfs.h b/KernelLand/Kernel/include/vfs_ramfs.h new file mode 100644 index 00000000..8bbc8b58 --- /dev/null +++ b/KernelLand/Kernel/include/vfs_ramfs.h @@ -0,0 +1,20 @@ +/* + * AcessMicro VFS + * - RAM Filesystem Coommon Structures + */ +#ifndef _RAMFS_H +#define _RAMFS_H +#include + +typedef struct sRamFS_File { + struct sRamFS_File *Next; + struct sRamFS_File *Parent; + char Name[32]; + tVFS_Node Node; + union { + struct sRamFS_File *FirstChild; + char *Bytes; + } Data; +} tRamFS_File; + +#endif diff --git a/KernelLand/Kernel/include/vfs_threads.h b/KernelLand/Kernel/include/vfs_threads.h new file mode 100644 index 00000000..95ec2278 --- /dev/null +++ b/KernelLand/Kernel/include/vfs_threads.h @@ -0,0 +1,19 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * include/vfs_threads.h + * - Handle maintainance functions for the VFS used by threading code + */ +#ifndef _VFS_THREADS_H_ +#define _VFS_THREADS_H_ + +// === FUNCTIONS === +extern void VFS_ReferenceUserHandles(void); +extern void VFS_CloseAllUserHandles(void); + +extern void *VFS_SaveHandles(int NumFDs, int *FDs); +extern void VFS_RestoreHandles(int NumFDs, void *Handles); +extern void VFS_FreeSavedHandles(int NumFDs, void *Handles); + +#endif diff --git a/KernelLand/Kernel/include/workqueue.h b/KernelLand/Kernel/include/workqueue.h new file mode 100644 index 00000000..ff0c7f4c --- /dev/null +++ b/KernelLand/Kernel/include/workqueue.h @@ -0,0 +1,31 @@ +/** + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * workqueue.h + * - FIFO Queue + */ +#ifndef _WORKQUEUE_H_ +#define _WORKQUEUE_H_ + +#include + +typedef struct sWorkqueue tWorkqueue; + +struct sWorkqueue +{ + tShortSpinlock Protector; + const char *Name; + int NextOffset; + + void *Head; + void *Tail; + struct sThread *Sleeper; +}; + +extern void Workqueue_Init(tWorkqueue *Queue, const char *Name, size_t NextOfset); +extern void *Workqueue_GetWork(tWorkqueue *Queue); +extern void Workqueue_AddWork(tWorkqueue *Queue, void *Ptr); + +#endif + diff --git a/KernelLand/Kernel/lib.c b/KernelLand/Kernel/lib.c new file mode 100644 index 00000000..60cbd4c7 --- /dev/null +++ b/KernelLand/Kernel/lib.c @@ -0,0 +1,1084 @@ +/* + * Acess2 + * Common Library Functions + */ +#include +#include + +// === CONSTANTS === +#define RANDOM_SEED 0xACE55052 +#define RANDOM_A 0x00731ADE +#define RANDOM_C 12345 +#define RANDOM_SPRUCE 0xf12b039 +// Jan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec +const short DAYS_BEFORE[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; +#define UNIX_TO_2K ((30*365*3600*24) + (7*3600*24)) //Normal years + leap years + +// === PROTOTYPES === +#if 0 + int atoi(const char *string); +void itoa(char *buf, Uint64 num, int base, int minLength, char pad); + int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args); + int sprintf(char *__s, const char *__format, ...); +#endif + int tolower(int c); +#if 0 + int strucmp(const char *Str1, const char *Str2); +char *strchr(const char *__s, int __c); + int strpos(const char *Str, char Ch); + Uint8 ByteSum(void *Ptr, int Size); +size_t strlen(const char *__s); +char *strcpy(char *__str1, const char *__str2); +char *strncpy(char *__str1, const char *__str2, size_t max); + int strcmp(const char *str1, const char *str2); + int strncmp(const char *str1, const char *str2, size_t num); +char *_strdup(const char *File, int Line, const char *Str); +char **str_split(const char *__str, char __ch); + int strpos8(const char *str, Uint32 Search); + int ReadUTF8(Uint8 *str, Uint32 *Val); + int WriteUTF8(Uint8 *str, Uint32 Val); + int DivUp(int num, int dem); +Sint64 timestamp(int sec, int mins, int hrs, int day, int month, int year); +void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms); + int rand(void); + + int CheckString(char *String); + int CheckMem(void *Mem, int NumBytes); + + int ModUtil_LookupString(char **Array, char *Needle); + int ModUtil_SetIdent(char *Dest, char *Value); + + int Hex(char *Dest, size_t Size, const Uint8 *SourceData); + int UnHex(Uint8 *Dest, size_t DestSize, const char *SourceString); +#endif + +// === EXPORTS === +EXPORT(atoi); +EXPORT(itoa); +EXPORT(vsnprintf); +EXPORT(sprintf); +EXPORT(tolower); +EXPORT(strucmp); +EXPORT(strchr); +EXPORT(strpos); +EXPORT(ByteSum); +EXPORT(strlen); +EXPORT(strcpy); +EXPORT(strncpy); +EXPORT(strcat); +EXPORT(strncat); +EXPORT(strcmp); +EXPORT(strncmp); +//EXPORT(strdup); +EXPORT(_strdup); // Takes File/Line too +EXPORT(str_split); +EXPORT(strpos8); +EXPORT(DivUp); +EXPORT(ReadUTF8); +EXPORT(WriteUTF8); +EXPORT(timestamp); +EXPORT(CheckString); +EXPORT(CheckMem); +EXPORT(ModUtil_LookupString); +EXPORT(ModUtil_SetIdent); +EXPORT(UnHex); +EXPORT(SwapEndian16); +EXPORT(SwapEndian32); +EXPORT(memmove); + +// === CODE === +/** + * \brief Convert a string into an integer + */ +int atoi(const char *string) +{ + int ret = 0; + ParseInt(string, &ret); + return ret; +} +int ParseInt(const char *string, int *Val) +{ + int ret = 0; + int bNeg = 0; + const char *orig_string = string; + + //Log("atoi: (string='%s')", string); + + // Clear non-numeric characters + while( !('0' <= *string && *string <= '9') && *string != '-' ) string++; + if( *string == '-' ) { + bNeg = 1; + while( !('0' <= *string && *string <= '9') ) string++; + } + + if(*string == '0') + { + string ++; + if(*string == 'x') + { + // Hex + string ++; + for( ;; string ++ ) + { + if('0' <= *string && *string <= '9') { + ret *= 16; + ret += *string - '0'; + } + else if('A' <= *string && *string <= 'F') { + ret *= 16; + ret += *string - 'A' + 10; + } + else if('a' <= *string && *string <= 'f') { + ret *= 16; + ret += *string - 'a' + 10; + } + else + break; + } + } + else // Octal + { + for( ; '0' <= *string && *string <= '7'; string ++ ) + { + ret *= 8; + ret += *string - '0'; + } + } + } + else // Decimal + { + for( ; '0' <= *string && *string <= '9'; string++) + { + ret *= 10; + ret += *string - '0'; + } + // Error check + if( ret == 0 ) return 0; + } + + if(bNeg) ret = -ret; + + //Log("atoi: RETURN %i", ret); + + if(Val) *Val = ret; + + return string - orig_string; +} + +static const char cUCDIGITS[] = "0123456789ABCDEF"; +/** + * \fn void itoa(char *buf, Uint64 num, int base, int minLength, char pad) + * \brief Convert an integer into a character string + */ +void itoa(char *buf, Uint64 num, int base, int minLength, char pad) +{ + char tmpBuf[64+1]; + int pos=0, i; + Uint64 rem; + + // Sanity check + if(!buf) return; + + // Sanity Check + if(base > 16 || base < 2) { + buf[0] = 0; + return; + } + + // Convert + while(num > base-1) { + num = DivMod64U(num, base, &rem); // Shift `num` and get remainder + tmpBuf[pos] = cUCDIGITS[ rem ]; + pos++; + } + tmpBuf[pos++] = cUCDIGITS[ num ]; // Last digit of `num` + + // Put in reverse + i = 0; + minLength -= pos; + while(minLength-- > 0) buf[i++] = pad; + while(pos-- > 0) buf[i++] = tmpBuf[pos]; // Reverse the order of characters + buf[i] = 0; +} + +/** + * \brief Append a character the the vsnprintf output + */ +#define PUTCH(c) _putch(c) +#define GETVAL() do {\ + if(isLongLong) val = va_arg(args, Uint64);\ + else val = va_arg(args, unsigned int);\ + }while(0) +/** + * \brief VArg String Number Print Formatted + */ +int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args) +{ + char c, pad = ' '; + int minSize = 0, precision = -1, len; + char tmpBuf[34]; // For Integers + const char *p = NULL; + int isLongLong = 0; + Uint64 val; + size_t pos = 0; + // Flags + int bPadLeft = 0; + + auto void _putch(char ch); + + void _putch(char ch) + { + if(pos < __maxlen) + { + if(__s) __s[pos] = ch; + pos ++; + } + } + + while((c = *__format++) != 0) + { + // Non control character + if(c != '%') { PUTCH(c); continue; } + + c = *__format++; + if(c == '\0') break; + + // Literal % + if(c == '%') { PUTCH('%'); continue; } + + // Pointer - Done first for debugging + if(c == 'p') { + Uint ptr = va_arg(args, Uint); + PUTCH('*'); PUTCH('0'); PUTCH('x'); + for( len = BITS/4; len --; ) + PUTCH( cUCDIGITS[ (ptr>>(len*4))&15 ] ); + continue ; + } + + // - Padding Side Flag + if(c == '-') { + bPadLeft = 1; + c = *__format++; + } + + // - Padding + if(c == '0') { + pad = '0'; + c = *__format++; + } + else + pad = ' '; + + // - Minimum length + if(c == '*') { // Dynamic length + minSize = va_arg(args, unsigned int); + c = *__format++; + } + else if('1' <= c && c <= '9') + { + minSize = 0; + while('0' <= c && c <= '9') + { + minSize *= 10; + minSize += c - '0'; + c = *__format++; + } + } + else + minSize = 0; + + // - Precision + precision = -1; + if( c == '.' ) { + c = *__format++; + + if(c == '*') { // Dynamic length + precision = va_arg(args, unsigned int); + c = *__format++; + } + else if('1' <= c && c <= '9') + { + precision = 0; + while('0' <= c && c <= '9') + { + precision *= 10; + precision += c - '0'; + c = *__format++; + } + } + } + + // - Default, Long or LongLong? + isLongLong = 0; + if(c == 'l') // Long is actually the default on x86 + { + c = *__format++; + if(c == 'l') { + c = *__format++; + isLongLong = 1; + } + } + + // - Now get the format code + p = tmpBuf; + switch(c) + { + case 'd': + case 'i': + GETVAL(); + if( isLongLong && val >> 63 ) { + PUTCH('-'); + val = -val; + } + else if( !isLongLong && val >> 31 ) { + PUTCH('-'); + val = -(Sint32)val; + } + itoa(tmpBuf, val, 10, minSize, pad); + goto printString; + case 'u': // Unsigned + GETVAL(); + itoa(tmpBuf, val, 10, minSize, pad); + goto printString; + case 'P': // Physical Address + PUTCH('0'); + PUTCH('x'); + if(sizeof(tPAddr) > 4) isLongLong = 1; + GETVAL(); + itoa(tmpBuf, val, 16, minSize, pad); + goto printString; + case 'X': // Hex + if(BITS == 64) + isLongLong = 1; // TODO: Handle non-x86 64-bit archs + GETVAL(); + itoa(tmpBuf, val, 16, minSize, pad); + goto printString; + + case 'x': // Lower case hex + GETVAL(); + itoa(tmpBuf, val, 16, minSize, pad); + goto printString; + case 'o': // Octal + GETVAL(); + itoa(tmpBuf, val, 8, minSize, pad); + goto printString; + case 'b': + GETVAL(); + itoa(tmpBuf, val, 2, minSize, pad); + goto printString; + + case 'B': //Boolean + val = va_arg(args, unsigned int); + if(val) p = "True"; + else p = "False"; + goto printString; + + // String - Null Terminated Array + case 's': + p = va_arg(args, char*); // Get Argument + if( !p || !CheckString(p) ) p = "(inval)"; // Avoid #PFs + printString: + if(!p) p = "(null)"; + len = strlen(p); + if( !bPadLeft ) while(len++ < minSize) PUTCH(pad); + while(*p && precision--) PUTCH(*p++); + if( bPadLeft ) while(len++ < minSize) PUTCH(pad); + break; + + case 'C': // Non-Null Terminated Character Array + p = va_arg(args, char*); + if( !CheckMem(p, minSize) ) continue; // No #PFs please + if(!p) goto printString; + while(minSize--) PUTCH(*p++); + break; + + // Single Character + case 'c': + default: + GETVAL(); + PUTCH( (Uint8)val ); + break; + } + } + + if(__s && pos != __maxlen) + __s[pos] = '\0'; + + return pos; +} +#undef PUTCH + +/** + */ +int sprintf(char *__s, const char *__format, ...) +{ + va_list args; + int ret; + + va_start(args, __format); + ret = vsnprintf(__s, -1, __format, args); + va_end(args); + + return ret; +} + +/** + * \fn int tolower(int c) + * \brief Converts a character to lower case + */ +int tolower(int c) +{ + if('A' <= c && c <= 'Z') + return c - 'A' + 'a'; + return c; +} + +/** + * \fn int strucmp(const char *Str1, const char *Str2) + * \brief Compare \a Str1 and \a Str2 case-insensitively + */ +int strucmp(const char *Str1, const char *Str2) +{ + while(*Str1 && tolower(*Str1) == tolower(*Str2)) + Str1++, Str2++; + return tolower(*Str1) - tolower(*Str2); +} + +/** + * \brief Locate a byte in a string + */ +char *strchr(const char *__s, int __c) +{ + for( ; *__s; __s ++ ) + { + if( *__s == __c ) return (char*)__s; + } + return NULL; +} + +/** + * \fn int strpos(const char *Str, char Ch) + * \brief Search a string for an ascii character + */ +int strpos(const char *Str, char Ch) +{ + int pos; + for(pos=0;Str[pos];pos++) + { + if(Str[pos] == Ch) return pos; + } + return -1; +} + +/** + * \fn Uint8 ByteSum(void *Ptr, int Size) + * \brief Adds the bytes in a memory region and returns the sum + */ +Uint8 ByteSum(const void *Ptr, int Size) +{ + Uint8 sum = 0; + const Uint8 *data = Ptr; + while(Size--) sum += *(data++); + return sum; +} + +/** + * \fn size_t strlen(const char *__str) + * \brief Get the length of string + */ +size_t strlen(const char *__str) +{ + size_t ret = 0; + while(*__str++) ret++; + return ret; +} + +/** + * \brief Copy a string to a new location + */ +char *strcpy(char *__str1, const char *__str2) +{ + while(*__str2) + *__str1++ = *__str2++; + *__str1 = '\0'; // Terminate String + return __str1; +} + +/** + * \brief Copy a string to a new location + * \note Copies at most `max` chars + */ +char *strncpy(char *__str1, const char *__str2, size_t __max) +{ + while(*__str2 && __max-- >= 1) + *__str1++ = *__str2++; + if(__max) + *__str1 = '\0'; // Terminate String + return __str1; +} + +/** + * \brief Append a string to another + */ +char *strcat(char *__dest, const char *__src) +{ + while(*__dest++); + __dest--; + while(*__src) + *__dest++ = *__src++; + *__dest = '\0'; + return __dest; +} + +/** + * \brief Append at most \a n chars to a string from another + * \note At most n+1 chars are written (the dest is always zero terminated) + */ +char *strncat(char *__dest, const char *__src, size_t n) +{ + while(*__dest++); + while(*__src && n-- >= 1) + *__dest++ = *__src++; + *__dest = '\0'; + return __dest; +} + +/** + * \fn int strcmp(const char *str1, const char *str2) + * \brief Compare two strings return the difference between + * the first non-matching characters. + */ +int strcmp(const char *str1, const char *str2) +{ + while(*str1 && *str1 == *str2) + str1++, str2++; + return *str1 - *str2; +} + +/** + * \fn int strncmp(const char *Str1, const char *Str2, size_t num) + * \brief Compare strings \a Str1 and \a Str2 to a maximum of \a num characters + */ +int strncmp(const char *Str1, const char *Str2, size_t num) +{ + if(num == 0) return 0; // TODO: Check what should officially happen here + while(--num && *Str1 && *Str1 == *Str2) + Str1++, Str2++; + return *Str1-*Str2; +} + +#if 0 +/** + * \fn char *strdup(const char *Str) + * \brief Duplicates a string + */ +char *strdup(const char *Str) +{ + char *ret; + ret = malloc(strlen(Str)+1); + if( !ret ) return NULL; + strcpy(ret, Str); + return ret; +} +#else + +/** + * \fn char *_strdup(const char *File, int Line, const char *Str) + * \brief Duplicates a string + */ +char *_strdup(const char *File, int Line, const char *Str) +{ + char *ret; + ret = Heap_Allocate(File, Line, strlen(Str)+1); + if( !ret ) return NULL; + strcpy(ret, Str); + return ret; +} +#endif + +/** + * \brief Split a string using the passed character + * \return NULL terminated array of strings on the heap + * \param __str String to split + * \param __ch Character to split by + */ +char **str_split(const char *__str, char __ch) +{ + int i, j; + int len = 1; + char **ret; + char *start; + + for( i = 0; __str[i]; i++ ) + { + if(__str[i] == __ch) + len ++; + } + + ret = malloc( sizeof(char*)*(len+1) + (i + 1) ); + if( !ret ) return NULL; + + j = 1; + start = (char *)&ret[len+1]; + ret[0] = start; + for( i = 0; __str[i]; i++ ) + { + if(__str[i] == __ch) { + *start++ = '\0'; + ret[j++] = start; + } + else { + *start++ = __str[i]; + } + } + *start = '\0'; + ret[j] = NULL; + + return ret; +} + +/** + * \fn int DivUp(int num, int dem) + * \brief Divide two numbers, rounding up + * \param num Numerator + * \param dem Denominator + */ +int DivUp(int num, int dem) +{ + return (num+dem-1)/dem; +} + +/** + * \fn int strpos8(const char *str, Uint32 search) + * \brief Search a string for a UTF-8 character + */ +int strpos8(const char *str, Uint32 Search) +{ + int pos; + Uint32 val = 0; + for(pos=0;str[pos];pos++) + { + // ASCII Range + if(Search < 128) { + if(str[pos] == Search) return pos; + continue; + } + if(*(Uint8*)(str+pos) < 128) continue; + + pos += ReadUTF8( (Uint8*)&str[pos], &val ); + if(val == Search) return pos; + } + return -1; +} + +/** + * \fn int ReadUTF8(Uint8 *str, Uint32 *Val) + * \brief Read a UTF-8 character from a string + */ +int ReadUTF8(const Uint8 *str, Uint32 *Val) +{ + *Val = 0xFFFD; // Assume invalid character + + // ASCII + if( !(*str & 0x80) ) { + *Val = *str; + return 1; + } + + // Middle of a sequence + if( (*str & 0xC0) == 0x80 ) { + return 1; + } + + // Two Byte + if( (*str & 0xE0) == 0xC0 ) { + *Val = (*str & 0x1F) << 6; // Upper 6 Bits + str ++; + if( (*str & 0xC0) != 0x80) return -1; // Validity check + *Val |= (*str & 0x3F); // Lower 6 Bits + return 2; + } + + // Three Byte + if( (*str & 0xF0) == 0xE0 ) { + *Val = (*str & 0x0F) << 12; // Upper 4 Bits + str ++; + if( (*str & 0xC0) != 0x80) return -1; // Validity check + *Val |= (*str & 0x3F) << 6; // Middle 6 Bits + str ++; + if( (*str & 0xC0) != 0x80) return -1; // Validity check + *Val |= (*str & 0x3F); // Lower 6 Bits + return 3; + } + + // Four Byte + if( (*str & 0xF1) == 0xF0 ) { + *Val = (*str & 0x07) << 18; // Upper 3 Bits + str ++; + if( (*str & 0xC0) != 0x80) return -1; // Validity check + *Val |= (*str & 0x3F) << 12; // Middle-upper 6 Bits + str ++; + if( (*str & 0xC0) != 0x80) return -1; // Validity check + *Val |= (*str & 0x3F) << 6; // Middle-lower 6 Bits + str ++; + if( (*str & 0xC0) != 0x80) return -1; // Validity check + *Val |= (*str & 0x3F); // Lower 6 Bits + return 4; + } + + // UTF-8 Doesn't support more than four bytes + return 4; +} + +/** + * \fn int WriteUTF8(Uint8 *str, Uint32 Val) + * \brief Write a UTF-8 character sequence to a string + */ +int WriteUTF8(Uint8 *str, Uint32 Val) +{ + // ASCII + if( Val < 128 ) { + if( str ) { + *str = Val; + } + return 1; + } + + // Two Byte + if( Val < 0x8000 ) { + if( str ) { + *str++ = 0xC0 | (Val >> 6); + *str++ = 0x80 | (Val & 0x3F); + } + return 2; + } + + // Three Byte + if( Val < 0x10000 ) { + if( str ) { + *str++ = 0xE0 | (Val >> 12); + *str++ = 0x80 | ((Val >> 6) & 0x3F); + *str++ = 0x80 | (Val & 0x3F); + } + return 3; + } + + // Four Byte + if( Val < 0x110000 ) { + if( str ) { + *str++ = 0xF0 | (Val >> 18); + *str++ = 0x80 | ((Val >> 12) & 0x3F); + *str++ = 0x80 | ((Val >> 6) & 0x3F); + *str++ = 0x80 | (Val & 0x3F); + } + return 4; + } + + // UTF-8 Doesn't support more than four bytes + return 0; +} + +/** + * \fn Uint64 timestamp(int sec, int mins, int hrs, int day, int month, int year) + * \brief Converts a date into an Acess Timestamp + */ +Sint64 timestamp(int sec, int min, int hrs, int day, int month, int year) +{ + int is_leap; + Sint64 stamp; + + if( !(0 <= sec && sec < 60) ) return 0; + if( !(0 <= min && min < 60) ) return 0; + if( !(0 <= hrs && hrs < 24) ) return 0; + if( !(0 <= day && day < 31) ) return 0; + if( !(0 <= month && month < 12) ) return 0; + + stamp = DAYS_BEFORE[month] + day; + + // Every 4 years + // - every 100 years + // + every 400 years + is_leap = (year % 4 == 0) - (year % 100 == 0) + (year % 400 == 0); + ASSERT(is_leap == 0 || is_leap == 1); + + if( is_leap && month > 1 ) // Leap year and after feb + stamp += 1; + + // Get seconds before the first of specified year + year -= 2000; // Base off Y2K + // base year days + total leap year days + stamp += year*365 + (year/400) - (year/100) + (year/4); + + stamp *= 3600*24; + stamp += UNIX_TO_2K; + stamp += sec; + stamp += min*60; + stamp += hrs*3600; + + return stamp * 1000; +} + +void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms) +{ + int is_leap = 0, i; + + auto Sint64 DivMod64(Sint64 N, Sint64 D, Sint64 *R); + + Sint64 DivMod64(Sint64 N, Sint64 D, Sint64 *R) + { + int sign = (N < 0) != (D < 0); + if(N < 0) N = -N; + if(D < 0) D = -D; + if(sign) + return -DivMod64U(N, D, (Uint64*)R); + else + return DivMod64U(N, D, (Uint64*)R); + } + + // Get time + // TODO: Leap-seconds? + { + Sint64 rem; + TS = DivMod64( TS, 1000, &rem ); + *ms = rem; + TS = DivMod64( TS, 60, &rem ); + *sec = rem; + TS = DivMod64( TS, 60, &rem ); + *mins = rem; + TS = DivMod64( TS, 24, &rem ); + *hrs = rem; + } + + // Adjust to Y2K + TS -= UNIX_TO_2K/(3600*24); + + // Year (400 yr blocks) - (400/4-3) leap years + *year = 400 * DivMod64( TS, 365*400 + (400/4-3), &TS ); + if( TS < 366 ) // First year in 400 is a leap + is_leap = 1; + else + { + // 100 yr blocks - 100/4-1 leap years + *year += 100 * DivMod64( TS, 365*100 + (100/4-1), &TS ); + if( TS < 366 ) // First year in 100 isn't a leap + is_leap = 0; + else + { + *year += 4 * DivMod64( TS, 365*4 + 1, &TS ); + if( TS < 366 ) // First year in 4 is a leap + is_leap = 1; + else + { + *year += DivMod64( TS, 356, &TS ); + } + } + } + *year += 2000; + + ASSERT(TS >= 0); + + *day = 0; + // Month (if after the first of march, which is 29 Feb in a leap year) + if( is_leap && TS > DAYS_BEFORE[2] ) { + TS -= 1; // Shifts 29 Feb to 28 Feb + *day = 1; + } + // Get what month it is + for( i = 0; i < 12; i ++ ) { + if( TS < DAYS_BEFORE[i] ) + break; + } + *month = i - 1; + // Get day + TS -= DAYS_BEFORE[i-1]; + *day += TS; // Plus offset from leap handling above +} + +/** + * \fn int rand() + * \brief Pseudo random number generator + */ +int rand(void) +{ + #if 0 + static Uint state = RANDOM_SEED; + Uint old = state; + // Get the next state value + giRandomState = (RANDOM_A*state + RANDOM_C); + // Check if it has changed, and if it hasn't, change it + if(state == old) state += RANDOM_SPRUCE; + return state; + #else + // http://en.wikipedia.org/wiki/Xorshift + // 2010-10-03 + static Uint32 x = 123456789; + static Uint32 y = 362436069; + static Uint32 z = 521288629; + static Uint32 w = 88675123; + Uint32 t; + + t = x ^ (x << 11); + x = y; y = z; z = w; + return w = w ^ (w >> 19) ^ t ^ (t >> 8); + #endif +} + +/* * + * \name Memory Validation + * \{ + */ +/** + * \brief Checks if a string resides fully in valid memory + */ +int CheckString(const char *String) +{ + tVAddr addr; + int bUser; + + addr = (tVAddr)String; + + if( !MM_GetPhysAddr( addr ) ) + return 0; + + // Check 1st page + bUser = MM_IsUser( addr ); + + while( *(char*)addr ) + { + if( (addr & (PAGE_SIZE-1)) == 0 ) + { + if(bUser && !MM_IsUser(addr) ) + return 0; + if(!bUser && !MM_GetPhysAddr(addr) ) + return 0; + } + addr ++; + } + return 1; +} + +/** + * \brief Check if a sized memory region is valid memory + * \return Boolean success + */ +int CheckMem(const void *Mem, int NumBytes) +{ + return MM_IsValidBuffer( (tVAddr)Mem, NumBytes ); +} +/* * + * \} + */ + +/** + * \brief Search a string array for \a Needle + * \note Helper function for eTplDrv_IOCtl::DRV_IOCTL_LOOKUP + */ +int ModUtil_LookupString(const char **Array, const char *Needle) +{ + int i; + if( !CheckString(Needle) ) return -1; + for( i = 0; Array[i]; i++ ) + { + if(strcmp(Array[i], Needle) == 0) return i; + } + return -1; +} + +int ModUtil_SetIdent(char *Dest, const char *Value) +{ + if( !CheckMem(Dest, 32) ) return -1; + strncpy(Dest, Value, 32); + return 1; +} + +int Hex(char *Dest, size_t Size, const Uint8 *SourceData) +{ + int i; + for( i = 0; i < Size; i ++ ) + { + sprintf(Dest + i*2, "%02x", SourceData[i]); + } + return i*2; +} + +/** + * \brief Convert a string of hexadecimal digits into a byte stream + */ +int UnHex(Uint8 *Dest, size_t DestSize, const char *SourceString) +{ + int i; + + for( i = 0; i < DestSize*2; i += 2 ) + { + Uint8 val = 0; + + if(SourceString[i] == '\0') break; + + if('0' <= SourceString[i] && SourceString[i] <= '9') + val |= (SourceString[i]-'0') << 4; + else if('A' <= SourceString[i] && SourceString[i] <= 'F') + val |= (SourceString[i]-'A'+10) << 4; + else if('a' <= SourceString[i] && SourceString[i] <= 'f') + val |= (SourceString[i]-'a'+10) << 4; + + if(SourceString[i+1] == '\0') break; + + if('0' <= SourceString[i+1] && SourceString[i+1] <= '9') + val |= (SourceString[i+1] - '0'); + else if('A' <= SourceString[i+1] && SourceString[i+1] <= 'F') + val |= (SourceString[i+1] - 'A' + 10); + else if('a' <= SourceString[i+1] && SourceString[i+1] <= 'f') + val |= (SourceString[i+1] - 'a' + 10); + + Dest[i/2] = val; + } + return i/2; +} + +Uint16 SwapEndian16(Uint16 Val) +{ + return ((Val&0xFF)<<8) | ((Val>>8)&0xFF); +} +Uint32 SwapEndian32(Uint32 Val) +{ + return ((Val&0xFF)<<24) | ((Val&0xFF00)<<8) | ((Val>>8)&0xFF00) | ((Val>>24)&0xFF); +} + +void *memmove(void *__dest, const void *__src, size_t len) +{ + size_t block_size; + char *dest = __dest; + const char *src = __src; + void *ret = __dest; + + if( len == 0 || dest == src ) + return dest; + + if( (tVAddr)dest > (tVAddr)src + len ) + return memcpy(dest, src, len); + if( (tVAddr)dest + len < (tVAddr)src ) + return memcpy(dest, src, len); + + // NOTE: Assumes memcpy works forward + if( (tVAddr)dest < (tVAddr)src ) + return memcpy(dest, src, len); + + if( (tVAddr)dest < (tVAddr)src ) + block_size = (tVAddr)src - (tVAddr)dest; + else + block_size = (tVAddr)dest - (tVAddr)src; + + block_size &= ~0xF; + + while(len >= block_size) + { + memcpy(dest, src, block_size); + len -= block_size; + dest += block_size; + src += block_size; + } + memcpy(dest, src, len); + return ret; + +} + diff --git a/KernelLand/Kernel/logging.c b/KernelLand/Kernel/logging.c new file mode 100644 index 00000000..b419be3d --- /dev/null +++ b/KernelLand/Kernel/logging.c @@ -0,0 +1,249 @@ +/* + * Acess 2 Kernel + * - By John Hodge (thePowersGang) + * + * logging.c - Kernel Logging Service + */ +#include +#include + +#define CACHE_MESSAGES 0 +#define PRINT_ON_APPEND 1 +#define USE_RING_BUFFER 1 +#define RING_BUFFER_SIZE 4096 + +// === CONSTANTS === +enum eLogLevels +{ + LOG_LEVEL_KPANIC, + LOG_LEVEL_PANIC, + LOG_LEVEL_FATAL, + LOG_LEVEL_ERROR, + LOG_LEVEL_WARNING, + LOG_LEVEL_NOTICE, + LOG_LEVEL_LOG, + LOG_LEVEL_DEBUG, + NUM_LOG_LEVELS +}; +const char *csaLevelColours[] = { + "\x1B[35m", "\x1B[34m", "\x1B[36m", "\x1B[31m", + "\x1B[33m", "\x1B[32m", "\x1B[0m", "\x1B[0m" + }; +const char *csaLevelCodes[] = {"k","p","f","e","w","n","l","d"}; + +// === TYPES === +typedef struct sLogEntry +{ + struct sLogEntry *Next; + struct sLogEntry *LevelNext; + Sint64 Time; + int Level; + int Length; + char Ident[9]; + char Data[]; +} tLogEntry; +typedef struct sLogList +{ + tLogEntry *Head; + tLogEntry *Tail; +} tLogList; + +// === PROTOTYPES === +void Log_AddEvent(const char *Ident, int Level, const char *Format, va_list Args); +static void Log_Int_PrintMessage(tLogEntry *Entry); +//void Log_KernelPanic(const char *Ident, const char *Message, ...); +//void Log_Panic(const char *Ident, const char *Message, ...); +//void Log_Error(const char *Ident, const char *Message, ...); +//void Log_Warning(const char *Ident, const char *Message, ...); +//void Log_Notice(const char *Ident, const char *Message, ...); +//void Log_Log(const char *Ident, const char *Message, ...); +//void Log_Debug(const char *Ident, const char *Message, ...); + +// === EXPORTS === +EXPORT(Log_Panic); +EXPORT(Log_Error); +EXPORT(Log_Warning); +EXPORT(Log_Notice); +EXPORT(Log_Log); +EXPORT(Log_Debug); + +// === GLOBALS === +tShortSpinlock glLogOutput; +#if USE_RING_BUFFER +Uint8 gaLog_RingBufferData[sizeof(tRingBuffer)+RING_BUFFER_SIZE]; +tRingBuffer *gpLog_RingBuffer = (void*)gaLog_RingBufferData; +#else +tMutex glLog; +tLogList gLog; +tLogList gLog_Levels[NUM_LOG_LEVELS]; +#endif + +// === CODE === +/** + * \brief Adds an event to the log + */ +void Log_AddEvent(const char *Ident, int Level, const char *Format, va_list Args) +{ + int len; + tLogEntry *ent; + va_list args_tmp; + + if( Level >= NUM_LOG_LEVELS ) return; + + va_copy(args_tmp, Args); + len = vsnprintf(NULL, 256, Format, args_tmp); + + //Log("len = %i", len); + + #if USE_RING_BUFFER || !CACHE_MESSAGES + { + char buf[sizeof(tLogEntry)+len+1]; + ent = (void*)buf; + #else + ent = malloc(sizeof(tLogEntry)+len+1); + #endif + ent->Time = now(); + strncpy(ent->Ident, Ident, 8); + ent->Ident[8] = '\0'; + ent->Level = Level; + ent->Length = len; + vsnprintf( ent->Data, len+1, Format, Args ); + + #if CACHE_MESSAGES + # if USE_RING_BUFFER + { + #define LOG_HDR_LEN (14+1+2+8+2) + char newData[ LOG_HDR_LEN + len + 2 + 1 ]; + char _ident[9]; + strncpy(_ident, Ident, 9); + sprintf( newData, "%014lli%s [%-8s] ", + ent->Time, csaLevelCodes[Level], Ident); + strcpy( newData + LOG_HDR_LEN, ent->Data ); + strcpy( newData + LOG_HDR_LEN + len, "\r\n" ); + gpLog_RingBuffer->Space = RING_BUFFER_SIZE; // Needed to init the buffer + RingBuffer_Write( gpLog_RingBuffer, newData, LOG_HDR_LEN + len + 2 ); + } + # else + Mutex_Acquire( &glLog ); + + ent->Next = gLog.Tail; + if(gLog.Head) + gLog.Tail = ent; + else + gLog.Tail = gLog.Head = ent; + + ent->LevelNext = gLog_Levels[Level].Tail; + if(gLog_Levels[Level].Head) + gLog_Levels[Level].Tail = ent; + else + gLog_Levels[Level].Tail = gLog_Levels[Level].Head = ent; + + Mutex_Release( &glLog ); + # endif + #endif + + #if PRINT_ON_APPEND || !CACHE_MESSAGES + Log_Int_PrintMessage( ent ); + #endif + + #if USE_RING_BUFFER || !CACHE_MESSAGES + } + #endif +} + +/** + * \brief Prints a log message to the debug console + */ +void Log_Int_PrintMessage(tLogEntry *Entry) +{ + SHORTLOCK( &glLogOutput ); + LogF("%s%014lli%s [%-8s] %i - %s", + csaLevelColours[Entry->Level], + Entry->Time, + csaLevelCodes[Entry->Level], + Entry->Ident, + Threads_GetTID(), + Entry->Data + ); + LogF("\x1B[0m\r\n"); // Separate in case Entry->Data is too long + SHORTREL( &glLogOutput ); +} + +/** + * \brief KERNEL PANIC!!!! + */ +void Log_KernelPanic(const char *Ident, const char *Message, ...) +{ + va_list args; + va_start(args, Message); + Log_AddEvent(Ident, LOG_LEVEL_KPANIC, Message, args); + va_end(args); + Panic("Log_KernelPanic - %s", Ident); +} + +/** + * \brief Panic Message - Driver Unrecoverable error + */ +void Log_Panic(const char *Ident, const char *Message, ...) +{ + va_list args; + va_start(args, Message); + Log_AddEvent(Ident, LOG_LEVEL_PANIC, Message, args); + va_end(args); +} + +/** + * \brief Error Message - Recoverable Error + */ +void Log_Error(const char *Ident, const char *Message, ...) +{ + va_list args; + va_start(args, Message); + Log_AddEvent(Ident, LOG_LEVEL_ERROR, Message, args); + va_end(args); +} + +/** + * \brief Warning Message - Something the user should know + */ +void Log_Warning(const char *Ident, const char *Message, ...) +{ + va_list args; + + va_start(args, Message); + Log_AddEvent(Ident, LOG_LEVEL_WARNING, Message, args); + va_end(args); +} + +/** + * \brief Notice Message - Something the user might like to know + */ +void Log_Notice(const char *Ident, const char *Message, ...) +{ + va_list args; + va_start(args, Message); + Log_AddEvent(Ident, LOG_LEVEL_NOTICE, Message, args); + va_end(args); +} + +/** + * \brief Log Message - Possibly useful information + */ +void Log_Log(const char *Ident, const char *Message, ...) +{ + va_list args; + va_start(args, Message); + Log_AddEvent(Ident, LOG_LEVEL_LOG, Message, args); + va_end(args); +} + +/** + * \brief Debug Message - Only a developer would want this info + */ +void Log_Debug(const char *Ident, const char *Message, ...) +{ + va_list args; + va_start(args, Message); + Log_AddEvent(Ident, LOG_LEVEL_DEBUG, Message, args); + va_end(args); +} diff --git a/KernelLand/Kernel/messages.c b/KernelLand/Kernel/messages.c new file mode 100644 index 00000000..afdcdbde --- /dev/null +++ b/KernelLand/Kernel/messages.c @@ -0,0 +1,141 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * messages.c + * - IPC Messages + */ +#define DEBUG 0 +#include +#include +#include +#include +#include + +// === CODE === +/** + * \fn int Proc_SendMessage(Uint Dest, int Length, void *Data) + * \brief Send an IPC message + * \param Dest Destination Thread + * \param Length Length of the message + * \param Data Message data + */ +int Proc_SendMessage(Uint Dest, int Length, void *Data) +{ + tThread *thread; + tMsg *msg; + + ENTER("pErr iDest iLength pData", Err, Dest, Length, Data); + + if(Length <= 0 || !Data) { + errno = -EINVAL; + LEAVE_RET('i', -1); + } + + // TODO: Check message length against global/per-thread maximums + // TODO: Restrict queue length + + // Get thread + thread = Threads_GetThread( Dest ); + + // Error check + if(!thread) LEAVE_RET('i', -1); + + // Get Spinlock + SHORTLOCK( &thread->IsLocked ); + + // Check if thread is still alive + if(thread->Status == THREAD_STAT_DEAD) { + SHORTREL( &thread->IsLocked ); + LEAVE_RET('i', -1); + } + + // Create message + msg = malloc( sizeof(tMsg)+Length ); + msg->Next = NULL; + msg->Source = Proc_GetCurThread()->TID; + msg->Length = Length; + memcpy(msg->Data, Data, Length); + + // If there are already messages + if(thread->LastMessage) { + thread->LastMessage->Next = msg; + thread->LastMessage = msg; + } else { + thread->Messages = msg; + thread->LastMessage = msg; + } + + SHORTREL(&thread->IsLocked); + + // Wake the thread + LOG("Waking %p (%i %s)", thread, thread->TID, thread->ThreadName); + Threads_PostEvent( thread, THREAD_EVENT_IPCMSG ); + + LEAVE_RET('i', 0); +} + +/** + * \fn int Proc_GetMessage(Uint *Source, void *Buffer) + * \brief Gets a message + * \param Source Where to put the source TID + * \param Buffer Buffer to place the message data (set to NULL to just get message length) + * \return Message length + */ +int Proc_GetMessage(Uint *Source, void *Buffer) +{ + int ret; + void *tmp; + tThread *cur = Proc_GetCurThread(); + + ENTER("pSource pBuffer", Source, Buffer); + + // Check if queue has any items + if(!cur->Messages) { + LEAVE('i', 0); + return 0; + } + + SHORTLOCK( &cur->IsLocked ); + + if(Source) { + *Source = cur->Messages->Source; + LOG("*Source = %i", *Source); + } + + // Get message length + if( !Buffer ) { + ret = cur->Messages->Length; + SHORTREL( &cur->IsLocked ); + LEAVE('i', ret); + return ret; + } + + // Get message + if(Buffer != GETMSG_IGNORE) + { + if( !CheckMem( Buffer, cur->Messages->Length ) ) + { + LOG("Invalid buffer"); + errno = -EINVAL; + SHORTREL( &cur->IsLocked ); + LEAVE('i', -1); + return -1; + } + LOG("Copied to buffer"); + memcpy(Buffer, cur->Messages->Data, cur->Messages->Length); + } + ret = cur->Messages->Length; + + // Remove from list + tmp = cur->Messages; + cur->Messages = cur->Messages->Next; + if(cur->Messages == NULL) cur->LastMessage = NULL; + + SHORTREL( &cur->IsLocked ); + + free(tmp); // Free outside of lock + + LEAVE('i', ret); + return ret; +} diff --git a/KernelLand/Kernel/modules.c b/KernelLand/Kernel/modules.c new file mode 100644 index 00000000..98ca1a1e --- /dev/null +++ b/KernelLand/Kernel/modules.c @@ -0,0 +1,449 @@ +/* + * Acess2 + * - Module Loader + */ +#define DEBUG 0 +#include +#include + +#define USE_EDI 0 +#define USE_UDI 0 + +// === PROTOTYPES === + int Module_int_Initialise(tModule *Module, const char *ArgString); +void Modules_int_GetBuiltinArray(void); +void Modules_LoadBuiltins(void); +void Modules_SetBuiltinParams(const char *Name, char *ArgString); + int Modules_InitialiseBuiltin(const char *Name); +// int Module_RegisterLoader(tModuleLoader *Loader); +// int Module_LoadMem(void *Buffer, Uint Length, char *ArgString); +// int Module_LoadFile(char *Path, char *ArgString); + int Module_int_ResolveDeps(tModule *Info); + int Module_IsLoaded(const char *Name); +// int Module_EnsureLoaded(const char *Name); + +// === EXPORTS === +EXPORT(Module_RegisterLoader); + +// === IMPORTS === +#if USE_UDI +extern int UDI_LoadDriver(void *Base); +#endif +extern void StartupPrint(const char *Str); +extern tModule gKernelModules; +extern tModule gKernelModulesEnd; + +// === GLOBALS === + int giNumBuiltinModules = 0; +tShortSpinlock glModuleSpinlock; +tModule *gLoadedModules = NULL; +tModuleLoader *gModule_Loaders = NULL; +tModule *gLoadingModules = NULL; +tModule **gapBuiltinModules = NULL; +char **gasBuiltinModuleArgs; + +// === CODE === +/** + * \brief Initialises a module + * \param Module Pointer to the module header + * \param ArgString Comma separated list of module arguments + * \return Zero on success, eModuleErrors or -1 on error + * \retval -1 Returned if a dependency fails, or a circular dependency + * exists. + * \retval 0 Returned on success + * \retval >0 Error code form the module's initialisation function + */ +int Module_int_Initialise(tModule *Module, const char *ArgString) +{ + int i, j; + int ret; + const char **deps; + char **args; + tModule *mod; + + ENTER("pModule", Module); + LOG("Module->Magic = 0x%x", Module->Magic); + if(Module->Magic != MODULE_MAGIC) { + Log_Warning( + "Module", + "Module %p is no a valid Acess2 module (0x%08x != 0x%08x)", + Module, Module->Magic, MODULE_MAGIC + ); + LEAVE('i', MODULE_ERR_BADMODULE); + return MODULE_ERR_BADMODULE; + } + LOG("Module->Name = %p \"%s\"", Module->Name, Module->Name); + + if(Module->Arch != MODULE_ARCH_ID) { + Log_Warning( + "Module", + "Module %p (%s) is for another architecture (%i)", + Module, Module->Name, Module->Arch + ); + } + + deps = Module->Dependencies; + + // Check if the module has been loaded + for( mod = gLoadedModules; mod; mod = mod->Next ) + { + if(mod == Module) LEAVE_RET('i', 0); + } + + // Add to the "loading" (prevents circular deps) + Module->Next = gLoadingModules; + gLoadingModules = Module; + + // Scan dependency list + for( j = 0; deps && deps[j]; j++ ) + { + // Check if the module is already loaded + for( mod = gLoadedModules; mod; mod = mod->Next ) + { + if(strcmp(deps[j], mod->Name) == 0) + break; + } + if( mod ) continue; // Dependency is loaded, check the rest + + // Ok, check if it's loading + for( mod = gLoadingModules->Next; mod; mod = mod->Next ) + { + if(strcmp(deps[j], mod->Name) == 0) + break; + } + if( mod ) { + Log_Warning("Module", "Circular dependency detected (%s and %s)", + mod->Name, Module->Name); + LEAVE_RET('i', -1); + } + + // So, if it's not loaded, we better load it then + for( i = 0; i < giNumBuiltinModules; i ++ ) + { + if( strcmp(deps[j], gapBuiltinModules[i]->Name) == 0 ) + break; + } + if( i == giNumBuiltinModules ) { + Log_Warning("Module", "Dependency '%s' for module '%s' failed", + deps[j], Module->Name); + return -1; + } + + // Dependency is not loaded, so load it + ret = Module_int_Initialise( + gapBuiltinModules[i], + gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL + ); + if( ret ) + { + // The only "ok" error is NOTNEEDED + if(ret != MODULE_ERR_NOTNEEDED) + LEAVE_RET('i', -1); + } + } + + // All Dependencies OK? Initialise + StartupPrint(Module->Name); + Log_Log("Module", "Starting %p '%s' v%i.%i", + Module, Module->Name, + Module->Version >> 8, Module->Version & 0xFF + ); + + if( ArgString ) + args = str_split( ArgString, ',' ); + else + args = NULL; + + ret = Module->Init(args); + + if(args) free(args); + + // Remove from loading list + gLoadingModules = gLoadingModules->Next; + + if( ret != MODULE_ERR_OK ) { + switch(ret) + { + case MODULE_ERR_MISC: + Log_Warning("Module", "Unable to load, reason: Miscelanious"); + break; + case MODULE_ERR_NOTNEEDED: + Log_Debug("Module", "Unable to load, reason: Module not needed"); + break; + case MODULE_ERR_MALLOC: + Log_Warning("Module", "Unable to load, reason: Error in malloc/realloc/calloc, probably not good"); + break; + default: + Log_Warning("Module", "Unable to load reason - Unknown code %i", ret); + break; + } + LEAVE_RET('i', ret); + return ret; + } + LOG("ret = %i", ret); + + // Add to loaded list + SHORTLOCK( &glModuleSpinlock ); + Module->Next = gLoadedModules; + gLoadedModules = Module; + SHORTREL( &glModuleSpinlock ); + + LEAVE_RET('i', 0); +} + +/** + * \brief Scans the builtin modules and creates an array of them + */ +void Modules_int_GetBuiltinArray(void) +{ + int i; + tModule *module; + + // Count + module = &gKernelModules; + i = 0; + while( (tVAddr)module < (tVAddr)&gKernelModulesEnd ) + { + if(module->Magic == MODULE_MAGIC) { + i ++; + module ++; + } + else + module = (void*)( (tVAddr)module + 4 ); + } + + // Create + giNumBuiltinModules = i; + gasBuiltinModuleArgs = calloc( giNumBuiltinModules, sizeof(char*) ); + gapBuiltinModules = malloc( giNumBuiltinModules * sizeof(tModule*) ); + + + // Fill + module = &gKernelModules; + i = 0; + while( (tVAddr)module < (tVAddr)&gKernelModulesEnd ) + { + if(module->Magic == MODULE_MAGIC) { + gapBuiltinModules[i] = module; + i ++; + module ++; + } + else + module = (void*)( (tVAddr)module + 4 ); + } +} + +/** + * \brief Initialises builtin modules + */ +void Modules_LoadBuiltins() +{ + int i; + + if( !gapBuiltinModules ) + Modules_int_GetBuiltinArray(); + + for( i = 0; i < giNumBuiltinModules; i++ ) + { + Module_int_Initialise( + gapBuiltinModules[i], + (gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL) + ); + } + + if( gasBuiltinModuleArgs != NULL ) + free(gasBuiltinModuleArgs); +} + +/** + * \brief Initialise a builtin module given it's name + * + * E.g. Used by VTerm to load an alternate video driver at runtime + */ +int Modules_InitialiseBuiltin(const char *Name) +{ + int i; + + // Check if it's loaded + if( Module_IsLoaded(Name) ) + return 0; + + if( !gapBuiltinModules ) + Modules_int_GetBuiltinArray(); + + for( i = 0; i < giNumBuiltinModules; i++ ) + { + if( strcmp(gapBuiltinModules[i]->Name, Name) == 0 ) { + return Module_int_Initialise(gapBuiltinModules[i], + (gasBuiltinModuleArgs ? gasBuiltinModuleArgs[i] : NULL) + ); + } + } + return -1; +} + +/** + * \brief Sets the parameters for a builtin module + */ +void Modules_SetBuiltinParams(const char *Name, char *ArgString) +{ + int i; + + if( gasBuiltinModuleArgs == NULL ) + { + Modules_int_GetBuiltinArray(); + } + + // I hate expensive scans + for( i = 0; i < giNumBuiltinModules; i++ ) + { + if(strcmp( gapBuiltinModules[i]->Name, Name ) == 0) { + gasBuiltinModuleArgs[i] = ArgString; + return ; + } + } + + Log_Warning("Modules", "Unknown builtin kernel module '%s'", Name); +} + +/** + * \brief Registers a tModuleLoader with the kernel + * \param Loader Pointer to loader structure (must be persistent) + */ +int Module_RegisterLoader(tModuleLoader *Loader) +{ + if(!Loader) return 1; + + Loader->Next = gModule_Loaders; + gModule_Loaders = Loader; + + return 0; +} + +/** + * \fn int Module_LoadMem(void *Buffer, Uint Length, char *ArgString) + * \brief Load a module from a memory location + */ +int Module_LoadMem(void *Buffer, Uint Length, const char *ArgString) +{ + char path[VFS_MEMPATH_SIZE]; + + VFS_GetMemPath(path, Buffer, Length); + + return Module_LoadFile( path, ArgString ); +} + +/** + * \fn int Module_LoadFile(const char *Path, const char *ArgString) + * \brief Load a module from a file + */ +int Module_LoadFile(const char *Path, const char *ArgString) +{ + void *base; + tModule *info; + + // Load Binary + base = Binary_LoadKernel(Path); + + // Error check + if(base == NULL) { + Log_Warning("Module", "Module_LoadFile - Unable to load '%s'", Path); + return 0; + } + + // Check for Acess Driver + if( Binary_FindSymbol(base, "DriverInfo", (Uint*)&info ) == 0 ) + { + tModuleLoader *tmp; + for( tmp = gModule_Loaders; tmp; tmp = tmp->Next) + { + if( tmp->Detector(base) == 0 ) continue; + + return tmp->Loader(base); + } + + #if USE_EDI + // Check for EDI Driver + if( Binary_FindSymbol(base, "driver_init", NULL ) != 0 ) + { + return Module_InitEDI( base ); // And intialise + } + #endif + + // Unknown module type?, return error + Binary_Unload(base); + #if USE_EDI + Log_Warning("Module", "Module '%s' has neither a Module Info struct, nor an EDI entrypoint", Path); + #else + Log_Warning("Module", "Module '%s' does not have a Module Info struct", Path); + #endif + return 0; + } + + // Initialise (and register) + if( Module_int_Initialise( info, ArgString ) ) + { + Binary_Unload(base); + return 0; + } + + return 1; +} + +/** + * \fn int Module_int_ResolveDeps(tModule *Info) + * \brief Resolves the dependencies + * \todo Implement + * \note Currently does not resolve the dependencies, just checks them + */ +int Module_int_ResolveDeps(tModule *Info) +{ + const char **names = Info->Dependencies; + + // Walk dependencies array + for( ; *names; names++ ) + { + // Check if the module is loaded + if( !Module_IsLoaded(*names) ) { + Log_Warning("Module", "Module `%s' requires `%s', which is not loaded\n", Info->Name, *names); + return 0; + } + } + return 1; +} + +/** + * \fn int Module_IsLoaded(const char *Name) + * \brief Checks if a module is loaded + * \param Name Name of module to find + */ +int Module_IsLoaded(const char *Name) +{ + tModule *mod = gLoadedModules; + + // Scan loaded list + for( ; mod; mod = mod->Next ) + { + // If found, return true + if(strcmp(mod->Name, Name) == 0) + return 1; + } + // not found - return false + return 0; +} + +/** + * \brief Load a module if needed + */ +int Module_EnsureLoaded(const char *Name) +{ + if( Module_IsLoaded(Name) ) + return 0; + + if( Modules_InitialiseBuiltin(Name) == 0 ) + return 0; + + // TODO: Load from a file? + + return -1; +} diff --git a/KernelLand/Kernel/mutex.c b/KernelLand/Kernel/mutex.c new file mode 100644 index 00000000..597242b6 --- /dev/null +++ b/KernelLand/Kernel/mutex.c @@ -0,0 +1,125 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * mutex.c + * - Mutexes + */ +#include +#include +#include + +// === PROTOTYPES === +#if 0 + int Mutex_Acquire(tMutex *Mutex); +void Mutex_Release(tMutex *Mutex); + int Mutex_IsLocked(tMutex *Mutex); +#endif + +// === CODE === +// +// Acquire mutex (see mutex.h for documentation) +// +int Mutex_Acquire(tMutex *Mutex) +{ + tThread *us = Proc_GetCurThread(); + + // Get protector + SHORTLOCK( &Mutex->Protector ); + +// Log("Mutex_Acquire: (%p)", Mutex); + + // Check if the lock is already held + if( Mutex->Owner ) { + SHORTLOCK( &glThreadListLock ); + // - Remove from active list + us = Threads_RemActive(); + us->Next = NULL; + // - Mark as sleeping + us->Status = THREAD_STAT_MUTEXSLEEP; + us->WaitPointer = Mutex; + + // - Add to waiting + if(Mutex->LastWaiting) { + Mutex->LastWaiting->Next = us; + Mutex->LastWaiting = us; + } + else { + Mutex->Waiting = us; + Mutex->LastWaiting = us; + } + + #if DEBUG_TRACE_STATE + Log("%p (%i %s) waiting on mutex %p", + us, us->TID, us->ThreadName, Mutex); + #endif + + #if 0 + { + int i = 0; + tThread *t; + for( t = Mutex->Waiting; t; t = t->Next, i++ ) + Log("[%i] (tMutex)%p->Waiting[%i] = %p (%i %s)", us->TID, Mutex, i, + t, t->TID, t->ThreadName); + } + #endif + + SHORTREL( &glThreadListLock ); + SHORTREL( &Mutex->Protector ); + while(us->Status == THREAD_STAT_MUTEXSLEEP) Threads_Yield(); + // We're only woken when we get the lock + us->WaitPointer = NULL; + } + // Ooh, let's take it! + else { + Mutex->Owner = us; + SHORTREL( &Mutex->Protector ); + } + + #if 0 + extern tMutex glPhysAlloc; + if( Mutex != &glPhysAlloc ) + LogF("Mutex %p taken by %i %p\n", Mutex, us->TID, __builtin_return_address(0)); + #endif + + return 0; +} + +// Release a mutex +void Mutex_Release(tMutex *Mutex) +{ + SHORTLOCK( &Mutex->Protector ); + //Log("Mutex_Release: (%p)", Mutex); + if( Mutex->Waiting ) { + Mutex->Owner = Mutex->Waiting; // Set owner + Mutex->Waiting = Mutex->Waiting->Next; // Next! + // Reset ->LastWaiting to NULL if we have just removed the last waiting thread + if( Mutex->LastWaiting == Mutex->Owner ) + Mutex->LastWaiting = NULL; + + // Wake new owner + if( Mutex->Owner->Status != THREAD_STAT_ACTIVE ) + Threads_AddActive(Mutex->Owner); + } + else { + Mutex->Owner = NULL; + } + SHORTREL( &Mutex->Protector ); + + #if 0 + extern tMutex glPhysAlloc; + if( Mutex != &glPhysAlloc ) + LogF("Mutex %p released by %i %p\n", Mutex, Threads_GetTID(), __builtin_return_address(0)); + #endif +} + +// Check if a mutex is locked +int Mutex_IsLocked(tMutex *Mutex) +{ + return Mutex->Owner != NULL; +} + +// === EXPORTS === +EXPORT(Mutex_Acquire); +EXPORT(Mutex_Release); +EXPORT(Mutex_IsLocked); diff --git a/KernelLand/Kernel/semaphore.c b/KernelLand/Kernel/semaphore.c new file mode 100644 index 00000000..d4583e74 --- /dev/null +++ b/KernelLand/Kernel/semaphore.c @@ -0,0 +1,266 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * semaphore.c + * - Semaphores + */ +#include +#include +#include + +#define SEMAPHORE_DEBUG 0 // Debug semaphores + +// === CODE === +// +// Initialise a semaphore +// +void Semaphore_Init(tSemaphore *Sem, int Value, int MaxValue, const char *Module, const char *Name) +{ + memset(Sem, 0, sizeof(tSemaphore)); + Sem->Value = Value; + Sem->ModName = Module; + Sem->Name = Name; + Sem->MaxValue = MaxValue; +} +// +// Wait for items to be avaliable +// +int Semaphore_Wait(tSemaphore *Sem, int MaxToTake) +{ + tThread *us; + int taken; + if( MaxToTake < 0 ) { + Log_Warning("Threads", "Semaphore_Wait: User bug - MaxToTake(%i) < 0, Sem=%p(%s)", + MaxToTake, Sem, Sem->Name); + } + + SHORTLOCK( &Sem->Protector ); + + // Check if there's already items avaliable + if( Sem->Value > 0 ) + { + // Take what we need + if( MaxToTake && Sem->Value > MaxToTake ) + taken = MaxToTake; + else + taken = Sem->Value; + Sem->Value -= taken; + } + else + { + SHORTLOCK( &glThreadListLock ); + + // - Remove from active list + us = Threads_RemActive(); + us->Next = NULL; + // - Mark as sleeping + us->Status = THREAD_STAT_SEMAPHORESLEEP; + us->WaitPointer = Sem; + us->RetStatus = MaxToTake; // Use RetStatus as a temp variable + + // - Add to waiting + if(Sem->LastWaiting) { + Sem->LastWaiting->Next = us; + Sem->LastWaiting = us; + } + else { + Sem->Waiting = us; + Sem->LastWaiting = us; + } + + #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG + Log("%p (%i %s) waiting on semaphore %p %s:%s", + us, us->TID, us->ThreadName, + Sem, Sem->ModName, Sem->Name); + #endif + + SHORTREL( &Sem->Protector ); // Release first to make sure it is released + SHORTREL( &glThreadListLock ); + while( us->Status == THREAD_STAT_SEMAPHORESLEEP ) + { + Threads_Yield(); + if(us->Status == THREAD_STAT_SEMAPHORESLEEP) + Log_Warning("Threads", "Semaphore %p %s:%s re-schedulued while asleep", + Sem, Sem->ModName, Sem->Name); + } + #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG + Log("Semaphore %p %s:%s woken", Sem, Sem->ModName, Sem->Name); + #endif + // We're only woken when there's something avaliable (or a signal arrives) + us->WaitPointer = NULL; + + taken = us->RetStatus; + + // Get the lock again + SHORTLOCK( &Sem->Protector ); + } + + // While there is space, and there are thread waiting + // wake the first thread and give it what it wants (or what's left) + while( (Sem->MaxValue == 0 || Sem->Value < Sem->MaxValue) && Sem->Signaling ) + { + int given; + tThread *toWake = Sem->Signaling; + + Sem->Signaling = Sem->Signaling->Next; + // Reset ->LastWaiting to NULL if we have just removed the last waiting thread + if( Sem->Signaling == NULL ) + Sem->LastSignaling = NULL; + + // Figure out how much to give + if( toWake->RetStatus && Sem->Value + toWake->RetStatus < Sem->MaxValue ) + given = toWake->RetStatus; + else + given = Sem->MaxValue - Sem->Value; + Sem->Value -= given; + + + #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG + Log("%p (%i %s) woken by wait on %p %s:%s", + toWake, toWake->TID, toWake->ThreadName, + Sem, Sem->ModName, Sem->Name); + #endif + + // Save the number we gave to the thread's status + toWake->RetStatus = given; + + // Wake the sleeper + SHORTLOCK( &glThreadListLock ); + if( toWake->Status != THREAD_STAT_ACTIVE ) + Threads_AddActive(toWake); + SHORTREL( &glThreadListLock ); + } + SHORTREL( &Sem->Protector ); + + #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG + Log("Semaphore %p %s:%s took %i by wait", + Sem, Sem->ModName, Sem->Name, taken); + #endif + + return taken; +} + +// +// Add items to a semaphore +// +int Semaphore_Signal(tSemaphore *Sem, int AmmountToAdd) +{ + int given; + int added; + + if( AmmountToAdd < 0 ) { + Log_Warning("Threads", "Semaphore_Signal: User bug - AmmountToAdd(%i) < 0, Sem=%p(%s)", + AmmountToAdd, Sem, Sem->Name); + } + SHORTLOCK( &Sem->Protector ); + + // Check if we have to block + if( Sem->MaxValue && Sem->Value == Sem->MaxValue ) + { + tThread *us; + #if 0 + Log_Debug("Threads", "Semaphore_Signal: IDLE Sem = %s:%s", Sem->ModName, Sem->Name); + Log_Debug("Threads", "Semaphore_Signal: Sem->Value(%i) == Sem->MaxValue(%i)", Sem->Value, Sem->MaxValue); + #endif + + SHORTLOCK( &glThreadListLock ); + // - Remove from active list + us = Threads_RemActive(); + us->Next = NULL; + // - Mark as sleeping + us->Status = THREAD_STAT_SEMAPHORESLEEP; + us->WaitPointer = Sem; + us->RetStatus = AmmountToAdd; // Use RetStatus as a temp variable + + // - Add to waiting + if(Sem->LastSignaling) { + Sem->LastSignaling->Next = us; + Sem->LastSignaling = us; + } + else { + Sem->Signaling = us; + Sem->LastSignaling = us; + } + + #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG + Log("%p (%i %s) signaling semaphore %p %s:%s", + us, us->TID, us->ThreadName, + Sem, Sem->ModName, Sem->Name); + #endif + + SHORTREL( &glThreadListLock ); + SHORTREL( &Sem->Protector ); + while(us->Status == THREAD_STAT_SEMAPHORESLEEP) Threads_Yield(); + // We're only woken when there's something avaliable + us->WaitPointer = NULL; + + added = us->RetStatus; + + // Get the lock again + SHORTLOCK( &Sem->Protector ); + } + // Non blocking + else + { + // Figure out how much we need to take off + if( Sem->MaxValue && Sem->Value + AmmountToAdd > Sem->MaxValue) + added = Sem->MaxValue - Sem->Value; + else + added = AmmountToAdd; + Sem->Value += added; + } + + // While there are items avaliable, and there are thread waiting + // wake the first thread and give it what it wants (or what's left) + while( Sem->Value && Sem->Waiting ) + { + tThread *toWake = Sem->Waiting; + + // Remove thread from list (double ended, so clear LastWaiting if needed) + Sem->Waiting = Sem->Waiting->Next; + if( Sem->Waiting == NULL ) + Sem->LastWaiting = NULL; + + // Figure out how much to give to woken thread + // - Requested count is stored in ->RetStatus + if( toWake->RetStatus && Sem->Value > toWake->RetStatus ) + given = toWake->RetStatus; + else + given = Sem->Value; + Sem->Value -= given; + + // Save the number we gave to the thread's status + toWake->RetStatus = given; + + if(toWake->bInstrTrace) + Log("%s(%i) given %i from %p", toWake->ThreadName, toWake->TID, given, Sem); + #if DEBUG_TRACE_STATE || SEMAPHORE_DEBUG + Log("%p (%i %s) woken by signal on %p %s:%s", + toWake, toWake->TID, toWake->ThreadName, + Sem, Sem->ModName, Sem->Name); + #endif + + // Wake the sleeper + if( toWake->Status != THREAD_STAT_ACTIVE ) + Threads_AddActive(toWake); + else + Warning("Thread %p (%i %s) is already awake", toWake, toWake->TID, toWake->ThreadName); + } + SHORTREL( &Sem->Protector ); + + return added; +} + +// +// Get the current value of a semaphore +// +int Semaphore_GetValue(tSemaphore *Sem) +{ + return Sem->Value; +} + +// === EXPORTS === +EXPORT(Semaphore_Init); +EXPORT(Semaphore_Wait); +EXPORT(Semaphore_Signal); diff --git a/KernelLand/Kernel/syscalls.c b/KernelLand/Kernel/syscalls.c new file mode 100644 index 00000000..5580187d --- /dev/null +++ b/KernelLand/Kernel/syscalls.c @@ -0,0 +1,389 @@ +/* + * AcessOS Microkernel Version + * syscalls.c + */ +#define DEBUG 0 + +#include +#include +#include +#include +#include +#include +#include + +#define CHECK_NUM_NULLOK(v,size) \ + if((v)&&!Syscall_Valid((size),(v))){ret=-1;err=-EINVAL;break;} +#define CHECK_STR_NULLOK(v) \ + if((v)&&!Syscall_ValidString((v))){ret=-1;err=-EINVAL;break;} +#define CHECK_NUM_NONULL(v,size) \ + if(!(v)||!Syscall_Valid((size),(v))){ret=-1;err=-EINVAL;break;} +#define CHECK_STR_NONULL(v) \ + if(!(v)||!Syscall_ValidString((v))){ret=-1;err=-EINVAL;break;} +#define CHECK_STR_ARRAY(arr) do {\ + int i;\ + char **tmp = (char**)arr; \ + CHECK_NUM_NONULL( tmp, sizeof(char**) ); \ + for(i=0;tmp[i];i++) { \ + CHECK_STR_NONULL( tmp[i] ); \ + CHECK_NUM_NONULL( &tmp[i+1], sizeof(char*) ); \ + }\ + if(tmp[i]) break;\ +} while(0) + +// === IMPORTS === +extern Uint Binary_Load(const char *file, Uint *entryPoint); + +// === PROTOTYPES === +void SyscallHandler(tSyscallRegs *Regs); + int Syscall_ValidString(const char *Addr); + int Syscall_Valid(int Size, const void *Addr); + +// === CODE === +// TODO: Do sanity checking on arguments, ATM the user can really fuck with the kernel +void SyscallHandler(tSyscallRegs *Regs) +{ + Uint64 ret = 0; + Uint err = -EOK; + int callNum = Regs->Num; + + #if DEBUG < 2 + if(callNum != SYS_READ && callNum != SYS_WRITE) { + #endif + ENTER("iThread iNum", Threads_GetTID(), callNum); + if(callNum < NUM_SYSCALLS) + LOG("Syscall %s", cSYSCALL_NAMES[callNum]); + LOG("Arg1: 0x%x, Arg2: 0x%x, Arg3: 0x%x, Arg4: 0x%x", Regs->Arg1, Regs->Arg2, Regs->Arg3, Regs->Arg4); + #if DEBUG < 2 + } + #endif + + switch(Regs->Num) + { + // -- Exit the current thread + case SYS_EXIT: Threads_Exit(0, Regs->Arg1); break; + + // -- Put the current thread to sleep + case SYS_SLEEP: Threads_Sleep(); break; + + // -- Yield current timeslice + case SYS_YIELD: Threads_Yield(); break; + + // -- Set Error Handler + case SYS_SETFAULTHANDLER: + Threads_SetFaultHandler(Regs->Arg1); + break; + + // -- Clone the current thread + case SYS_CLONE: + // Call clone system call + ret = Proc_Clone(Regs->Arg1); + break; + + // -- Send a signal + case SYS_KILL: + err = -ENOSYS; + ret = -1; + break; + + // -- Wait fr an event + case SYS_WAITEVENT: + // Message mask + ret = Threads_WaitEvents(Regs->Arg1); + break; + + // -- Wait for a thread + case SYS_WAITTID: + // Sanity Check (Status can be NULL) + CHECK_NUM_NULLOK( (int*)Regs->Arg2, sizeof(int) ); + // TID, *Status + ret = Threads_WaitTID(Regs->Arg1, (int*)Regs->Arg2); + break; + + // -- Get the physical address of a page + case SYS_GETPHYS: + ret = MM_GetPhysAddr(Regs->Arg1); + break; + + // -- Map an address + case SYS_MAP: MM_Map(Regs->Arg1, Regs->Arg2); break; + + // -- Allocate an address + case SYS_ALLOCATE: ret = MM_Allocate(Regs->Arg1); break; + + // -- Unmap an address + case SYS_UNMAP: MM_Deallocate(Regs->Arg1); break; + + // -- Get Thread/Process IDs + case SYS_GETTID: ret = Threads_GetTID(); break; + case SYS_GETPID: ret = Threads_GetPID(); break; + + // -- Get User/Group IDs + case SYS_GETUID: ret = Threads_GetUID(); break; + case SYS_GETGID: ret = Threads_GetGID(); break; + + // -- Set User/Group IDs + case SYS_SETUID: ret = Threads_SetUID(Regs->Arg1); break; + case SYS_SETGID: ret = Threads_SetGID(Regs->Arg1); break; + + // -- Send Message + case SYS_SENDMSG: + CHECK_NUM_NONULL( (void*)Regs->Arg3, Regs->Arg2 ); + // Destination, Size, *Data + ret = Proc_SendMessage(Regs->Arg1, Regs->Arg2, (void*)Regs->Arg3); + break; + // -- Check for messages + case SYS_GETMSG: + CHECK_NUM_NULLOK( (Uint*)Regs->Arg1, sizeof(Uint) ); + // NOTE: Can't do range checking as we don't know the size + // - Should be done by Proc_GetMessage + if( Regs->Arg2 && Regs->Arg2 != -1 && !MM_IsUser(Regs->Arg2) ) { + err = -EINVAL; ret = -1; break; + } + // *Source, *Data + ret = Proc_GetMessage((Uint*)Regs->Arg1, (void*)Regs->Arg2); + break; + + // -- Get the current timestamp + case SYS_GETTIME: + ret = now(); + break; + + // -- Set the thread's name + case SYS_SETNAME: + CHECK_STR_NONULL( (char*) Regs->Arg1); + Threads_SetName( (char*)Regs->Arg1 ); + break; + + // --- + // Binary Control + // --- + // -- Create a new process + case SYS_SPAWN: + CHECK_STR_NONULL((const char*)Regs->Arg1); + CHECK_STR_ARRAY((const char**)Regs->Arg2); + CHECK_STR_ARRAY((const char**)Regs->Arg3); + CHECK_NUM_NONULL((void*)Regs->Arg5, Regs->Arg4*sizeof(int)); + ret = Proc_SysSpawn( + (const char*)Regs->Arg1, (const char**)Regs->Arg2, (const char**)Regs->Arg3, + Regs->Arg4, (int*)Regs->Arg5 + ); + break; + // -- Replace the current process with another + case SYS_EXECVE: + CHECK_STR_NONULL((char*)Regs->Arg1); + CHECK_STR_ARRAY( (char**)Regs->Arg2 ); + if( Regs->Arg3 ) + CHECK_STR_ARRAY( (char**)Regs->Arg3 ); + LEAVE('s', "Assuming 0"); + // Path, **Argv, **Envp, DataSize (=0 to tell it to create a copy) + ret = Proc_Execve( + (const char*)Regs->Arg1, (const char**)Regs->Arg2, (const char**)Regs->Arg3, + 0 + ); + break; + // -- Load a binary into the current process + case SYS_LOADBIN: + CHECK_STR_NONULL( (char*)Regs->Arg1 ); + CHECK_NUM_NONULL( (Uint*)Regs->Arg2, sizeof(Uint) ); + // Path, *Entrypoint + ret = Binary_Load((char*)Regs->Arg1, (Uint*)Regs->Arg2); + break; + + // --- + // Virtual Filesystem + // --- + case SYS_OPEN: + CHECK_STR_NONULL( (char*)Regs->Arg1 ); + LOG("VFS_Open(\"%s\", 0x%x)", (char*)Regs->Arg1, Regs->Arg2 | VFS_OPENFLAG_USER); + ret = VFS_Open((char*)Regs->Arg1, Regs->Arg2 | VFS_OPENFLAG_USER); + break; + + case SYS_CLOSE: + LOG("VFS_Close(%i)", Regs->Arg1); + VFS_Close( Regs->Arg1 ); + break; + + case SYS_SEEK: + #if BITS == 64 + ret = VFS_Seek( Regs->Arg1, Regs->Arg2, Regs->Arg3 ); + #else + ret = VFS_Seek( Regs->Arg1, Regs->Arg2|(((Uint64)Regs->Arg3)<<32), Regs->Arg4 ); + #endif + break; + + case SYS_TELL: + ret = VFS_Tell( Regs->Arg1 ); + break; + + case SYS_WRITE: + CHECK_NUM_NONULL( (void*)Regs->Arg2, Regs->Arg3 ); + ret = VFS_Write( Regs->Arg1, Regs->Arg3, (void*)Regs->Arg2 ); + break; + + case SYS_READ: + CHECK_NUM_NONULL( (void*)Regs->Arg2, Regs->Arg3 ); + ret = VFS_Read( Regs->Arg1, Regs->Arg3, (void*)Regs->Arg2 ); + break; + + case SYS_FINFO: + CHECK_NUM_NONULL( (void*)Regs->Arg2, sizeof(tFInfo) + Regs->Arg3*sizeof(tVFS_ACL) ); + // FP, Dest, MaxACLs + ret = VFS_FInfo( Regs->Arg1, (void*)Regs->Arg2, Regs->Arg3 ); + break; + + // Get ACL Value + case SYS_GETACL: + CHECK_NUM_NONULL( (void*)Regs->Arg2, sizeof(tVFS_ACL) ); + ret = VFS_GetACL( Regs->Arg1, (void*)Regs->Arg2 ); + break; + + // Read Directory + case SYS_READDIR: + // TODO: What if the filename is longer? + // Maybe force it to be a 256 byte buffer + CHECK_NUM_NONULL( (void*)Regs->Arg2, 256 ); + ret = VFS_ReadDir( Regs->Arg1, (void*)Regs->Arg2 ); + break; + + // Open a file that is a entry in an open directory + case SYS_OPENCHILD: + CHECK_STR_NONULL( (char*)Regs->Arg2 ); + ret = VFS_OpenChild( Regs->Arg1, (char*)Regs->Arg2, Regs->Arg3 | VFS_OPENFLAG_USER); + break; + + // Change Directory + case SYS_CHDIR: + CHECK_STR_NONULL( (const char*)Regs->Arg1 ); + ret = VFS_ChDir( (const char*)Regs->Arg1 ); + break; + + // IO Control + case SYS_IOCTL: + // All sanity checking should be done by the driver + if( Regs->Arg3 && !MM_IsUser(Regs->Arg3) ) { + err = -EINVAL; ret = -1; break; + } + ret = VFS_IOCtl( Regs->Arg1, Regs->Arg2, (void*)Regs->Arg3 ); + break; + + // Mount a filesystem + case SYS_MOUNT: + // Only root can mount filesystems + if(Threads_GetUID() != 0) { + err = -EACCES; + ret = -1; + break; + } + // Sanity check the paths + if(!Syscall_ValidString((char*)Regs->Arg1) + || !Syscall_ValidString((char*)Regs->Arg2) + || !Syscall_ValidString((char*)Regs->Arg3) + || !Syscall_ValidString((char*)Regs->Arg4) ) { + err = -EINVAL; + ret = -1; + break; + } + ret = VFS_Mount( + (char*)Regs->Arg1, // Device + (char*)Regs->Arg2, // Mount point + (char*)Regs->Arg3, // Filesystem + (char*)Regs->Arg4 // Options + ); + break; + + // Wait on a set of handles + case SYS_SELECT: + // Sanity checks + if( (Regs->Arg2 && !Syscall_Valid(sizeof(fd_set), (void*)Regs->Arg2)) + || (Regs->Arg3 && !Syscall_Valid(sizeof(fd_set), (void*)Regs->Arg3)) + || (Regs->Arg4 && !Syscall_Valid(sizeof(fd_set), (void*)Regs->Arg4)) + || (Regs->Arg5 && !Syscall_Valid(sizeof(tTime), (void*)Regs->Arg5)) ) + { + err = -EINVAL; + ret = -1; + break; + } + // Perform the call + ret = VFS_Select( + Regs->Arg1, // Max handle + (fd_set *)Regs->Arg2, // Read + (fd_set *)Regs->Arg3, // Write + (fd_set *)Regs->Arg4, // Errors + (tTime *)Regs->Arg5, // Timeout + (Uint32)Regs->Arg6, // Extra wakeup events + 0 // User handles + ); + break; + + // -- Debug + //#if DEBUG_BUILD + case SYS_DEBUG: + CHECK_STR_NONULL( (char*)Regs->Arg1 ); + LogF("Log: %08lli [%i] ", now(), Threads_GetTID()); + LogF((const char*)Regs->Arg1, + Regs->Arg2, Regs->Arg3, Regs->Arg4, Regs->Arg5, Regs->Arg6); + LogF("\r\n"); + break; + //#endif + + // -- Default (Return Error) + default: + Log_Warning("Syscalls", "Unknown System Call %i", Regs->Num); + if(Regs->Num < NUM_SYSCALLS) + Log_Warning("Syscall", " named '%s'", cSYSCALL_NAMES[Regs->Num]); + err = -ENOSYS; + ret = -1; + break; + } + + if(err == 0) err = errno; + + if(err != 0) { + LOG("ID: %i, Return errno = %i", Regs->Num, err); + } + + #if BITS < 64 + Regs->Return = ret&0xFFFFFFFF; + Regs->RetHi = ret >> 32; + #else + Regs->Return = ret; + #endif + Regs->Error = err; + #if DEBUG + # if DEBUG < 2 + if( callNum != SYS_READ && callNum != SYS_WRITE ) { + # endif + LOG("err = %i", err); + if( callNum == SYS_EXECVE ) + LOG("Actual %i", ret); + else + LEAVE('x', ret); + # if DEBUG < 2 + } + # endif + #endif +} + +/** + * \fn int Syscall_ValidString(const char *Addr) + * \brief Checks if a memory address contains a valid string + */ +int Syscall_ValidString(const char *Addr) +{ + // Check if the memory is user memory + if(!MM_IsUser( (tVAddr) Addr)) return 0; + + return CheckString( Addr ); +} + +/** + * \fn int Syscall_Valid(int Size, const void *Addr) + * \brief Checks if a memory address is valid + */ +int Syscall_Valid(int Size, const void *Addr) +{ + if(!MM_IsUser( (tVAddr)Addr )) return 0; + + return CheckMem( Addr, Size ); +} diff --git a/KernelLand/Kernel/syscalls.lst b/KernelLand/Kernel/syscalls.lst new file mode 100644 index 00000000..283caf9c --- /dev/null +++ b/KernelLand/Kernel/syscalls.lst @@ -0,0 +1,64 @@ + +0 +SYS_EXIT Kill this thread +SYS_CLONE Create a new thread +SYS_KILL Send a signal +SYS_SETFAULTHANDLER Set signal Handler +SYS_YIELD Yield remainder of timestamp +SYS_SLEEP Sleep until messaged or signaled +SYS_WAITEVENT Wait for an event +SYS_WAITTID Wait for a thread to do something + +SYS_SETNAME Sets the name of the current thread +SYS_GETNAME Gets the name of a thread +SYS_GETTID Get current thread ID +SYS_GETPID Get current thread group ID +SYS_SETPRI Set process priority + +SYS_SENDMSG Send an IPC message +SYS_GETMSG Recieve an IPC message + +SYS_GETTIME Get the current timestamp + +SYS_SPAWN Spawn a new process +SYS_EXECVE Replace the current process +SYS_LOADBIN Load a binary into the current address space +SYS_UNLOADBIN Unload a loaded binary +SYS_LOADMOD Load a module into the kernel + +32 +SYS_GETPHYS Get the physical address of a page +SYS_MAP Map a physical address +SYS_ALLOCATE Allocate a page +SYS_UNMAP Unmap a page +SYS_PREALLOC Preallocate a page +SYS_SETFLAGS Set a page's flags +SYS_SHAREWITH Share a page with another thread + +SYS_GETUID Get current User ID +SYS_GETGID Get current Group ID +SYS_SETUID Set current user ID +SYS_SETGID Set current Group ID + +64 +SYS_OPEN Open a file +SYS_REOPEN Close a file and reuse its handle +SYS_CLOSE Close a file +SYS_READ Read from an open file +SYS_WRITE Write to an open file +SYS_IOCTL Perform an IOCtl Call +SYS_SEEK Seek to a new position in the file +SYS_READDIR Read from an open directory +SYS_OPENCHILD Open a child entry in a directory +SYS_GETACL Get an ACL Value +SYS_SETACL Set an ACL Value +SYS_FINFO Get file information +SYS_MKDIR Create a new directory +SYS_LINK Create a new link to a file +SYS_SYMLINK Create a symbolic link +SYS_UNLINK Delete a file +SYS_TELL Return the current file position +SYS_CHDIR Change current directory +SYS_GETCWD Get current directory +SYS_MOUNT Mount a filesystem +SYS_SELECT Wait for file handles diff --git a/KernelLand/Kernel/system.c b/KernelLand/Kernel/system.c new file mode 100644 index 00000000..8fc2a1cc --- /dev/null +++ b/KernelLand/Kernel/system.c @@ -0,0 +1,252 @@ +/* + * Acess 2 Kernel + * - By John Hodge (thePowersGang) + * system.c + * - Architecture Independent System Init + */ +#define DEBUG 0 +#include + +// === IMPORTS === +extern void Arch_LoadBootModules(void); +extern int Modules_LoadBuiltins(void); +extern void Modules_SetBuiltinParams(char *Name, char *ArgString); +extern void Debug_SetKTerminal(const char *File); + +// === PROTOTYPES === +void System_Init(char *Commandline); +void System_ParseCommandLine(char *ArgString); +void System_ExecuteCommandLine(void); +void System_ParseVFS(char *Arg); +void System_ParseModuleArgs(char *Arg); +void System_ParseSetting(char *Arg); + +// === GLOBALS === +const char *gsInitBinary = "/Acess/SBin/init"; +char *argv[32]; + int argc; + +// === CODE === +void System_Init(char *CommandLine) +{ + // Parse Kernel's Command Line + System_ParseCommandLine(CommandLine); + + // Initialise modules + Log_Log("Config", "Initialising builtin modules..."); + Modules_LoadBuiltins(); + Arch_LoadBootModules(); + + System_ExecuteCommandLine(); + + // - Execute the Config Script + Log_Log("Config", "Spawning init '%s'", gsInitBinary); + Proc_Spawn(gsInitBinary); + + // Set the debug to be echoed to the terminal + Log_Log("Config", "Kernel now echoes to VT7 (Ctrl-Alt-F8)"); + Debug_SetKTerminal("/Devices/VTerm/7"); +} + +/** + * \fn void System_ParseCommandLine(char *ArgString) + * \brief Parses the kernel's command line and sets the environment + */ +void System_ParseCommandLine(char *ArgString) +{ + int i; + char *str; + + Log_Log("Config", "Kernel Invocation (%p) \"%s\"", ArgString, ArgString); + + // --- Get Arguments --- + str = ArgString; + for( argc = 0; argc < 32; argc++ ) + { + // Eat Whitespace + while(*str == ' ') str++; + // Check for the end of the string + if(*str == '\0') { argc--; break;} + argv[argc] = str; + if(*str == '"') { + while(*str && !(*str == '"' && str[-1] != '\\')) + str ++; + } + else { + while(*str && *str != ' ') + str++; + } + if(*str == '\0') break; // Check for EOS + *str = '\0'; // Cap off argument string + str ++; // and increment the string pointer + } + if(argc < 32) + argc ++; // Count last argument + + // --- Parse Arguments (Pass 1) --- + for( i = 1; i < argc; i++ ) + { + switch(argv[i][0]) + { + // --- VFS --- + // Ignored on this pass + case '/': + break; + + // --- Module Paramaters --- + // -VTerm:Width=640,Height=480,Scrollback=2 + case '-': + System_ParseModuleArgs( argv[i] ); + break; + // --- Config Options --- + // SCRIPT=/Acess/Conf/BootConf.cfg + default: + System_ParseSetting( argv[i] ); + break; + } + } +} + +void System_ExecuteCommandLine(void) +{ + int i; + if(argc > 0) + LOG("Invocation '%s'", argv[0]); + for( i = 1; i < argc; i++ ) + { + LOG("argv[%i] = '%s'", i, argv[i]); + switch(argv[i][0]) + { + // --- VFS --- + // Mount /System=ext2:/Devices/ATA/A1 + // Symlink /Acess=/System/Acess2 + case '/': + System_ParseVFS( argv[i] ); + break; + } + } +} + +/** + * \fn void System_ParseVFS(char *Arg) + */ +void System_ParseVFS(char *Arg) +{ + char *value; + int fd; + + value = Arg; + // Search for the '=' token + while( *value && *value != '=' ) + value++; + + // Check if the equals was found + if( *value == '\0' ) { + Log_Warning("Config", "Expected '=' in the string '%s'", Arg); + return ; + } + + // Edit string + *value = '\0'; value ++; + + // Check assignment type + // - Symbolic Link = + if(value[0] == '/') + { + Log_Log("Config", "Symbolic link '%s' pointing to '%s'", Arg, value); + VFS_Symlink(Arg, value); + } + // - Mount =: + else + { + char *dev = value; + // Find colon + while(*dev && *dev != ':') dev++; + if(*dev) { + *dev = '\0'; + dev++; // Eat ':' + } + // Create Mountpoint + if( (fd = VFS_Open(Arg, 0)) == -1 ) { + Log_Log("Config", "Creating directory '%s'", Arg, value); + VFS_MkDir( Arg ); + } else { + VFS_Close(fd); + } + // Mount + Log_Log("Config", "Mounting '%s' to '%s' ('%s')", dev, Arg, value); + VFS_Mount(dev, Arg, value, ""); + } +} + +/** + * \brief Parse a module argument string + * \param Arg Argument string + */ +void System_ParseModuleArgs(char *Arg) +{ + char *name, *args; + int i; + + // Remove '-' + name = Arg + 1; + + // Find the start of the args + i = strpos(name, ':'); + if( i == -1 ) { + Log_Warning("Config", "Module spec with no arguments"); + #if 1 + return ; + #else + i = strlen(name); + args = name + i; + #endif + } + else { + name[i] = '\0'; + args = name + i + 1; + } + + Log_Log("Config", "Setting boot parameters for '%s' to '%s'", name, args); + Modules_SetBuiltinParams(name, args); +} + +/** + * \fn void System_ParseSetting(char *Arg) + */ +void System_ParseSetting(char *Arg) +{ + char *value; + value = Arg; + + // Search for the '=' token + while( *value && *value != '=' ) + value++; + + // Check for boolean/flag (no '=') + if(*value == '\0') + { + //if(strcmp(Arg, "") == 0) { + //} else { + Log_Warning("Config", "Kernel flag '%s' is not recognised", Arg); + //} + } + else + { + *value = '\0'; // Remove '=' + value ++; // and eat it's position + + if(strcmp(Arg, "INIT") == 0) { + Log_Log("Config", "Init binary: '%s'", value); + if(strlen(value) == 0) + gsInitBinary = NULL; + else + gsInitBinary = value; + } + else { + Log_Warning("Config", "Kernel config setting '%s' is not recognised", Arg); + } + + } +} + diff --git a/KernelLand/Kernel/threads.c b/KernelLand/Kernel/threads.c new file mode 100644 index 00000000..956eede8 --- /dev/null +++ b/KernelLand/Kernel/threads.c @@ -0,0 +1,1355 @@ +/* + * Acess2 + * threads.c + * - Common Thread Control + */ +#include +#include +#include +#include +#include +#include +#include // VFS Handle maintainence + +// Configuration +#define DEBUG_TRACE_TICKETS 0 // Trace ticket counts +#define DEBUG_TRACE_STATE 0 // Trace state changes (sleep/wake) + +// --- Schedulers --- +#define SCHED_UNDEF 0 +#define SCHED_LOTTERY 1 // Lottery scheduler +#define SCHED_RR_SIM 2 // Single Queue Round Robin +#define SCHED_RR_PRI 3 // Multi Queue Round Robin +// Set scheduler type +#define SCHEDULER_TYPE SCHED_RR_PRI + +// === CONSTANTS === +#define DEFAULT_QUANTUM 5 +#define DEFAULT_PRIORITY 5 +#define MIN_PRIORITY 10 + +// === IMPORTS === + +// === TYPE === +typedef struct +{ + tThread *Head; + tThread *Tail; +} tThreadList; + +// === PROTOTYPES === +void Threads_Init(void); +#if 0 +void Threads_Delete(tThread *Thread); + int Threads_SetName(const char *NewName); +#endif +char *Threads_GetName(tTID ID); +#if 0 +void Threads_SetPriority(tThread *Thread, int Pri); +tThread *Threads_CloneTCB(Uint *Err, Uint Flags); + int Threads_WaitTID(int TID, int *status); +tThread *Threads_GetThread(Uint TID); +#endif +tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread); +void Threads_int_AddToList(tThreadList *List, tThread *Thread); +#if 0 +void Threads_Exit(int TID, int Status); +void Threads_Kill(tThread *Thread, int Status); +void Threads_Yield(void); +void Threads_Sleep(void); + int Threads_Wake(tThread *Thread); +void Threads_AddActive(tThread *Thread); +tThread *Threads_RemActive(void); +#endif +void Threads_ToggleTrace(int TID); +void Threads_Fault(int Num); +void Threads_SegFault(tVAddr Addr); +#if 0 + int Threads_GetPID(void); + int Threads_GetTID(void); +tUID Threads_GetUID(void); +tGID Threads_GetGID(void); + int Threads_SetUID(Uint *Errno, tUID ID); + int Threads_SetGID(Uint *Errno, tUID ID); +#endif +void Threads_Dump(void); +void Threads_DumpActive(void); + +// === GLOBALS === +// -- Core Thread -- +struct sProcess gProcessZero = { + }; +// Only used for the core kernel +tThread gThreadZero = { + .Status = THREAD_STAT_ACTIVE, // Status + .ThreadName = (char*)"ThreadZero", // Name + .Quantum = DEFAULT_QUANTUM, // Default Quantum + .Remaining = DEFAULT_QUANTUM, // Current Quantum + .Priority = DEFAULT_PRIORITY // Number of tickets + }; +// -- Processes -- +// --- Locks --- +tShortSpinlock glThreadListLock; ///\note NEVER use a heap function while locked +// --- Current State --- +volatile int giNumActiveThreads = 0; // Number of threads on the active queue +volatile Uint giNextTID = 1; // Next TID to allocate +// --- Thread Lists --- +tThread *gAllThreads = NULL; // All allocated threads +tThreadList gSleepingThreads; // Sleeping Threads + int giNumCPUs = 1; // Number of CPUs +BOOL gaThreads_NoTaskSwitch[MAX_CPUS]; // Disables task switches for each core (Pseudo-IF) +// --- Scheduler Types --- +#if SCHEDULER_TYPE == SCHED_LOTTERY +const int caiTICKET_COUNTS[MIN_PRIORITY+1] = {100,81,64,49,36,25,16,9,4,1,0}; +volatile int giFreeTickets = 0; // Number of tickets held by non-scheduled threads +tThreadList gActiveThreads; // Currently Running Threads +#elif SCHEDULER_TYPE == SCHED_RR_SIM +tThreadList gActiveThreads; // Currently Running Threads +#elif SCHEDULER_TYPE == SCHED_RR_PRI +tThreadList gaActiveThreads[MIN_PRIORITY+1]; // Active threads for each priority level +#else +# error "Unkown scheduler type" +#endif + +// === CODE === +/** + * \fn void Threads_Init(void) + * \brief Initialse the thread list + */ +void Threads_Init(void) +{ + ArchThreads_Init(); + + Log_Debug("Threads", "Offsets of tThread"); + Log_Debug("Threads", ".Priority = %i", offsetof(tThread, Priority)); + Log_Debug("Threads", ".KernelStack = %i", offsetof(tThread, KernelStack)); + + // Create Initial Task +// #if SCHEDULER_TYPE == SCHED_RR_PRI +// gaActiveThreads[gThreadZero.Priority].Head = &gThreadZero; +// gaActiveThreads[gThreadZero.Priority].Tail = &gThreadZero; +// #else +// gActiveThreads.Head = &gThreadZero; +// gActiveThreads.Tail = &gThreadZero; +// #endif + + gAllThreads = &gThreadZero; + giNumActiveThreads = 1; + gThreadZero.Process = &gProcessZero; + + Proc_Start(); +} + +void Threads_Delete(tThread *Thread) +{ + // Set to dead + Thread->Status = THREAD_STAT_BURIED; + + // Clear out process state + Proc_ClearThread(Thread); + + Thread->Process->nThreads --; + if( Thread->Process->nThreads == 0 ) + { + tProcess *proc = Thread->Process; + // VFS Cleanup + VFS_CloseAllUserHandles(); + // Architecture cleanup + Proc_ClearProcess( proc ); + // VFS Configuration strings + if( proc->CurrentWorkingDir) + free( proc->CurrentWorkingDir ); + if( proc->RootDir ) + free( proc->RootDir ); + // Process descriptor + free( proc ); + } + + // Free name + if( IsHeap(Thread->ThreadName) ) + free(Thread->ThreadName); + + // Remove from global list + // TODO: Lock this too + if( Thread == gAllThreads ) + gAllThreads = Thread->GlobalNext; + else + Thread->GlobalPrev->GlobalNext = Thread->GlobalNext; + + free(Thread); +} + +/** + * \fn void Threads_SetName(const char *NewName) + * \brief Sets the current thread's name + * \param NewName New name for the thread + * \return Boolean Failure + */ +int Threads_SetName(const char *NewName) +{ + tThread *cur = Proc_GetCurThread(); + char *oldname = cur->ThreadName; + + // NOTE: There is a possibility of non-thread safety here + // A thread could read the current name pointer before it is zeroed + + cur->ThreadName = NULL; + + if( IsHeap(oldname) ) free( oldname ); + cur->ThreadName = strdup(NewName); + + Log_Debug("Threads", "Thread renamed to '%s'", NewName); + + return 0; +} + +/** + * \fn char *Threads_GetName(int ID) + * \brief Gets a thread's name + * \param ID Thread ID (-1 indicates current thread) + * \return Pointer to name + * \retval NULL Failure + */ +char *Threads_GetName(tTID ID) +{ + if(ID == -1) { + return Proc_GetCurThread()->ThreadName; + } + return Threads_GetThread(ID)->ThreadName; +} + +/** + * \fn void Threads_SetPriority(tThread *Thread, int Pri) + * \brief Sets the priority of a task + * \param Thread Thread to update ticket count (NULL means current thread) + * \param Pri New priority + */ +void Threads_SetPriority(tThread *Thread, int Pri) +{ + // Get current thread + if(Thread == NULL) Thread = Proc_GetCurThread(); + // Bounds checking + // - If < 0, set to lowest priority + // - Minumum priority is actualy a high number, 0 is highest + if(Pri < 0) Pri = MIN_PRIORITY; + if(Pri > MIN_PRIORITY) Pri = MIN_PRIORITY; + + // Do we actually have to do anything? + if( Pri == Thread->Priority ) return; + + #if SCHEDULER_TYPE == SCHED_RR_PRI + if( Thread != Proc_GetCurThread() ) + { + SHORTLOCK( &glThreadListLock ); + // Remove from old priority + Threads_int_DelFromQueue( &gaActiveThreads[Thread->Priority], Thread ); + // And add to new + Threads_int_AddToList( &gaActiveThreads[Pri], Thread ); + Thread->Priority = Pri; + SHORTREL( &glThreadListLock ); + } + else + Thread->Priority = Pri; + #else + // If this isn't the current thread, we need to lock + if( Thread != Proc_GetCurThread() ) + { + SHORTLOCK( &glThreadListLock ); + + #if SCHEDULER_TYPE == SCHED_LOTTERY + giFreeTickets -= caiTICKET_COUNTS[Thread->Priority] - caiTICKET_COUNTS[Pri]; + # if DEBUG_TRACE_TICKETS + Log("Threads_SetTickets: new giFreeTickets = %i [-%i+%i]", + giFreeTickets, + caiTICKET_COUNTS[Thread->Priority], caiTICKET_COUNTS[Pri]); + # endif + #endif + Thread->Priority = Pri; + SHORTREL( &glThreadListLock ); + } + else + Thread->Priority = Pri; + #endif + + #if DEBUG_TRACE_STATE + Log("Threads_SetPriority: %p(%i %s) pri set %i", + Thread, Thread->TID, Thread->ThreadName, + Pri); + #endif +} + +/** + * \brief Clone the TCB of the current thread + * \param Flags Flags for something... (What is this for?) + */ +tThread *Threads_CloneTCB(Uint Flags) +{ + tThread *cur, *new; + cur = Proc_GetCurThread(); + + // Allocate and duplicate + new = malloc(sizeof(tThread)); + if(new == NULL) { errno = -ENOMEM; return NULL; } + memcpy(new, cur, sizeof(tThread)); + + new->CurCPU = -1; + new->Next = NULL; + memset( &new->IsLocked, 0, sizeof(new->IsLocked)); + new->Status = THREAD_STAT_PREINIT; + new->RetStatus = 0; + + // Get Thread ID + new->TID = giNextTID++; + new->Parent = cur; + new->bInstrTrace = 0; + + // Clone Name + new->ThreadName = strdup(cur->ThreadName); + + // Set Thread Group ID (PID) + if(Flags & CLONE_VM) { + tProcess *newproc, *oldproc; + oldproc = cur->Process; + new->Process = malloc( sizeof(struct sProcess) ); + newproc = new->Process; + newproc->PID = new->TID; + newproc->UID = oldproc->UID; + newproc->GID = oldproc->GID; + newproc->MaxFD = oldproc->MaxFD; + if( oldproc->CurrentWorkingDir ) + newproc->CurrentWorkingDir = strdup( oldproc->CurrentWorkingDir ); + else + newproc->CurrentWorkingDir = NULL; + if( oldproc->RootDir ) + newproc->RootDir = strdup( oldproc->RootDir ); + else + newproc->RootDir = NULL; + newproc->nThreads = 1; + // Reference all handles in the VFS + VFS_ReferenceUserHandles(); + } + else { + new->Process->nThreads ++; + } + + // Messages are not inherited + new->Messages = NULL; + new->LastMessage = NULL; + + // Set State + new->Remaining = new->Quantum = cur->Quantum; + new->Priority = cur->Priority; + new->_errno = 0; + + // Set Signal Handlers + new->CurFaultNum = 0; + new->FaultHandler = cur->FaultHandler; + + // Maintain a global list of threads + SHORTLOCK( &glThreadListLock ); + new->GlobalPrev = NULL; // Protect against bugs + new->GlobalNext = gAllThreads; + gAllThreads->GlobalPrev = new; + gAllThreads = new; + SHORTREL( &glThreadListLock ); + + return new; +} + +/** + * \brief Clone the TCB of the kernel thread + */ +tThread *Threads_CloneThreadZero(void) +{ + tThread *new; + + // Allocate and duplicate + new = malloc(sizeof(tThread)); + if(new == NULL) { + return NULL; + } + memcpy(new, &gThreadZero, sizeof(tThread)); + + new->Process->nThreads ++; + + new->CurCPU = -1; + new->Next = NULL; + memset( &new->IsLocked, 0, sizeof(new->IsLocked)); + new->Status = THREAD_STAT_PREINIT; + new->RetStatus = 0; + + // Get Thread ID + new->TID = giNextTID++; + new->Parent = 0; + + // Clone Name + new->ThreadName = NULL; + + // Messages are not inherited + new->Messages = NULL; + new->LastMessage = NULL; + + // Set State + new->Remaining = new->Quantum = DEFAULT_QUANTUM; + new->Priority = DEFAULT_PRIORITY; + new->bInstrTrace = 0; + + // Set Signal Handlers + new->CurFaultNum = 0; + new->FaultHandler = 0; + + // Maintain a global list of threads + SHORTLOCK( &glThreadListLock ); + new->GlobalPrev = NULL; // Protect against bugs + new->GlobalNext = gAllThreads; + gAllThreads->GlobalPrev = new; + gAllThreads = new; + SHORTREL( &glThreadListLock ); + + return new; +} + +/** + * \brief Wait for a task to change state + * \param TID Thread ID to wait on (-1: Any child thread, 0: Any Child/Sibling, <-1: -PID) + * \param Status Thread return status + * \return TID of child that changed state + */ +tTID Threads_WaitTID(int TID, int *Status) +{ + // Any Child + if(TID == -1) { + Log_Error("Threads", "TODO: Threads_WaitTID(TID=-1) - Any Child"); + return -1; + } + + // Any peer/child thread + if(TID == 0) { + Log_Error("Threads", "TODO: Threads_WaitTID(TID=0) - Any Child/Sibling"); + return -1; + } + + // TGID = abs(TID) + if(TID < -1) { + Log_Error("Threads", "TODO: Threads_WaitTID(TID<0) - TGID"); + return -1; + } + + // Specific Thread + if(TID > 0) { + tThread *t = Threads_GetThread(TID); + tTID ret; + + // Wait for the thread to die! + // TODO: Handle child also being suspended if wanted + while(t->Status != THREAD_STAT_ZOMBIE) { + Threads_Sleep(); + Log_Debug("Threads", "%i waiting for %i, t->Status = %i", + Threads_GetTID(), t->TID, t->Status); + } + + // Set return status + ret = t->TID; + switch(t->Status) + { + case THREAD_STAT_ZOMBIE: + // Kill the thread + t->Status = THREAD_STAT_DEAD; + // TODO: Child return value? + if(Status) *Status = t->RetStatus; + // add to delete queue + Threads_Delete( t ); + break; + default: + if(Status) *Status = -1; + break; + } + return ret; + } + + return -1; +} + +/** + * \brief Gets a thread given its TID + * \param TID Thread ID + * \return Thread pointer + */ +tThread *Threads_GetThread(Uint TID) +{ + tThread *thread; + + // Search global list + for(thread = gAllThreads; + thread; + thread = thread->GlobalNext) + { + if(thread->TID == TID) + return thread; + } + + Log("Unable to find TID %i on main list\n", TID); + + return NULL; +} + +/** + * \brief Deletes an entry from a list + * \param List Pointer to the list head + * \param Thread Thread to find + * \return \a Thread + */ +tThread *Threads_int_DelFromQueue(tThreadList *List, tThread *Thread) +{ + tThread *ret, *prev = NULL; + + for(ret = List->Head; + ret && ret != Thread; + prev = ret, ret = ret->Next + ); + + // Is the thread on the list + if(!ret) { + //LogF("%p(%s) is not on list %p\n", Thread, Thread->ThreadName, List); + return NULL; + } + + if( !prev ) { + List->Head = Thread->Next; + //LogF("%p(%s) removed from head of %p\n", Thread, Thread->ThreadName, List); + } + else { + prev->Next = Thread->Next; + //LogF("%p(%s) removed from %p (prev=%p)\n", Thread, Thread->ThreadName, List, prev); + } + if( Thread->Next == NULL ) + List->Tail = prev; + + return Thread; +} + +void Threads_int_AddToList(tThreadList *List, tThread *Thread) +{ + if( List->Head ) + List->Tail->Next = Thread; + else + List->Head = Thread; + List->Tail = Thread; + Thread->Next = NULL; +} + +/** + * \brief Exit the current process (or another?) + * \param TID Thread ID to kill + * \param Status Exit status + */ +void Threads_Exit(int TID, int Status) +{ + if( TID == 0 ) + Threads_Kill( Proc_GetCurThread(), (Uint)Status & 0xFF ); + else + Threads_Kill( Threads_GetThread(TID), (Uint)Status & 0xFF ); + + // Halt forever, just in case + for(;;) HALT(); +} + +/** + * \fn void Threads_Kill(tThread *Thread, int Status) + * \brief Kill a thread + * \param Thread Thread to kill + * \param Status Status code to return to the parent + */ +void Threads_Kill(tThread *Thread, int Status) +{ + tMsg *msg; + int isCurThread = Thread == Proc_GetCurThread(); + + // TODO: Disown all children? + #if 1 + { + tThread *child; + // TODO: I should keep a .Children list + for(child = gAllThreads; + child; + child = child->GlobalNext) + { + if(child->Parent == Thread) + child->Parent = &gThreadZero; + } + } + #endif + + ///\note Double lock is needed due to overlap of lock areas + + // Lock thread (stop us recieving messages) + SHORTLOCK( &Thread->IsLocked ); + + // Clear Message Queue + while( Thread->Messages ) + { + msg = Thread->Messages->Next; + free( Thread->Messages ); + Thread->Messages = msg; + } + + // Lock thread list + SHORTLOCK( &glThreadListLock ); + + switch(Thread->Status) + { + case THREAD_STAT_PREINIT: // Only on main list + break; + + // Currently active thread + case THREAD_STAT_ACTIVE: + if( Thread != Proc_GetCurThread() ) + { + #if SCHEDULER_TYPE == SCHED_RR_PRI + tThreadList *list = &gaActiveThreads[Thread->Priority]; + #else + tThreadList *list = &gActiveThreads; + #endif + if( Threads_int_DelFromQueue( list, Thread ) ) + { + } + else + { + Log_Warning("Threads", + "Threads_Kill - Thread %p(%i,%s) marked as active, but not on list", + Thread, Thread->TID, Thread->ThreadName + ); + } + #if SCHEDULER_TYPE == SCHED_LOTTERY + giFreeTickets -= caiTICKET_COUNTS[ Thread->Priority ]; + #endif + } + // Ensure that we are not rescheduled + Thread->Remaining = 0; // Clear Remaining Quantum + Thread->Quantum = 0; // Clear Quantum to indicate dead thread + + // Update bookkeeping + giNumActiveThreads --; + break; + // Kill it while it sleeps! + case THREAD_STAT_SLEEPING: + if( !Threads_int_DelFromQueue( &gSleepingThreads, Thread ) ) + { + Log_Warning("Threads", + "Threads_Kill - Thread %p(%i,%s) marked as sleeping, but not on list", + Thread, Thread->TID, Thread->ThreadName + ); + } + break; + + // Brains!... You cannot kill something that is already dead + case THREAD_STAT_ZOMBIE: + Log_Warning("Threads", "Threads_Kill - Thread %p(%i,%s) is undead, you cannot kill it", + Thread, Thread->TID, Thread->ThreadName); + SHORTREL( &glThreadListLock ); + SHORTREL( &Thread->IsLocked ); + return ; + + default: + Log_Warning("Threads", "Threads_Kill - BUG Un-checked status (%i)", + Thread->Status); + break; + } + + // Save exit status + Thread->RetStatus = Status; + + SHORTREL( &Thread->IsLocked ); + + Thread->Status = THREAD_STAT_ZOMBIE; + SHORTREL( &glThreadListLock ); + // TODO: Send something like SIGCHLD + Threads_Wake( Thread->Parent ); + + Log("Thread %i went *hurk* (%i)", Thread->TID, Status); + + // And, reschedule + if(isCurThread) + { + for( ;; ) + Proc_Reschedule(); + } +} + +/** + * \brief Yield remainder of the current thread's timeslice + */ +void Threads_Yield(void) +{ +// Log("Threads_Yield: by %p", __builtin_return_address(0)); + Proc_Reschedule(); +} + +/** + * \fn void Threads_Sleep(void) + * \brief Take the current process off the run queue + */ +void Threads_Sleep(void) +{ + tThread *cur = Proc_GetCurThread(); + + // Acquire Spinlock + SHORTLOCK( &glThreadListLock ); + + // Don't sleep if there is a message waiting + if( cur->Messages ) { + SHORTREL( &glThreadListLock ); + return; + } + + // Remove us from running queue + Threads_RemActive(); + // Mark thread as sleeping + cur->Status = THREAD_STAT_SLEEPING; + + // Add to Sleeping List (at the top) + Threads_int_AddToList( &gSleepingThreads, cur ); + + #if DEBUG_TRACE_STATE + Log("Threads_Sleep: %p (%i %s) sleeping", cur, cur->TID, cur->ThreadName); + #endif + + // Release Spinlock + SHORTREL( &glThreadListLock ); + + while(cur->Status != THREAD_STAT_ACTIVE) { + Proc_Reschedule(); + if( cur->Status != THREAD_STAT_ACTIVE ) + Log("%i - Huh? why am I up? zzzz...", cur->TID); + } +} + + +/** + * \brief Wakes a sleeping/waiting thread up + * \param Thread Thread to wake + * \return Boolean Failure (Returns ERRNO) + * \warning This should ONLY be called with task switches disabled + */ +int Threads_Wake(tThread *Thread) +{ + if(!Thread) + return -EINVAL; + + switch(Thread->Status) + { + case THREAD_STAT_ACTIVE: + Log("Threads_Wake - Waking awake thread (%i)", Thread->TID); + return -EALREADY; + + case THREAD_STAT_SLEEPING: + SHORTLOCK( &glThreadListLock ); + // Remove from sleeping queue + Threads_int_DelFromQueue(&gSleepingThreads, Thread); + + SHORTREL( &glThreadListLock ); + Threads_AddActive( Thread ); + + #if DEBUG_TRACE_STATE + Log("Threads_Sleep: %p (%i %s) woken", Thread, Thread->TID, Thread->ThreadName); + #endif + return -EOK; + + case THREAD_STAT_SEMAPHORESLEEP: { + tSemaphore *sem; + tThread *th, *prev=NULL; + + sem = Thread->WaitPointer; + + SHORTLOCK( &sem->Protector ); + + // Remove from sleeping queue + for( th = sem->Waiting; th; prev = th, th = th->Next ) + if( th == Thread ) break; + if( th ) + { + if(prev) + prev->Next = Thread->Next; + else + sem->Waiting = Thread->Next; + if(sem->LastWaiting == Thread) + sem->LastWaiting = prev; + } + else + { + prev = NULL; + for( th = sem->Signaling; th; prev = th, th = th->Next ) + if( th == Thread ) break; + if( !th ) { + Log_Warning("Threads", "Thread %p(%i %s) is not on semaphore %p(%s:%s)", + Thread, Thread->TID, Thread->ThreadName, + sem, sem->ModName, sem->Name); + return -EINTERNAL; + } + + if(prev) + prev->Next = Thread->Next; + else + sem->Signaling = Thread->Next; + if(sem->LastSignaling == Thread) + sem->LastSignaling = prev; + } + + Thread->RetStatus = 0; // It didn't get anything + Threads_AddActive( Thread ); + + #if DEBUG_TRACE_STATE + Log("Threads_Sleep: %p(%i %s) woken from semaphore", Thread, Thread->TID, Thread->ThreadName); + #endif + SHORTREL( &sem->Protector ); + } return -EOK; + + case THREAD_STAT_WAITING: + Warning("Threads_Wake - Waiting threads are not currently supported"); + return -ENOTIMPL; + + case THREAD_STAT_DEAD: + Warning("Threads_Wake - Attempt to wake dead thread (%i)", Thread->TID); + return -ENOTIMPL; + + default: + Warning("Threads_Wake - Unknown process status (%i)\n", Thread->Status); + return -EINTERNAL; + } +} + +/** + * \brief Wake a thread given the TID + * \param TID Thread ID to wake + * \return Boolean Faulure (errno) + */ +int Threads_WakeTID(tTID TID) +{ + tThread *thread = Threads_GetThread(TID); + int ret; + if(!thread) + return -ENOENT; + ret = Threads_Wake( thread ); + //Log_Debug("Threads", "TID %i woke %i (%p)", Threads_GetTID(), TID, thread); + return ret; +} + +void Threads_ToggleTrace(int TID) +{ + tThread *thread = Threads_GetThread(TID); + if(!thread) return ; + thread->bInstrTrace = !thread->bInstrTrace; +} + +/** + * \brief Adds a thread to the active queue + */ +void Threads_AddActive(tThread *Thread) +{ + SHORTLOCK( &glThreadListLock ); + + if( Thread->Status == THREAD_STAT_ACTIVE ) { + tThread *cur = Proc_GetCurThread(); + Log_Warning("Threads", "WTF, %p CPU%i %p (%i %s) is adding %p (%i %s) when it is active", + __builtin_return_address(0), + GetCPUNum(), cur, cur->TID, cur->ThreadName, Thread, Thread->TID, Thread->ThreadName); + SHORTREL( &glThreadListLock ); + return ; + } + + // Set state + Thread->Status = THREAD_STAT_ACTIVE; +// Thread->CurCPU = -1; + // Add to active list + { + #if SCHEDULER_TYPE == SCHED_RR_PRI + tThreadList *list = &gaActiveThreads[Thread->Priority]; + #else + tThreadList *list = &gActiveThreads; + #endif + Threads_int_AddToList( list, Thread ); + } + + // Update bookkeeping + giNumActiveThreads ++; + + #if SCHEDULER_TYPE == SCHED_LOTTERY + { + int delta; + // Only change the ticket count if the thread is un-scheduled + if(Thread->CurCPU != -1) + delta = 0; + else + delta = caiTICKET_COUNTS[ Thread->Priority ]; + + giFreeTickets += delta; + # if DEBUG_TRACE_TICKETS + Log("CPU%i %p (%i %s) added, new giFreeTickets = %i [+%i]", + GetCPUNum(), Thread, Thread->TID, Thread->ThreadName, + giFreeTickets, delta + ); + # endif + } + #endif + + SHORTREL( &glThreadListLock ); +} + +/** + * \brief Removes the current thread from the active queue + * \warning This should ONLY be called with the lock held + * \return Current thread pointer + */ +tThread *Threads_RemActive(void) +{ + #if 0 + tThread *ret = Proc_GetCurThread(); + + if( !IS_LOCKED(&glThreadListLock) ) { + Log_KernelPanic("Threads", "Threads_RemActive called without lock held"); + return NULL; + } + + // Delete from active queue + #if SCHEDULER_TYPE == SCHED_RR_PRI + if( !Threads_int_DelFromQueue(&gaActiveThreads[ret->Priority], ret) ) + #else + if( !Threads_int_DelFromQueue(&gActiveThreads, ret) ) + #endif + { + Log_Warning("Threads", "Current thread %p(%i %s) is not on active queue", + ret, ret->TID, ret->ThreadName + ); + return NULL; + } + + ret->Next = NULL; + ret->Remaining = 0; + + giNumActiveThreads --; + // no need to decrement tickets, scheduler did it for us + + #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS + Log("CPU%i %p (%i %s) removed, giFreeTickets = %i [nc]", + GetCPUNum(), ret, ret->TID, ret->ThreadName, giFreeTickets); + #endif + + return ret; + #else + return Proc_GetCurThread(); + #endif +} + +/** + * \fn void Threads_SetFaultHandler(Uint Handler) + * \brief Sets the signal handler for a signal + */ +void Threads_SetFaultHandler(Uint Handler) +{ + //Log_Debug("Threads", "Threads_SetFaultHandler: Handler = %p", Handler); + Proc_GetCurThread()->FaultHandler = Handler; +} + +/** + * \fn void Threads_Fault(int Num) + * \brief Calls a fault handler + */ +void Threads_Fault(int Num) +{ + tThread *thread = Proc_GetCurThread(); + + if(!thread) return ; + + Log_Log("Threads", "Threads_Fault: thread->FaultHandler = %p", thread->FaultHandler); + + switch(thread->FaultHandler) + { + case 0: // Panic? + Threads_Kill(thread, -1); + HALT(); + return ; + case 1: // Dump Core? + Threads_Kill(thread, -1); + HALT(); + return ; + } + + // Double Fault? Oh, F**k + if(thread->CurFaultNum != 0) { + Log_Warning("Threads", "Threads_Fault: Double fault on %i", thread->TID); + Threads_Kill(thread, -1); // For now, just kill + HALT(); + } + + thread->CurFaultNum = Num; + + Proc_CallFaultHandler(thread); +} + +/** + * \fn void Threads_SegFault(tVAddr Addr) + * \brief Called when a Segment Fault occurs + */ +void Threads_SegFault(tVAddr Addr) +{ + tThread *cur = Proc_GetCurThread(); + cur->bInstrTrace = 0; + Log_Warning("Threads", "Thread #%i committed a segfault at address %p", cur->TID, Addr); + MM_DumpTables(0, USER_MAX); + Threads_Fault( 1 ); + //Threads_Exit( 0, -1 ); +} + +// --- Process Structure Access Functions --- +tPID Threads_GetPID(void) +{ + return Proc_GetCurThread()->Process->PID; +} +tTID Threads_GetTID(void) +{ + return Proc_GetCurThread()->TID; +} +tUID Threads_GetUID(void) +{ + return Proc_GetCurThread()->Process->UID; +} +tGID Threads_GetGID(void) +{ + return Proc_GetCurThread()->Process->GID; +} + +int Threads_SetUID(tUID ID) +{ + tThread *t = Proc_GetCurThread(); + if( t->Process->UID != 0 ) { + errno = -EACCES; + return -1; + } + Log_Debug("Threads", "PID %i's UID set to %i", t->Process->PID, ID); + t->Process->UID = ID; + return 0; +} + +int Threads_SetGID(tGID ID) +{ + tThread *t = Proc_GetCurThread(); + if( t->Process->UID != 0 ) { + errno = -EACCES; + return -1; + } + Log_Debug("Threads", "PID %i's GID set to %i", t->Process->PID, ID); + t->Process->GID = ID; + return 0; +} + +// --- Per-thread storage --- +int *Threads_GetErrno(void) +{ + return &Proc_GetCurThread()->_errno; +} + +// --- Configuration --- +int *Threads_GetMaxFD(void) +{ + return &Proc_GetCurThread()->Process->MaxFD; +} +char **Threads_GetChroot(void) +{ + return &Proc_GetCurThread()->Process->RootDir; +} +char **Threads_GetCWD(void) +{ + return &Proc_GetCurThread()->Process->CurrentWorkingDir; +} +// --- + +/** + * \fn void Threads_Dump(void) + */ +void Threads_DumpActive(void) +{ + tThread *thread; + tThreadList *list; + #if SCHEDULER_TYPE == SCHED_RR_PRI + int i; + #endif + + Log("Active Threads: (%i reported)", giNumActiveThreads); + + #if SCHEDULER_TYPE == SCHED_RR_PRI + for( i = 0; i < MIN_PRIORITY+1; i++ ) + { + list = &gaActiveThreads[i]; + #else + list = &gActiveThreads; + #endif + for(thread=list->Head;thread;thread=thread->Next) + { + Log(" %p %i (%i) - %s (CPU %i)", + thread, thread->TID, thread->Process->PID, thread->ThreadName, thread->CurCPU); + if(thread->Status != THREAD_STAT_ACTIVE) + Log(" ERROR State (%i) != THREAD_STAT_ACTIVE (%i)", + thread->Status, THREAD_STAT_ACTIVE); + Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum); + Log(" KStack 0x%x", thread->KernelStack); + if( thread->bInstrTrace ) + Log(" Tracing Enabled"); + Proc_DumpThreadCPUState(thread); + } + + #if SCHEDULER_TYPE == SCHED_RR_PRI + } + #endif +} + +/** + * \fn void Threads_Dump(void) + * \brief Dumps a list of currently running threads + */ +void Threads_Dump(void) +{ + tThread *thread; + + Log("--- Thread Dump ---"); + Threads_DumpActive(); + + Log("All Threads:"); + for(thread=gAllThreads;thread;thread=thread->GlobalNext) + { + Log(" %p %i (%i) - %s (CPU %i)", + thread, thread->TID, thread->Process->PID, thread->ThreadName, thread->CurCPU); + Log(" State %i (%s)", thread->Status, casTHREAD_STAT[thread->Status]); + switch(thread->Status) + { + case THREAD_STAT_MUTEXSLEEP: + Log(" Mutex Pointer: %p", thread->WaitPointer); + break; + case THREAD_STAT_SEMAPHORESLEEP: + Log(" Semaphore Pointer: %p", thread->WaitPointer); + Log(" Semaphore Name: %s:%s", + ((tSemaphore*)thread->WaitPointer)->ModName, + ((tSemaphore*)thread->WaitPointer)->Name + ); + break; + case THREAD_STAT_ZOMBIE: + Log(" Return Status: %i", thread->RetStatus); + break; + default: break; + } + Log(" Priority %i, Quantum %i", thread->Priority, thread->Quantum); + Log(" KStack 0x%x", thread->KernelStack); + if( thread->bInstrTrace ) + Log(" Tracing Enabled"); + Proc_DumpThreadCPUState(thread); + } +} + +/** + * \brief Gets the next thread to run + * \param CPU Current CPU + * \param Last The thread the CPU was running + */ +tThread *Threads_GetNextToRun(int CPU, tThread *Last) +{ + tThread *thread; + + // If this CPU has the lock, we must let it complete + if( CPU_HAS_LOCK( &glThreadListLock ) ) + return Last; + + // Don't change threads if the current CPU has switches disabled + if( gaThreads_NoTaskSwitch[CPU] ) + return Last; + + // Lock thread list + SHORTLOCK( &glThreadListLock ); + + // Make sure the current (well, old) thread is marked as de-scheduled + if(Last) Last->CurCPU = -1; + + // No active threads, just take a nap + if(giNumActiveThreads == 0) { + SHORTREL( &glThreadListLock ); + #if DEBUG_TRACE_TICKETS + Log("No active threads"); + #endif + return NULL; + } + + #if 0 + #if SCHEDULER_TYPE != SCHED_RR_PRI + // Special case: 1 thread + if(giNumActiveThreads == 1) { + if( gActiveThreads.Head->CurCPU == -1 ) + gActiveThreads.Head->CurCPU = CPU; + + SHORTREL( &glThreadListLock ); + + if( gActiveThreads.Head->CurCPU == CPU ) + return gActiveThreads.Head; + + return NULL; // CPU has nothing to do + } + #endif + #endif + + // Allow the old thread to be scheduled again + if( Last ) { + if( Last->Status == THREAD_STAT_ACTIVE ) { + tThreadList *list; + #if SCHEDULER_TYPE == SCHED_LOTTERY + giFreeTickets += caiTICKET_COUNTS[ Last->Priority ]; + # if DEBUG_TRACE_TICKETS + LogF("Log: CPU%i released %p (%i %s) into the pool (%i [+%i] tickets in pool)\n", + CPU, Last, Last->TID, Last->ThreadName, giFreeTickets, + caiTICKET_COUNTS[ Last->Priority ]); + # endif + #endif + + #if SCHEDULER_TYPE == SCHED_RR_PRI + list = &gaActiveThreads[ Last->Priority ]; + #else + list = &gActiveThreads; + #endif + // Add to end of list + Threads_int_AddToList( list, Last ); + } + #if SCHEDULER_TYPE == SCHED_LOTTERY && DEBUG_TRACE_TICKETS + else + LogF("Log: CPU%i released %p (%i %s)->Status = %i (Released,not in pool)\n", + CPU, Last, Last->TID, Last->ThreadName, Last->Status); + #endif + Last->CurCPU = -1; + } + + // --- + // Lottery Scheduler + // --- + #if SCHEDULER_TYPE == SCHED_LOTTERY + { + int ticket, number; + # if 1 + number = 0; + for(thread = gActiveThreads.Head; thread; thread = thread->Next) + { + if(thread->Status != THREAD_STAT_ACTIVE) + Panic("Bookkeeping fail - %p %i(%s) is on the active queue with a status of %i", + thread, thread->TID, thread->ThreadName, thread->Status); + if(thread->Next == thread) { + Panic("Bookkeeping fail - %p %i(%s) loops back on itself", + thread, thread->TID, thread->ThreadName, thread->Status); + } + number += caiTICKET_COUNTS[ thread->Priority ]; + } + if(number != giFreeTickets) { + Panic("Bookkeeping fail (giFreeTickets(%i) != number(%i)) - CPU%i", + giFreeTickets, number, CPU); + } + # endif + + // No free tickets (all tasks delegated to cores) + if( giFreeTickets == 0 ) { + SHORTREL(&glThreadListLock); + return NULL; + } + + // Get the ticket number + ticket = number = rand() % giFreeTickets; + + // Find the next thread + for(thread = gActiveThreads.Head; thread; prev = thread, thread = thread->Next ) + { + if( caiTICKET_COUNTS[ thread->Priority ] > number) break; + number -= caiTICKET_COUNTS[ thread->Priority ]; + } + + // If we didn't find a thread, something went wrong + if(thread == NULL) + { + number = 0; + for(thread=gActiveThreads;thread;thread=thread->Next) { + if(thread->CurCPU >= 0) continue; + number += caiTICKET_COUNTS[ thread->Priority ]; + } + Panic("Bookeeping Failed - giFreeTickets(%i) > true count (%i)", + giFreeTickets, number); + } + + // Remove + if(prev) + prev->Next = thread->Next; + else + gActiveThreads.Head = thread->Next; + if(!thread->Next) + gActiveThreads.Tail = prev; + + giFreeTickets -= caiTICKET_COUNTS[ thread->Priority ]; + # if DEBUG_TRACE_TICKETS + LogF("Log: CPU%i allocated %p (%i %s), (%i [-%i] tickets in pool), \n", + CPU, thread, thread->TID, thread->ThreadName, + giFreeTickets, caiTICKET_COUNTS[ thread->Priority ]); + # endif + } + + // --- + // Priority based round robin scheduler + // --- + #elif SCHEDULER_TYPE == SCHED_RR_PRI + { + int i; + thread = NULL; + for( i = 0; i < MIN_PRIORITY + 1; i ++ ) + { + if( !gaActiveThreads[i].Head ) + continue ; + + thread = gaActiveThreads[i].Head; + + // Remove from head + gaActiveThreads[i].Head = thread->Next; + if(!thread->Next) + gaActiveThreads[i].Tail = NULL; + thread->Next = NULL; + break; + } + + // Anything to do? + if( !thread ) { + SHORTREL(&glThreadListLock); + return NULL; + } + if( thread->Status != THREAD_STAT_ACTIVE ) { + LogF("Oops, Thread %i (%s) is not active\n", thread->TID, thread->ThreadName); + } + } + #elif SCHEDULER_TYPE == SCHED_RR_SIM + { + // Get the next thread off the list + thread = gActiveThreads.Head; + gActiveThreads.Head = thread->Next; + if(!thread->Next) + gaActiveThreads.Tail = NULL; + thread->Next = NULL; + + // Anything to do? + if( !thread ) { + SHORTREL(&glThreadListLock); + return NULL; + } + } + #else + # error "Unimplemented scheduling algorithm" + #endif + + // Make the new thread non-schedulable + thread->CurCPU = CPU; + thread->Remaining = thread->Quantum; + + SHORTREL( &glThreadListLock ); + + return thread; +} + +// === EXPORTS === +EXPORT(Threads_GetUID); +EXPORT(Threads_GetGID); diff --git a/KernelLand/Kernel/time.c b/KernelLand/Kernel/time.c new file mode 100644 index 00000000..2c83fa32 --- /dev/null +++ b/KernelLand/Kernel/time.c @@ -0,0 +1,114 @@ +/* + * Acess 2 + * - By John Hodge (thePowersGang) + * + * Timer Code + */ +#include +#include +#include +#include // Proc_GetCurThread + +// === CONSTANTS === +#define NUM_TIMERS 8 + +// === TYPEDEFS === +struct sTimer { + tTimer *Next; + Sint64 FiresAfter; + void (*Callback)(void*); + void *Argument; +}; + +// === PROTOTYPES === +void Timer_CallTimers(void); + +// === GLOBALS === +volatile Uint64 giTicks = 0; +volatile Sint64 giTimestamp = 0; +volatile Uint64 giPartMiliseconds = 0; +tTimer *gTimers; // TODO: Replace by a ring-list timer + +// === CODE === +/** + * \fn void Timer_CallTimers() + */ +void Timer_CallTimers() +{ + while( gTimers && gTimers->FiresAfter < now() ) + { + tTimer *next; + + if( gTimers->Callback ) + gTimers->Callback(gTimers->Argument); + else + Threads_PostEvent(gTimers->Argument, THREAD_EVENT_TIMER); + + next = gTimers->Next; + free(gTimers); + gTimers = next; + } +} + +/** + * \brief Schedule an action + */ +tTimer *Time_CreateTimer(int Delta, tTimerCallback *Callback, void *Argument) +{ + tTimer *ret; + tTimer *t, *p; + + if(Callback == NULL) + Argument = Proc_GetCurThread(); + + // TODO: Use a pool instead? + ret = malloc(sizeof(tTimer)); + + ret->Callback = Callback; + ret->FiresAfter = now() + Delta; + ret->Argument = Argument; + + // Add into list (sorted) + for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next ) + { + if( t->FiresAfter > ret->FiresAfter ) break; + } + ret->Next = t; + p->Next = ret; + + return ret; +} + +/** + * \brief Delete a timer + */ +void Time_RemoveTimer(tTimer *Timer) +{ + tTimer *t, *p; + for( p = (tTimer*)&gTimers, t = gTimers; t; p = t, t = t->Next ) + { + if( t == Timer ) + { + p->Next = t->Next; + free(Timer); + return ; + } + } +} + +/** + * \fn void Time_Delay(int Delay) + * \brief Delay for a small ammount of time + */ +void Time_Delay(int Delay) +{ +// tTime dest = now() + Delay; +// while(dest > now()) Threads_Yield(); + Time_CreateTimer(Delay, NULL, NULL); + Threads_WaitEvents(THREAD_EVENT_TIMER); +} + +// === EXPORTS === +EXPORT(Time_CreateTimer); +EXPORT(Time_RemoveTimer); +EXPORT(Time_Delay); diff --git a/KernelLand/Kernel/vfs/acls.c b/KernelLand/Kernel/vfs/acls.c new file mode 100644 index 00000000..ab88b985 --- /dev/null +++ b/KernelLand/Kernel/vfs/acls.c @@ -0,0 +1,157 @@ +/* + * Acess Micro VFS + */ +#include +#include "vfs.h" +#include "vfs_int.h" + +// === GLOBALS === +tVFS_ACL gVFS_ACL_EveryoneRWX = { {1,-1}, {0,VFS_PERM_ALL} }; +tVFS_ACL gVFS_ACL_EveryoneRW = { {1,-1}, {0,VFS_PERM_ALL^VFS_PERM_EXECUTE} }; +tVFS_ACL gVFS_ACL_EveryoneRX = { {1,-1}, {0,VFS_PERM_READ|VFS_PERM_EXECUTE} }; +tVFS_ACL gVFS_ACL_EveryoneRO = { {1,-1}, {0,VFS_PERM_READ} }; + +// === CODE === +/** + * \fn int VFS_CheckACL(tVFS_Node *Node, Uint Permissions) + * \brief Checks the permissions on a file + */ +int VFS_CheckACL(tVFS_Node *Node, Uint Permissions) +{ + int i; + int uid = Threads_GetUID(); + int gid = Threads_GetGID(); + + // Root can do anything + if(uid == 0) return 1; + + // Root only file?, fast return + if( Node->NumACLs == 0 ) { + Log("VFS_CheckACL - %p inaccesable, NumACLs = 0, uid=%i", Node, uid); + return 0; + } + + // Check Deny Permissions + for(i=0;iNumACLs;i++) + { + if(!Node->ACLs[i].Inv) continue; // Ignore ALLOWs + if(Node->ACLs[i].ID != 0x7FFFFFFF) + { + if(!Node->ACLs[i].Group && Node->ACLs[i].ID != uid) continue; + if(Node->ACLs[i].Group && Node->ACLs[i].ID != gid) continue; + } + + //Log("Deny %x", Node->ACLs[i].Perms); + + if(Node->ACLs[i].Perms & Permissions) { + Log("VFS_CheckACL - %p inaccesable, %x denied", + Node, Node->ACLs[i].Perms & Permissions); + return 0; + } + } + + // Check for allow permissions + for(i=0;iNumACLs;i++) + { + if(Node->ACLs[i].Inv) continue; // Ignore DENYs + if(Node->ACLs[i].ID != 0x7FFFFFFF) + { + if(!Node->ACLs[i].Group && Node->ACLs[i].ID != uid) continue; + if(Node->ACLs[i].Group && Node->ACLs[i].ID != gid) continue; + } + + //Log("Allow %x", Node->ACLs[i].Perms); + + if((Node->ACLs[i].Perms & Permissions) == Permissions) return 1; + } + + Log("VFS_CheckACL - %p inaccesable, %x not allowed", Node, Permissions); + return 0; +} +/** + * \fn int VFS_GetACL(int FD, tVFS_ACL *Dest) + */ +int VFS_GetACL(int FD, tVFS_ACL *Dest) +{ + int i; + tVFS_Handle *h = VFS_GetHandle(FD); + + // Error check + if(!h) { + return -1; + } + + // Root can do anything + if(Dest->Group == 0 && Dest->ID == 0) { + Dest->Inv = 0; + Dest->Perms = -1; + return 1; + } + + // Root only file?, fast return + if( h->Node->NumACLs == 0 ) { + Dest->Inv = 0; + Dest->Perms = 0; + return 0; + } + + // Check Deny Permissions + for(i=0;iNode->NumACLs;i++) + { + if(h->Node->ACLs[i].Group != Dest->Group) continue; + if(h->Node->ACLs[i].ID != Dest->ID) continue; + + Dest->Inv = h->Node->ACLs[i].Inv; + Dest->Perms = h->Node->ACLs[i].Perms; + return 1; + } + + + Dest->Inv = 0; + Dest->Perms = 0; + return 0; +} + +/** + * \fn tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group) + * \brief Converts UNIX permissions to three Acess ACL entries + */ +tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group) +{ + tVFS_ACL *ret = malloc(sizeof(tVFS_ACL)*3); + + // Error Check + if(!ret) return NULL; + + // Owner + ret[0].Group = 0; ret[0].ID = Owner; + ret[0].Inv = 0; ret[0].Perms = 0; + if(Mode & 0400) ret[0].Perms |= VFS_PERM_READ; + if(Mode & 0200) ret[0].Perms |= VFS_PERM_WRITE; + if(Mode & 0100) ret[0].Perms |= VFS_PERM_EXECUTE; + + // Group + ret[1].Group = 1; ret[1].ID = Group; + ret[1].Inv = 0; ret[1].Perms = 0; + if(Mode & 0040) ret[1].Perms |= VFS_PERM_READ; + if(Mode & 0020) ret[1].Perms |= VFS_PERM_WRITE; + if(Mode & 0010) ret[1].Perms |= VFS_PERM_EXECUTE; + + // Global + ret[2].Group = 1; ret[2].ID = -1; + ret[2].Inv = 0; ret[2].Perms = 0; + if(Mode & 0004) ret[2].Perms |= VFS_PERM_READ; + if(Mode & 0002) ret[2].Perms |= VFS_PERM_WRITE; + if(Mode & 0001) ret[2].Perms |= VFS_PERM_EXECUTE; + + // Return buffer + return ret; +} + +// === EXPORTS === +// --- Variables --- +EXPORTV(gVFS_ACL_EveryoneRWX); +EXPORTV(gVFS_ACL_EveryoneRW); +EXPORTV(gVFS_ACL_EveryoneRX); +// --- Functions --- +EXPORT(VFS_UnixToAcessACL); diff --git a/KernelLand/Kernel/vfs/dir.c b/KernelLand/Kernel/vfs/dir.c new file mode 100644 index 00000000..d4c15309 --- /dev/null +++ b/KernelLand/Kernel/vfs/dir.c @@ -0,0 +1,192 @@ +/* + * Acess2 VFS + * - Directory Management Functions + */ +#define DEBUG 0 +#include +#include +#include + +// === IMPORTS === +extern tVFS_Mount *gRootMount; + +// === PROTOTYPES === +#if 0 + int VFS_MkDir(const char *Path); + int VFS_MkNod(const char *Path, Uint Flags); + int VFS_Symlink(const char *Name, const char *Link); +#endif + +// === CODE === +/** + * \fn int VFS_MkDir(char *Path) + * \brief Create a new node + * \param Path Path of directory to create + */ +int VFS_MkDir(const char *Path) +{ + return VFS_MkNod(Path, VFS_FFLAG_DIRECTORY); +} + +/** + * \fn int VFS_MkNod(char *Path, Uint Flags) + * \brief Create a new node in a directory + * \param Path Path of new node + * \param Flags Flags to apply to the node + */ +int VFS_MkNod(const char *Path, Uint Flags) +{ + char *absPath, *name; + int pos = 0, oldpos = 0; + int next = 0; + tVFS_Node *parent; + int ret; + + ENTER("sPath xFlags", Path, Flags); + + absPath = VFS_GetAbsPath(Path); + LOG("absPath = '%s'", absPath); + + while( (next = strpos(&absPath[pos+1], '/')) != -1 ) { + LOG("next = %i", next); + pos += next+1; + LOG("pos = %i", pos); + oldpos = pos; + } + absPath[oldpos] = '\0'; // Mutilate path + name = &absPath[oldpos+1]; + + LOG("absPath = '%s', name = '%s'", absPath, name); + + // Check for root + if(absPath[0] == '\0') + parent = VFS_ParsePath("/", NULL, NULL); + else + parent = VFS_ParsePath(absPath, NULL, NULL); + + LOG("parent = %p", parent); + + if(!parent) { + LEAVE('i', -1); + return -1; // Error Check + } + + // Permissions Check + if( !VFS_CheckACL(parent, VFS_PERM_EXECUTE|VFS_PERM_WRITE) ) { + _CloseNode(parent); + free(absPath); + LEAVE('i', -1); + return -1; + } + + LOG("parent = %p", parent); + + if(!parent->Type || !parent->Type->MkNod) { + Warning("VFS_MkNod - Directory has no MkNod method"); + LEAVE('i', -1); + return -1; + } + + // Create node + ret = parent->Type->MkNod(parent, name, Flags); + + // Free allocated string + free(absPath); + + // Free Parent + _CloseNode(parent); + + // Error Check + if(ret == 0) { + LEAVE('i', -1); + return -1; + } + + LEAVE('i', 0); + return 0; +} + +/** + * \fn int VFS_Symlink(const char *Name, const char *Link) + * \brief Creates a symlink called \a Name to \a Link + * \param Name Name of symbolic link + * \param Link Destination of symbolic link + */ +int VFS_Symlink(const char *Name, const char *Link) +{ + char *realLink; + int fp; + + ENTER("sName sLink", Name, Link); + + // Get absolue path name + realLink = VFS_GetAbsPath( Link ); + if(!realLink) { + Log_Warning("VFS", "Path '%s' is badly formed", Link); + LEAVE('i', -1); + return -1; + } + + LOG("realLink = '%s'", realLink); + + // Make node + if( VFS_MkNod(Name, VFS_FFLAG_SYMLINK) != 0 ) { + Log_Warning("VFS", "Unable to create link node '%s'", Name); + free(realLink); + LEAVE('i', -2); + return -2; // Make link node + } + + // Write link address + fp = VFS_Open(Name, VFS_OPENFLAG_WRITE|VFS_OPENFLAG_NOLINK); + VFS_Write(fp, strlen(realLink), realLink); + VFS_Close(fp); + + free(realLink); + + LEAVE('i', 1); + return 1; +} + +/** + * \fn int VFS_ReadDir(int FD, char *Dest) + * \brief Read from a directory + */ +int VFS_ReadDir(int FD, char *Dest) +{ + tVFS_Handle *h = VFS_GetHandle(FD); + char *tmp; + + //ENTER("ph pDest", h, Dest); + + if(!h || !h->Node->Type || !h->Node->Type->ReadDir) { + //LEAVE('i', 0); + return 0; + } + + if(h->Node->Size != -1 && h->Position >= h->Node->Size) { + //LEAVE('i', 0); + return 0; + } + + do { + tmp = h->Node->Type->ReadDir(h->Node, h->Position); + if((Uint)tmp < (Uint)VFS_MAXSKIP) + h->Position += (Uint)tmp; + else + h->Position ++; + } while(tmp != NULL && (Uint)tmp < (Uint)VFS_MAXSKIP); + + //LOG("tmp = '%s'", tmp); + + if(!tmp) { + //LEAVE('i', 0); + return 0; + } + + strcpy(Dest, tmp); + free(tmp); + + //LEAVE('i', 1); + return 1; +} diff --git a/KernelLand/Kernel/vfs/fs/devfs.c b/KernelLand/Kernel/vfs/fs/devfs.c new file mode 100644 index 00000000..b3f4a579 --- /dev/null +++ b/KernelLand/Kernel/vfs/fs/devfs.c @@ -0,0 +1,164 @@ +/* + * Acess 2 + * Device Filesystem (DevFS) + * - vfs/fs/devfs.c + */ +#include +#include +#include + +// === PROTOTYPES === +#if 0 + int DevFS_AddDevice(tDevFS_Driver *Device); +void DevFS_DelDevice(tDevFS_Driver *Device); +#endif +tVFS_Node *DevFS_InitDevice(const char *Device, const char **Options); +char *DevFS_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name); + +// === GLOBALS === +tVFS_Driver gDevFS_Info = { + "devfs", 0, DevFS_InitDevice, NULL, NULL + }; +tVFS_NodeType gDevFS_DirType = { + .TypeName = "DevFS-Dir", + .ReadDir = DevFS_ReadDir, + .FindDir = DevFS_FindDir + }; +tVFS_Node gDevFS_RootNode = { + .Size = 0, + .Flags = VFS_FFLAG_DIRECTORY, + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Type = &gDevFS_DirType + }; +tDevFS_Driver *gDevFS_Drivers = NULL; + int giDevFS_NextID = 1; +tShortSpinlock glDevFS_ListLock; + +// === CODE === +/** + * \fn int DevFS_AddDevice(tDevFS_Driver *Device) + */ +int DevFS_AddDevice(tDevFS_Driver *Device) +{ + int ret = 0; + tDevFS_Driver *dev; + + SHORTLOCK( &glDevFS_ListLock ); + + // Check if the device is already registered or the name is taken + for( dev = gDevFS_Drivers; dev; dev = dev->Next ) + { + if(dev == Device) break; + if(strcmp(dev->Name, Device->Name) == 0) break; + } + + if(dev) { + if(dev == Device) + Log_Warning("DevFS", "Device %p '%s' attempted to register itself twice", + dev, dev->Name); + else + Log_Warning("DevFS", "Device %p attempted to register '%s' which was owned by %p", + Device, dev->Name, dev); + ret = 0; // Error + } + else { + Device->Next = gDevFS_Drivers; + gDevFS_Drivers = Device; + gDevFS_RootNode.Size ++; + ret = giDevFS_NextID ++; + } + SHORTREL( &glDevFS_ListLock ); + + return ret; +} + +/** + * \brief Delete a device from the DevFS folder + */ +void DevFS_DelDevice(tDevFS_Driver *Device) +{ + tDevFS_Driver *prev = NULL, *dev; + + SHORTLOCK( &glDevFS_ListLock ); + // Search list for device + for(dev = gDevFS_Drivers; + dev && dev != Device; + prev = dev, dev = dev->Next + ); + + // Check if it was found + if(dev) + { + if(prev) + prev->Next = Device->Next; + else + gDevFS_Drivers = Device->Next; + } + else + Log_Warning("DevFS", "Attempted to unregister device %p '%s' which was not registered", + Device, Device->Name); + + SHORTREL( &glDevFS_ListLock ); +} + +/** + * \brief Initialise the DevFS and detect double-mounting, or just do nothing + * \note STUB + */ +tVFS_Node *DevFS_InitDevice(const char *Device, const char **Options) +{ + return &gDevFS_RootNode; +} + +/** + * \fn char *DevFS_ReadDir(tVFS_Node *Node, int Pos) + */ +char *DevFS_ReadDir(tVFS_Node *Node, int Pos) +{ + tDevFS_Driver *dev; + + if(Pos < 0) return NULL; + + for(dev = gDevFS_Drivers; + dev && Pos--; + dev = dev->Next + ); + + if(dev) + return strdup(dev->Name); + else + return NULL; +} + +/** + * \fn tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name) + * \brief Get an entry from the devices directory + */ +tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name) +{ + tDevFS_Driver *dev; + + //ENTER("pNode sName", Node, Name); + + for(dev = gDevFS_Drivers; + dev; + dev = dev->Next + ) + { + //LOG("dev = %p", dev); + //LOG("dev->Name = '%s'", dev->Name); + if(strcmp(dev->Name, Name) == 0) { + //LEAVE('p', &dev->RootNode); + return &dev->RootNode; + } + } + + //LEAVE('n'); + return NULL; +} + +// --- EXPORTS --- +EXPORT(DevFS_AddDevice); +EXPORT(DevFS_DelDevice); diff --git a/KernelLand/Kernel/vfs/fs/root.c b/KernelLand/Kernel/vfs/fs/root.c new file mode 100644 index 00000000..9fa1732b --- /dev/null +++ b/KernelLand/Kernel/vfs/fs/root.c @@ -0,0 +1,255 @@ +/* + * AcessMicro VFS + * - Root Filesystem Driver + */ +#define DEBUG 0 +#include +#include +#include + +// === CONSTANTS === +#define MAX_FILES 64 +#define MAX_FILE_SIZE 1024 + +// === PROTOTYPES === +tVFS_Node *Root_InitDevice(const char *Device, const char **Options); + int Root_MkNod(tVFS_Node *Node, const char *Name, Uint Flags); +tVFS_Node *Root_FindDir(tVFS_Node *Node, const char *Name); +char *Root_ReadDir(tVFS_Node *Node, int Pos); +Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); +tRamFS_File *Root_int_AllocFile(void); + +// === GLOBALS === +tVFS_Driver gRootFS_Info = { + "rootfs", 0, Root_InitDevice, NULL, NULL + }; +tRamFS_File RootFS_Files[MAX_FILES]; +tVFS_ACL RootFS_DirACLs[3] = { + {{0,0}, {0,VFS_PERM_ALL}}, // Owner (Root) + {{1,0}, {0,VFS_PERM_ALL}}, // Group (Root) + {{0,-1}, {0,VFS_PERM_ALL^VFS_PERM_WRITE}} // World (Nobody) +}; +tVFS_ACL RootFS_FileACLs[3] = { + {{0,0}, {0,VFS_PERM_ALL^VFS_PERM_EXECUTE}}, // Owner (Root) + {{1,0}, {0,VFS_PERM_ALL^VFS_PERM_EXECUTE}}, // Group (Root) + {{0,-1}, {0,VFS_PERM_READ}} // World (Nobody) +}; +tVFS_NodeType gRootFS_DirType = { + .TypeName = "RootFS-Dir", + .ReadDir = Root_ReadDir, + .FindDir = Root_FindDir, + .MkNod = Root_MkNod +}; +tVFS_NodeType gRootFS_FileType = { + .TypeName = "RootFS-File", + .Read = Root_Read, + .Write = Root_Write, +}; + +// === CODE === +/** + * \brief Initialise the root filesystem + */ +tVFS_Node *Root_InitDevice(const char *Device, const char **Options) +{ + tRamFS_File *root; + if(strcmp(Device, "root") != 0) { + return NULL; + } + + // Create Root Node + root = &RootFS_Files[0]; + + root->Node.ImplPtr = root; + + root->Node.CTime + = root->Node.MTime + = root->Node.ATime = now(); + root->Node.NumACLs = 3; + root->Node.ACLs = RootFS_DirACLs; + + root->Node.Type = &gRootFS_DirType; + + return &root->Node; +} + +/** + * \fn int Root_MkNod(tVFS_Node *Node, const char *Name, Uint Flags) + * \brief Create an entry in the root directory + */ +int Root_MkNod(tVFS_Node *Node, const char *Name, Uint Flags) +{ + tRamFS_File *parent = Node->ImplPtr; + tRamFS_File *child; + tRamFS_File *prev = (tRamFS_File *) &parent->Data.FirstChild; + + ENTER("pNode sName xFlags", Node, Name, Flags); + + LOG("%i > %i", strlen(Name)+1, sizeof(child->Name)); + if(strlen(Name) + 1 > sizeof(child->Name)) + LEAVE_RET('i', 0); + + // Find last child, while we're at it, check for duplication + for( child = parent->Data.FirstChild; child; prev = child, child = child->Next ) + { + if(strcmp(child->Name, Name) == 0) { + LEAVE('i', 0); + return 0; + } + } + + child = Root_int_AllocFile(); + memset(child, 0, sizeof(tRamFS_File)); + + strcpy(child->Name, Name); + + child->Parent = parent; + child->Next = NULL; + child->Data.FirstChild = NULL; + + child->Node.ImplPtr = child; + child->Node.Flags = Flags; + child->Node.NumACLs = 3; + child->Node.Size = 0; + + if(Flags & VFS_FFLAG_DIRECTORY) + { + child->Node.ACLs = RootFS_DirACLs; + child->Node.Type = &gRootFS_DirType; + } else { + if(Flags & VFS_FFLAG_SYMLINK) + child->Node.ACLs = RootFS_DirACLs; + else + child->Node.ACLs = RootFS_FileACLs; + child->Node.Type = &gRootFS_FileType; + } + + prev->Next = child; + + parent->Node.Size ++; + + LEAVE('i', 1); + return 1; +} + +/** + * \fn tVFS_Node *Root_FindDir(tVFS_Node *Node, const char *Name) + * \brief Find an entry in the filesystem + */ +tVFS_Node *Root_FindDir(tVFS_Node *Node, const char *Name) +{ + tRamFS_File *parent = Node->ImplPtr; + tRamFS_File *child = parent->Data.FirstChild; + + //Log("Root_FindDir: (Node=%p, Name='%s')", Node, Name); + + for(;child;child = child->Next) + { + //Log(" Root_FindDir: strcmp('%s', '%s')", child->Node.Name, Name); + if(strcmp(child->Name, Name) == 0) return &child->Node; + } + + return NULL; +} + +/** + * \fn char *Root_ReadDir(tVFS_Node *Node, int Pos) + * \brief Get an entry from the filesystem + */ +char *Root_ReadDir(tVFS_Node *Node, int Pos) +{ + tRamFS_File *parent = Node->ImplPtr; + tRamFS_File *child = parent->Data.FirstChild; + + for( ; child && Pos--; child = child->Next ) ; + + if(child) return strdup(child->Name); + + return NULL; +} + +/** + * \fn Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Read from a file in the root directory + */ +Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tRamFS_File *file = Node->ImplPtr; + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + + if(Offset > Node->Size) { + LEAVE('i', 0); + return 0; + } + if(Length > Node->Size) Length = Node->Size; + + if(Offset+Length > Node->Size) + Length = Node->Size - Offset; + + memcpy(Buffer, file->Data.Bytes+Offset, Length); + LOG("Buffer = '%.*s'", (int)Length, Buffer); + + LEAVE('i', Length); + return Length; +} + +/** + * \fn Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Write to a file in the root directory + */ +Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + tRamFS_File *file = Node->ImplPtr; + + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + + if(Offset > Node->Size) { + LEAVE('i', -1); + return -1; + } + + if(Offset + Length > MAX_FILE_SIZE) + { + Length = MAX_FILE_SIZE - Offset; + } + + LOG("Buffer = '%.*s'", (int)Length, Buffer); + + // Check if buffer needs to be expanded + if(Offset + Length > Node->Size) + { + void *tmp = realloc( file->Data.Bytes, Offset + Length ); + if(tmp == NULL) { + Warning("Root_Write - Increasing buffer size failed"); + LEAVE('i', -1); + return -1; + } + file->Data.Bytes = tmp; + Node->Size = Offset + Length; + LOG("Expanded buffer to %i bytes", (int)Node->Size); + } + + memcpy(file->Data.Bytes+Offset, Buffer, Length); + LOG("File - '%.*s'", Node->Size, file->Data.Bytes); + + LEAVE('i', Length); + return Length; +} + +/** + * \fn tRamFS_File *Root_int_AllocFile(void) + * \brief Allocates a file from the pool + */ +tRamFS_File *Root_int_AllocFile(void) +{ + int i; + for( i = 0; i < MAX_FILES; i ++ ) + { + if( RootFS_Files[i].Name[0] == '\0' ) + { + return &RootFS_Files[i]; + } + } + return NULL; +} diff --git a/KernelLand/Kernel/vfs/handle.c b/KernelLand/Kernel/vfs/handle.c new file mode 100644 index 00000000..d22f9656 --- /dev/null +++ b/KernelLand/Kernel/vfs/handle.c @@ -0,0 +1,270 @@ +/* + * Acess2 VFS + * - AllocHandle, GetHandle + */ +#define DEBUG 0 +#include +#include +#include "vfs.h" +#include "vfs_int.h" +#include "vfs_ext.h" +#include // GetMaxFD +#include // Handle maintainance + +// === CONSTANTS === +#define MAX_KERNEL_FILES 128 + +// === PROTOTYPES === +#if 0 +tVFS_Handle *VFS_GetHandle(int FD); +#endif + int VFS_AllocHandle(int FD, tVFS_Node *Node, int Mode); + +// === GLOBALS === +tVFS_Handle *gaUserHandles = (void*)MM_PPD_HANDLES; +tVFS_Handle *gaKernelHandles = (void*)MM_KERNEL_VFS; + +// === CODE === +/** + * \fn tVFS_Handle *VFS_GetHandle(int FD) + * \brief Gets a pointer to the handle information structure + */ +tVFS_Handle *VFS_GetHandle(int FD) +{ + tVFS_Handle *h; + + //Log_Debug("VFS", "VFS_GetHandle: (FD=0x%x)", FD); + + if(FD < 0) return NULL; + + if(FD & VFS_KERNEL_FLAG) { + FD &= (VFS_KERNEL_FLAG - 1); + if(FD >= MAX_KERNEL_FILES) return NULL; + h = &gaKernelHandles[ FD ]; + } else { + if(FD >= *Threads_GetMaxFD()) return NULL; + h = &gaUserHandles[ FD ]; + } + + if(h->Node == NULL) return NULL; + //Log_Debug("VFS", "VFS_GetHandle: RETURN %p", h); + return h; +} + +int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode) +{ + int i; + + // Check for a user open + if(bIsUser) + { + int max_handles = *Threads_GetMaxFD(); + // Allocate Buffer + if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 ) + { + Uint addr, size; + size = max_handles * sizeof(tVFS_Handle); + for(addr = 0; addr < size; addr += 0x1000) + { + if( !MM_Allocate( (tVAddr)gaUserHandles + addr ) ) + { + Warning("OOM - VFS_AllocHandle"); + Threads_Exit(0, 0xFF); // Terminate user + } + } + memset( gaUserHandles, 0, size ); + } + // Get a handle + for( i = 0; i < max_handles; i ++ ) + { + if(gaUserHandles[i].Node) continue; + gaUserHandles[i].Node = Node; + gaUserHandles[i].Position = 0; + gaUserHandles[i].Mode = Mode; + return i; + } + } + else + { + // Allocate space if not already + if( MM_GetPhysAddr( (tVAddr)gaKernelHandles ) == 0 ) + { + Uint addr, size; + size = MAX_KERNEL_FILES * sizeof(tVFS_Handle); + for(addr = 0; addr < size; addr += 0x1000) + { + if( !MM_Allocate( (tVAddr)gaKernelHandles + addr ) ) + { + Panic("OOM - VFS_AllocHandle"); + Threads_Exit(0, 0xFF); // Terminate application (get some space back) + } + } + memset( gaKernelHandles, 0, size ); + } + // Get a handle + for(i=0;iNode ) + continue ; + if( h->Node->Type && h->Node->Type->Reference ) + h->Node->Type->Reference( h->Node ); + } +} + +void VFS_CloseAllUserHandles(void) +{ + int i; + int max_handles = *Threads_GetMaxFD(); + + // Check if this process has any handles + if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 ) + return ; + + for( i = 0; i < max_handles; i ++ ) + { + tVFS_Handle *h; + h = &gaUserHandles[i]; + if( !h->Node ) + continue ; + if( h->Node->Type && h->Node->Type->Close ) + h->Node->Type->Close( h->Node ); + } +} + +/** + * \brief Take a backup of a set of file descriptors + */ +void *VFS_SaveHandles(int NumFDs, int *FDs) +{ + tVFS_Handle *ret; + int i; + int max_handles = *Threads_GetMaxFD(); + + // Check if this process has any handles + if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) == 0 ) + return NULL; + + // Allocate + ret = malloc( NumFDs * sizeof(tVFS_Handle) ); + if( !ret ) + return NULL; + + if( NumFDs > max_handles ) + NumFDs = max_handles; + + // Take copies of the handles + for( i = 0; i < NumFDs; i ++ ) + { + tVFS_Handle *h; + if( FDs == NULL ) + h = &gaUserHandles[i]; + else { + h = VFS_GetHandle(FDs[i] & (VFS_KERNEL_FLAG - 1)); + if(!h) { + Log_Warning("VFS", "VFS_SaveHandles - Invalid FD %i", + FDs[i] & (VFS_KERNEL_FLAG - 1) ); + free(ret); + return NULL; + } + } + memcpy( &ret[i], h, sizeof(tVFS_Handle) ); + + // Reference node + if( !h->Node ) + continue ; + if( h->Node->Type && h->Node->Type->Reference ) + h->Node->Type->Reference( h->Node ); + } + + return ret; +} + +void VFS_RestoreHandles(int NumFDs, void *Handles) +{ + tVFS_Handle *handles = Handles; + int i; + + // NULL = nothing to do + if( !Handles ) + return ; + + // Check if there is already a set of handles + if( MM_GetPhysAddr( (tVAddr)gaUserHandles ) != 0 ) + return ; + + + // Allocate user handle area + { + Uint addr, size; + int max_handles = *Threads_GetMaxFD(); + size = max_handles * sizeof(tVFS_Handle); + for(addr = 0; addr < size; addr += 0x1000) + { + if( !MM_Allocate( (tVAddr)gaUserHandles + addr ) ) + { + Warning("OOM - VFS_AllocHandle"); + Threads_Exit(0, 0xFF); // Terminate user + } + } + memset( gaUserHandles, 0, size ); + } + + // Restore handles + memcpy( gaUserHandles, handles, NumFDs * sizeof(tVFS_Handle) ); + // Reference when copied + for( i = 0; i < NumFDs; i ++ ) + { + tVFS_Handle *h = &handles[i]; + + if( !h->Node ) + continue ; + if( h->Node->Type && h->Node->Type->Reference ) + h->Node->Type->Reference( h->Node ); + } +} + +void VFS_FreeSavedHandles(int NumFDs, void *Handles) +{ + tVFS_Handle *handles = Handles; + int i; + + // NULL = nothing to do + if( !Handles ) + return ; + + // Dereference all saved nodes + for( i = 0; i < NumFDs; i ++ ) + { + tVFS_Handle *h = &handles[i]; + + if( !h->Node ) + continue ; + if( h->Node->Type && h->Node->Type->Close ) + h->Node->Type->Close( h->Node ); + } + free( Handles ); +} diff --git a/KernelLand/Kernel/vfs/io.c b/KernelLand/Kernel/vfs/io.c new file mode 100644 index 00000000..e7a2a068 --- /dev/null +++ b/KernelLand/Kernel/vfs/io.c @@ -0,0 +1,216 @@ +/* + * AcessMicro VFS + * - File IO Passthru's + */ +#define DEBUG 0 +#include +#include "vfs.h" +#include "vfs_int.h" + +// === CODE === +/** + * \fn Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer) + * \brief Read data from a node (file) + */ +Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer) +{ + tVFS_Handle *h; + Uint64 ret; + + ENTER("iFD XLength pBuffer", FD, Length, Buffer); + + h = VFS_GetHandle(FD); + if(!h) LEAVE_RET('i', -1); + + if( !(h->Mode & VFS_OPENFLAG_READ) || h->Node->Flags & VFS_FFLAG_DIRECTORY ) + LEAVE_RET('i', -1); + + if(!h->Node->Type || !h->Node->Type->Read) LEAVE_RET('i', 0); + + ret = h->Node->Type->Read(h->Node, h->Position, Length, Buffer); + if(ret == -1) LEAVE_RET('i', -1); + + h->Position += ret; + LEAVE('X', ret); + return ret; +} + +/** + * \fn Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Read data from a given offset (atomic) + */ +Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tVFS_Handle *h; + Uint64 ret; + + h = VFS_GetHandle(FD); + if(!h) return -1; + + if( !(h->Mode & VFS_OPENFLAG_READ) ) return -1; + if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1; + + if( !h->Node->Type || !h->Node->Type->Read) { + Warning("VFS_ReadAt - Node %p, does not have a read method", h->Node); + return 0; + } + ret = h->Node->Type->Read(h->Node, Offset, Length, Buffer); + if(ret == -1) return -1; + return ret; +} + +/** + * \fn Uint64 VFS_Write(int FD, Uint64 Length, const void *Buffer) + * \brief Read data from a node (file) + */ +Uint64 VFS_Write(int FD, Uint64 Length, const void *Buffer) +{ + tVFS_Handle *h; + Uint64 ret; + + h = VFS_GetHandle(FD); + if(!h) return -1; + + if( !(h->Mode & VFS_OPENFLAG_WRITE) ) return -1; + if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1; + + if( !h->Node->Type || !h->Node->Type->Write ) return 0; + + ret = h->Node->Type->Write(h->Node, h->Position, Length, Buffer); + if(ret == -1) return -1; + + h->Position += ret; + return ret; +} + +/** + * \fn Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer) + * \brief Write data to a file at a given offset + */ +Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + tVFS_Handle *h; + Uint64 ret; + + h = VFS_GetHandle(FD); + if(!h) return -1; + + if( !(h->Mode & VFS_OPENFLAG_WRITE) ) return -1; + if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1; + + if(!h->Node->Type || !h->Node->Type->Write) return 0; + ret = h->Node->Type->Write(h->Node, Offset, Length, Buffer); + + if(ret == -1) return -1; + return ret; +} + +/** + * \fn Uint64 VFS_Tell(int FD) + * \brief Returns the current file position + */ +Uint64 VFS_Tell(int FD) +{ + tVFS_Handle *h; + + h = VFS_GetHandle(FD); + if(!h) return -1; + + return h->Position; +} + +/** + * \fn int VFS_Seek(int FD, Sint64 Offset, int Whence) + * \brief Seek to a new location + * \param FD File descriptor + * \param Offset Where to go + * \param Whence From where + */ +int VFS_Seek(int FD, Sint64 Offset, int Whence) +{ + tVFS_Handle *h; + + h = VFS_GetHandle(FD); + if(!h) return -1; + + //Log_Debug("VFS", "VFS_Seek: (fd=0x%x, Offset=0x%llx, Whence=%i)", + // FD, Offset, Whence); + + // Set relative to current position + if(Whence == 0) { + h->Position += Offset; + return 0; + } + + // Set relative to end of file + if(Whence < 0) { + if( h->Node->Size == -1 ) return -1; + + h->Position = h->Node->Size - Offset; + return 0; + } + + // Set relative to start of file + h->Position = Offset; + return 0; +} + +/** + * \fn int VFS_IOCtl(int FD, int ID, void *Buffer) + * \brief Call an IO Control on a file + */ +int VFS_IOCtl(int FD, int ID, void *Buffer) +{ + tVFS_Handle *h; + + h = VFS_GetHandle(FD); + if(!h) return -1; + + if(!h->Node->Type || !h->Node->Type->IOCtl) return -1; + return h->Node->Type->IOCtl(h->Node, ID, Buffer); +} + +/** + * \fn int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs) + * \brief Retrieve file information + * \return Number of ACLs stored + */ +int VFS_FInfo(int FD, tFInfo *Dest, int MaxACLs) +{ + tVFS_Handle *h; + int max; + + h = VFS_GetHandle(FD); + if(!h) return -1; + + if( h->Mount ) + Dest->mount = h->Mount->Identifier; + else + Dest->mount = 0; + Dest->inode = h->Node->Inode; + Dest->uid = h->Node->UID; + Dest->gid = h->Node->GID; + Dest->size = h->Node->Size; + Dest->atime = h->Node->ATime; + Dest->ctime = h->Node->MTime; + Dest->mtime = h->Node->CTime; + Dest->numacls = h->Node->NumACLs; + + Dest->flags = 0; + if(h->Node->Flags & VFS_FFLAG_DIRECTORY) Dest->flags |= 0x10; + if(h->Node->Flags & VFS_FFLAG_SYMLINK) Dest->flags |= 0x20; + + max = (MaxACLs < h->Node->NumACLs) ? MaxACLs : h->Node->NumACLs; + memcpy(&Dest->acls, h->Node->ACLs, max*sizeof(tVFS_ACL)); + + return max; +} + +// === EXPORTS === +EXPORT(VFS_Read); +EXPORT(VFS_Write); +EXPORT(VFS_ReadAt); +EXPORT(VFS_WriteAt); +EXPORT(VFS_IOCtl); +EXPORT(VFS_Seek); +EXPORT(VFS_Tell); diff --git a/KernelLand/Kernel/vfs/main.c b/KernelLand/Kernel/vfs/main.c new file mode 100644 index 00000000..5ff0e7c2 --- /dev/null +++ b/KernelLand/Kernel/vfs/main.c @@ -0,0 +1,166 @@ +/* + * Acess 2 + * Virtual File System + */ +#include +#include +#include +#include +#include +#include + +// === IMPORTS === +extern tVFS_Driver gRootFS_Info; +extern tVFS_Driver gDevFS_Info; + +// === PROTOTYPES === +#if 0 + int VFS_Init(void); +char *VFS_GetTruePath(const char *Path); +void VFS_GetMemPath(char *Dest, void *Base, Uint Length); +tVFS_Driver *VFS_GetFSByName(const char *Name); + int VFS_AddDriver(tVFS_Driver *Info); +#endif +void VFS_UpdateDriverFile(void); + +// === EXPORTS === +EXPORT(VFS_AddDriver); + +// === GLOBALS === +tVFS_Node NULLNode = {0}; +tShortSpinlock slDriverListLock; +tVFS_Driver *gVFS_Drivers = NULL; +char *gsVFS_DriverFile = NULL; + int giVFS_DriverFileID = 0; + +char *gsVFS_MountFile = NULL; + int giVFS_MountFileID = 0; + +// === CODE === +/** + * \fn int VFS_Init(void) + * \brief Initialises the VFS for use by the kernel and user + */ +int VFS_Init(void) +{ + // Core Drivers + gVFS_Drivers = &gRootFS_Info; + gVFS_Drivers->Next = &gDevFS_Info; + VFS_UpdateDriverFile(); + + // Register with SysFS + giVFS_MountFileID = SysFS_RegisterFile("VFS/Mounts", NULL, 0); + giVFS_DriverFileID = SysFS_RegisterFile("VFS/Drivers", NULL, 0); + + if( VFS_Mount("root", "/", "rootfs", "") != 0 ) { + Log_KernelPanic("VFS", "Unable to mount root (Where the **** is rootfs?)"); + return -1; + } + VFS_MkDir("/Devices"); + VFS_MkDir("/Mount"); + VFS_Mount("dev", "/Devices", "devfs", ""); + + Log_Debug("VFS", "Setting max files"); + *Threads_GetMaxFD() = 32; + return 0; +} + +/** + * \fn char *VFS_GetTruePath(const char *Path) + * \brief Gets the true path (non-symlink) of a file + */ +char *VFS_GetTruePath(const char *Path) +{ + tVFS_Node *node; + char *ret, *tmp; + + tmp = VFS_GetAbsPath(Path); + if(tmp == NULL) return NULL; + //Log(" VFS_GetTruePath: tmp = '%s'", tmp); + node = VFS_ParsePath(tmp, &ret, NULL); + free(tmp); + //Log(" VFS_GetTruePath: node=%p, ret='%s'", node, ret); + + if(!node) return NULL; + if(node->Type->Close) node->Type->Close(node); + + return ret; +} + +/** + * \fn void VFS_GetMemPath(char *Dest, void *Base, Uint Length) + * \brief Create a VFS memory pointer path + */ +void VFS_GetMemPath(char *Dest, void *Base, Uint Length) +{ + Dest[0] = '$'; + itoa( &Dest[1], (tVAddr)Base, 16, BITS/4, '0' ); + Dest[BITS/4+1] = ':'; + itoa( &Dest[BITS/4+2], Length, 16, BITS/4, '0' ); + Dest[BITS/2+2] = '\0'; +} + +/** + * \fn tVFS_Driver *VFS_GetFSByName(const char *Name) + * \brief Gets a filesystem structure given a name + */ +tVFS_Driver *VFS_GetFSByName(const char *Name) +{ + tVFS_Driver *drv = gVFS_Drivers; + + for(;drv;drv=drv->Next) + { +// Log("strcmp('%s' (%p), '%s') == 0?", drv->Name, drv->Name, Name); + if(strcmp(drv->Name, Name) == 0) + return drv; + } + return NULL; +} + +/** + * \fn int VFS_AddDriver(tVFS_Driver *Info) + */ +int VFS_AddDriver(tVFS_Driver *Info) +{ + if(!Info) return -1; + + SHORTLOCK( &slDriverListLock ); + Info->Next = gVFS_Drivers; + gVFS_Drivers = Info; + SHORTREL( &slDriverListLock ); + + VFS_UpdateDriverFile(); + + return 0; +} + +/** + * \fn void VFS_UpdateDriverFile(void) + * \brief Updates the driver list file + */ +void VFS_UpdateDriverFile(void) +{ + tVFS_Driver *drv; + int len = 0; + char *buf; + + // Format: + // \n + for( drv = gVFS_Drivers; drv; drv = drv->Next ) + { + len += 1 + strlen(drv->Name); + } + buf = malloc(len+1); + len = 0; + for( drv = gVFS_Drivers; drv; drv = drv->Next ) + { + strcpy( &buf[len], drv->Name ); + len += strlen(drv->Name); + buf[len++] = '\n'; + } + buf[len] = '\0'; + + SysFS_UpdateFile( giVFS_DriverFileID, buf, len ); + if(gsVFS_DriverFile) free(gsVFS_DriverFile); + gsVFS_DriverFile = buf; +} diff --git a/KernelLand/Kernel/vfs/memfile.c b/KernelLand/Kernel/vfs/memfile.c new file mode 100644 index 00000000..14b39481 --- /dev/null +++ b/KernelLand/Kernel/vfs/memfile.c @@ -0,0 +1,135 @@ +/* + * Acess 2 + * Virtual File System + * - Memory Pseudo Files + */ +#include +#include + +// === PROTOTYPES === +tVFS_Node *VFS_MemFile_Create(const char *Path); +void VFS_MemFile_Close(tVFS_Node *Node); +Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); + +// === GLOBALS === +tVFS_NodeType gVFS_MemFileType = { + .Close = VFS_MemFile_Close, + .Read = VFS_MemFile_Read, + .Write = VFS_MemFile_Write + }; + +// === CODE === +/** + * \fn tVFS_Node *VFS_MemFile_Create(const char *Path) + */ +tVFS_Node *VFS_MemFile_Create(const char *Path) +{ + Uint base, size; + const char *str = Path; + tVFS_Node *ret; + + str++; // Eat '$' + + // Read Base address + base = 0; + for( ; ('0' <= *str && *str <= '9') || ('A' <= *str && *str <= 'F'); str++ ) + { + base *= 16; + if('A' <= *str && *str <= 'F') + base += *str - 'A' + 10; + else + base += *str - '0'; + } + + // Check separator + if(*str++ != ':') return NULL; + + // Read buffer size + size = 0; + for( ; ('0' <= *str && *str <= '9') || ('A' <= *str && *str <= 'F'); str++ ) + { + size *= 16; + if('A' <= *str && *str <= 'F') + size += *str - 'A' + 10; + else + size += *str - '0'; + } + + // Check for NULL byte + if(*str != '\0') return NULL; + + // Allocate and fill node + ret = malloc(sizeof(tVFS_Node)); + memset(ret, 0, sizeof(tVFS_Node)); + + // State + ret->ImplPtr = (void*)base; + ret->Size = size; + + // ACLs + ret->NumACLs = 1; + ret->ACLs = &gVFS_ACL_EveryoneRWX; + + // Functions + ret->Type = &gVFS_MemFileType; + + return ret; +} + +/** + * \fn void VFS_MemFile_Close(tVFS_Node *Node) + * \brief Dereference and clean up a memory file + */ +void VFS_MemFile_Close(tVFS_Node *Node) +{ + Node->ReferenceCount --; + if( Node->ReferenceCount == 0 ) { + Node->ImplPtr = NULL; + free(Node); + } +} + +/** + * \fn Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Read from a memory file + */ +Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + // Check for use of free'd file + if(Node->ImplPtr == NULL) return 0; + + // Check for out of bounds read + if(Offset > Node->Size) return 0; + + // Truncate data read if needed + if(Offset + Length > Node->Size) + Length = Node->Size - Offset; + + // Copy Data + memcpy(Buffer, (Uint8*)Node->ImplPtr + Offset, Length); + + return Length; +} + +/** + * \fn Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Write to a memory file + */ +Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + // Check for use of free'd file + if(Node->ImplPtr == NULL) return 0; + + // Check for out of bounds read + if(Offset > Node->Size) return 0; + + // Truncate data read if needed + if(Offset + Length > Node->Size) + Length = Node->Size - Offset; + + // Copy Data + memcpy((Uint8*)Node->ImplPtr + Offset, Buffer, Length); + + return Length; +} diff --git a/KernelLand/Kernel/vfs/mmap.c b/KernelLand/Kernel/vfs/mmap.c new file mode 100644 index 00000000..9fe9282c --- /dev/null +++ b/KernelLand/Kernel/vfs/mmap.c @@ -0,0 +1,210 @@ +/* + * Acess2 Kernel VFS + * - By John Hodge (thePowersGang) + * + * mmap.c + * - VFS_MMap support + */ +#define DEBUG 0 +#include +#include +#include +#include + +#define MMAP_PAGES_PER_BLOCK 16 + +// === STRUCTURES === +typedef struct sVFS_MMapPageBlock tVFS_MMapPageBlock; +struct sVFS_MMapPageBlock +{ + tVFS_MMapPageBlock *Next; + Uint64 BaseOffset; // Must be a multiple of MMAP_PAGES_PER_BLOCK*PAGE_SIZE + tPAddr PhysAddrs[MMAP_PAGES_PER_BLOCK]; +}; + +// === CODE === +void *VFS_MMap(void *DestHint, size_t Length, int Protection, int Flags, int FD, Uint64 Offset) +{ + tVFS_Handle *h; + tVAddr mapping_dest, mapping_base; + int npages, pagenum; + tVFS_MMapPageBlock *pb, *prev; + + ENTER("pDestHint iLength xProtection xFlags xFD XOffset", DestHint, Length, Protection, Flags, FD, Offset); + + if( Flags & MMAP_MAP_ANONYMOUS ) + Offset = (tVAddr)DestHint & 0xFFF; + + npages = ((Offset & (PAGE_SIZE-1)) + Length + (PAGE_SIZE - 1)) / PAGE_SIZE; + pagenum = Offset / PAGE_SIZE; + + mapping_base = (tVAddr)DestHint; + mapping_dest = mapping_base & ~(PAGE_SIZE-1); + + // TODO: Locate space for the allocation + + // Handle anonymous mappings + if( Flags & MMAP_MAP_ANONYMOUS ) + { + size_t ofs = 0; + LOG("%i pages anonymous to %p", npages, mapping_dest); + for( ; npages --; mapping_dest += PAGE_SIZE, ofs += PAGE_SIZE ) + { + if( MM_GetPhysAddr(mapping_dest) ) { + // TODO: Set flags to COW if needed (well, if shared) + MM_SetFlags(mapping_dest, MM_PFLAG_COW, MM_PFLAG_COW); + LOG("clear from %p, %i bytes", (void*)(mapping_base + ofs), + PAGE_SIZE - (mapping_base & (PAGE_SIZE-1)) + ); + memset( (void*)(mapping_base + ofs), 0, PAGE_SIZE - (mapping_base & (PAGE_SIZE-1))); + } + else { + LOG("New empty page"); + // TODO: Map a COW zero page instead + if( !MM_Allocate(mapping_dest) ) { + // TODO: Error + Log_Warning("VFS", "VFS_MMap: Anon alloc to %p failed", mapping_dest); + } + memset((void*)mapping_dest, 0, PAGE_SIZE); + LOG("Anon map to %p", mapping_dest); + } + } + LEAVE_RET('p', (void*)mapping_base); + } + + h = VFS_GetHandle(FD); + if( !h || !h->Node ) LEAVE_RET('n', NULL); + + LOG("h = %p", h); + + Mutex_Acquire( &h->Node->Lock ); + + // Search for existing mapping for each page + // - Sorted list of 16 page blocks + for( + pb = h->Node->MMapInfo, prev = NULL; + pb && pb->BaseOffset + MMAP_PAGES_PER_BLOCK < pagenum; + prev = pb, pb = pb->Next + ); + + LOG("pb = %p, pb->BaseOffset = %X", pb, pb ? pb->BaseOffset : 0); + + // - Allocate a block if needed + if( !pb || pb->BaseOffset > pagenum ) + { + void *old_pb = pb; + pb = malloc( sizeof(tVFS_MMapPageBlock) ); + if(!pb) { + Mutex_Release( &h->Node->Lock ); + LEAVE_RET('n', NULL); + } + pb->Next = old_pb; + pb->BaseOffset = pagenum - pagenum % MMAP_PAGES_PER_BLOCK; + memset(pb->PhysAddrs, 0, sizeof(pb->PhysAddrs)); + if(prev) + prev->Next = pb; + else + h->Node->MMapInfo = pb; + } + + // - Map (and allocate) pages + while( npages -- ) + { + if( MM_GetPhysAddr(mapping_dest) == 0 ) + { + if( pb->PhysAddrs[pagenum - pb->BaseOffset] == 0 ) + { + tVFS_NodeType *nt = h->Node->Type; + if( !nt ) + { + // TODO: error + } + else if( nt->MMap ) + nt->MMap(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE, (void*)mapping_dest); + else + { + int read_len; + // Allocate pages and read data + if( MM_Allocate(mapping_dest) == 0 ) { + // TODO: Unwrap + Mutex_Release( &h->Node->Lock ); + LEAVE('n'); + return NULL; + } + // TODO: Clip read length + read_len = nt->Read(h->Node, pagenum*PAGE_SIZE, PAGE_SIZE, (void*)mapping_dest); +// if( read_len != PAGE_SIZE ) { +// memset( (void*)(mapping_dest+read_len), 0, PAGE_SIZE-read_len ); +// } + } + pb->PhysAddrs[pagenum - pb->BaseOffset] = MM_GetPhysAddr( mapping_dest ); + MM_SetPageNode( pb->PhysAddrs[pagenum - pb->BaseOffset], h->Node ); + MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] ); + LOG("Read and map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest, + pb->PhysAddrs[pagenum - pb->BaseOffset]); + } + else + { + MM_Map( mapping_dest, pb->PhysAddrs[pagenum - pb->BaseOffset] ); + MM_RefPhys( pb->PhysAddrs[pagenum - pb->BaseOffset] ); + LOG("Cached map %X to %p (%P)", pagenum*PAGE_SIZE, mapping_dest, + pb->PhysAddrs[pagenum - pb->BaseOffset]); + } + h->Node->ReferenceCount ++; + + // Set flags + if( !(Protection & MMAP_PROT_WRITE) ) { + MM_SetFlags(mapping_dest, MM_PFLAG_RO, MM_PFLAG_RO); + } + else { + MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO); + } + + if( Protection & MMAP_PROT_EXEC ) { + MM_SetFlags(mapping_dest, MM_PFLAG_EXEC, MM_PFLAG_EXEC); + } + else { + MM_SetFlags(mapping_dest, 0, MM_PFLAG_EXEC); + } + } + else + { + LOG("Flag update on %p", mapping_dest); + if( (MM_GetFlags(mapping_dest) & MM_PFLAG_RO) && (Protection & MMAP_PROT_WRITE) ) + { + MM_SetFlags(mapping_dest, 0, MM_PFLAG_RO); + } + } + if( Flags & MMAP_MAP_PRIVATE ) + MM_SetFlags(mapping_dest, MM_PFLAG_COW, MM_PFLAG_COW); + pagenum ++; + mapping_dest += PAGE_SIZE; + + // Roll on to next block if needed + if(pagenum - pb->BaseOffset == MMAP_PAGES_PER_BLOCK) + { + if( pb->Next && pb->Next->BaseOffset == pagenum ) + pb = pb->Next; + else + { + tVFS_MMapPageBlock *oldpb = pb; + pb = malloc( sizeof(tVFS_MMapPageBlock) ); + pb->Next = oldpb->Next; + pb->BaseOffset = pagenum; + memset(pb->PhysAddrs, 0, sizeof(pb->PhysAddrs)); + oldpb->Next = pb; + } + pagenum = 0; + } + } + + Mutex_Release( &h->Node->Lock ); + + LEAVE('p', mapping_base); + return (void*)mapping_base; +} + +int VFS_MUnmap(void *Addr, size_t Length) +{ + return 0; +} diff --git a/KernelLand/Kernel/vfs/mount.c b/KernelLand/Kernel/vfs/mount.c new file mode 100644 index 00000000..1773218a --- /dev/null +++ b/KernelLand/Kernel/vfs/mount.c @@ -0,0 +1,176 @@ +/* + * Acess Micro - VFS Server version 1 + */ +#include +#include +#include +#include + +// === IMPORTS === +extern int giVFS_MountFileID; +extern char *gsVFS_MountFile; + +// === PROTOTYPES === +#if 0 + int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options); +#endif +void VFS_UpdateMountFile(void); + +// === GLOBALS === +tMutex glVFS_MountList; +tVFS_Mount *gVFS_Mounts; +tVFS_Mount *gVFS_RootMount = NULL; +Uint32 giVFS_NextMountIdent = 1; + +// === CODE === +/** + * \brief Mount a device + * \param Device Device string to mount + * \param MountPoint Destination for the mount + * \param Filesystem Filesystem to use for the mount + * \param Options Options to be passed to the filesystem + * \return -1 on Invalid FS, -2 on No Mem, 0 on success + * + * Mounts the filesystem on \a Device at \a MountPoint using the driver + * \a Filesystem. The options in the string \a Options is passed to the + * driver's mount. + */ +int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options) +{ + tVFS_Mount *mnt; + tVFS_Driver *fs; + int deviceLen = strlen(Device); + int mountLen = strlen(MountPoint); + int argLen = strlen(Options); + + // Get the filesystem + fs = VFS_GetFSByName(Filesystem); + if(!fs) { + Log_Warning("VFS", "VFS_Mount - Unknown FS Type '%s'", Filesystem); + return -1; + } + + // Create mount information + mnt = malloc( sizeof(tVFS_Mount)+deviceLen+1+mountLen+1+argLen+1 ); + if(!mnt) { + return -2; + } + + // HACK: Forces VFS_ParsePath to fall back on root + if(mountLen == 1 && MountPoint[0] == '/') + mnt->MountPointLen = 0; + else + mnt->MountPointLen = mountLen; + + // Fill Structure + mnt->Filesystem = fs; + + mnt->Device = &mnt->StrData[0]; + memcpy( mnt->Device, Device, deviceLen+1 ); + + mnt->MountPoint = &mnt->StrData[deviceLen+1]; + memcpy( mnt->MountPoint, MountPoint, mountLen+1 ); + + mnt->Options = &mnt->StrData[deviceLen+1+mountLen+1]; + memcpy( mnt->Options, Options, argLen+1 ); + + // Initialise Volume + mnt->RootNode = fs->InitDevice(Device, NULL); //&ArgString); + if(!mnt->RootNode) { + free(mnt); + return -2; + } + + mnt->Identifier = giVFS_NextMountIdent++; + #if 0 + // Ensure identifiers don't repeat + // - Only a problem if there have been 4 billion mounts + while( giVFS_NextMountIdent == 0 || VFS_GetMountByIdent(giVFS_NextMountIdent) ) + giVFS_NextMountIdent ++; + #endif + + // Set root + if(!gVFS_RootMount) gVFS_RootMount = mnt; + + // Add to mount list + Mutex_Acquire( &glVFS_MountList ); + { + tVFS_Mount *tmp; + mnt->Next = NULL; + if(gVFS_Mounts) { + for( tmp = gVFS_Mounts; tmp->Next; tmp = tmp->Next ); + tmp->Next = mnt; + } + else { + gVFS_Mounts = mnt; + } + } + Mutex_Release( &glVFS_MountList ); + + Log_Log("VFS", "Mounted '%s' to '%s' ('%s')", Device, MountPoint, Filesystem); + + VFS_UpdateMountFile(); + + return 0; +} + +/** + * \brief Gets a mount point given the identifier + */ +tVFS_Mount *VFS_GetMountByIdent(Uint32 MountID) +{ + tVFS_Mount *mnt; + for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next) + { + if(mnt->Identifier == MountID) + return mnt; + } + return NULL; +} + +/** + * \brief Updates the mount file buffer + * + * Updates the ProcFS mounts file buffer to match the current mounts list. + */ +void VFS_UpdateMountFile(void) +{ + int len = 0; + char *buf; + tVFS_Mount *mnt; + + // Format: + // \t\t\t\n + + for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next) + { + len += 4 + strlen(mnt->Device) + strlen(mnt->MountPoint) + + strlen(mnt->Filesystem->Name) + strlen(mnt->Options); + } + + buf = malloc( len + 1 ); + len = 0; + for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next) + { + strcpy( &buf[len], mnt->Device ); + len += strlen(mnt->Device); + buf[len++] = '\t'; + + strcpy( &buf[len], mnt->MountPoint ); + len += strlen(mnt->MountPoint); + buf[len++] = '\t'; + + strcpy( &buf[len], mnt->Filesystem->Name ); + len += strlen(mnt->Filesystem->Name); + buf[len++] = '\t'; + + strcpy( &buf[len], mnt->Options ); + len += strlen(mnt->Options); + buf[len++] = '\n'; + } + buf[len] = 0; + + SysFS_UpdateFile( giVFS_MountFileID, buf, len ); + if( gsVFS_MountFile ) free( gsVFS_MountFile ); + gsVFS_MountFile = buf; +} diff --git a/KernelLand/Kernel/vfs/nodecache.c b/KernelLand/Kernel/vfs/nodecache.c new file mode 100644 index 00000000..d2796c1b --- /dev/null +++ b/KernelLand/Kernel/vfs/nodecache.c @@ -0,0 +1,219 @@ +/* + * AcessMicro VFS + * - File IO Passthru's + */ +#include +#include "vfs.h" +#include "vfs_int.h" + +// === TYPES === +typedef struct sCachedInode { + struct sCachedInode *Next; + tVFS_Node Node; +} tCachedInode; +typedef struct sInodeCache { + struct sInodeCache *Next; + int Handle; + tCachedInode *FirstNode; // Sorted List + Uint64 MaxCached; // Speeds up Searching +} tInodeCache; + +// === PROTOTYPES === +tInodeCache *Inode_int_GetFSCache(int Handle); + +// === GLOBALS === + int gVFS_NextInodeHandle = 1; +tShortSpinlock glVFS_InodeCache; +tInodeCache *gVFS_InodeCache = NULL; + +// === CODE === +/** + * \fn int Inode_GetHandle() + */ +int Inode_GetHandle() +{ + tInodeCache *ent; + + ent = malloc( sizeof(tInodeCache) ); + ent->MaxCached = 0; + ent->Handle = gVFS_NextInodeHandle++; + ent->Next = NULL; ent->FirstNode = NULL; + + // Add to list + SHORTLOCK( &glVFS_InodeCache ); + ent->Next = gVFS_InodeCache; + gVFS_InodeCache = ent; + SHORTREL( &glVFS_InodeCache ); + + return gVFS_NextInodeHandle-1; +} + +/** + * \fn tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode) + * \brief Gets a node from the cache + */ +tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode) +{ + tInodeCache *cache; + tCachedInode *ent; + + cache = Inode_int_GetFSCache(Handle); + if(!cache) return NULL; + + if(Inode > cache->MaxCached) return NULL; + + // Search Cache + ent = cache->FirstNode; + for( ; ent; ent = ent->Next ) + { + if(ent->Node.Inode < Inode) continue; + if(ent->Node.Inode > Inode) return NULL; + ent->Node.ReferenceCount ++; + return &ent->Node; + } + + return NULL; // Should never be reached +} + +/** + * \fn tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node) + */ +tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node) +{ + tInodeCache *cache; + tCachedInode *newEnt, *ent, *prev; + + cache = Inode_int_GetFSCache(Handle); + if(!cache) return NULL; + + if(Node->Inode > cache->MaxCached) + cache->MaxCached = Node->Inode; + + // Search Cache + ent = cache->FirstNode; + prev = (tCachedInode*) &cache->FirstNode; + for( ; ent; prev = ent, ent = ent->Next ) + { + if(ent->Node.Inode < Node->Inode) continue; + if(ent->Node.Inode == Node->Inode) { + ent->Node.ReferenceCount ++; + return &ent->Node; + } + break; + } + + // Create new entity + newEnt = malloc(sizeof(tCachedInode)); + newEnt->Next = ent; + memcpy(&newEnt->Node, Node, sizeof(tVFS_Node)); + prev->Next = newEnt; + + return &newEnt->Node; +} + +/** + * \fn void Inode_UncacheNode(int Handle, Uint64 Inode) + * \brief Dereferences/Removes a cached node + */ +void Inode_UncacheNode(int Handle, Uint64 Inode) +{ + tInodeCache *cache; + tCachedInode *ent, *prev; + + cache = Inode_int_GetFSCache(Handle); + if(!cache) return ; + + if(Inode > cache->MaxCached) return ; + + // Search Cache + ent = cache->FirstNode; + prev = (tCachedInode*) &cache->FirstNode; // Special case removal + for( ; ent; prev = ent, ent = ent->Next ) + { + if(ent->Node.Inode < Inode) continue; + if(ent->Node.Inode > Inode) return; + ent->Node.ReferenceCount --; + // Check if node needs to be freed + if(ent->Node.ReferenceCount == 0) + { + prev->Next = ent->Next; + if(ent->Node.Inode == cache->MaxCached) + { + if(ent != cache->FirstNode) + cache->MaxCached = prev->Node.Inode; + else + cache->MaxCached = 0; + } + + free(ent); + } + return ; + } + + return ; +} + +/** + * \fn void Inode_ClearCache(int Handle) + * \brief Removes a cache + */ +void Inode_ClearCache(int Handle) +{ + tInodeCache *cache; + tInodeCache *prev = NULL; + tCachedInode *ent, *next; + + // Find the cache + for( + cache = gVFS_InodeCache; + cache && cache->Handle < Handle; + prev = cache, cache = cache->Next + ); + if(!cache || cache->Handle != Handle) return; + + // Search Cache + ent = cache->FirstNode; + while( ent ) + { + ent->Node.ReferenceCount = 1; + next = ent->Next; + + if(ent->Node.Type && ent->Node.Type->Close) + ent->Node.Type->Close( &ent->Node ); + free(ent); + + ent = next; + } + + // Free Cache + if(prev == NULL) + gVFS_InodeCache = cache->Next; + else + prev->Next = cache->Next; + free(cache); +} + +/** + * \fn tInodeCache *Inode_int_GetFSCache(int Handle) + * \brief Gets a cache given it's handle + */ +tInodeCache *Inode_int_GetFSCache(int Handle) +{ + tInodeCache *cache = gVFS_InodeCache; + // Find Cache + for( ; cache; cache = cache->Next ) + { + if(cache->Handle > Handle) continue; + if(cache->Handle < Handle) { + Warning("Inode_int_GetFSCache - Handle %i not in cache\n", Handle); + return NULL; + } + break; + } + if(!cache) { + Warning("Inode_int_GetFSCache - Handle %i not in cache [NULL]\n", Handle); + return NULL; + } + + return cache; +} diff --git a/KernelLand/Kernel/vfs/open.c b/KernelLand/Kernel/vfs/open.c new file mode 100644 index 00000000..1536d1a0 --- /dev/null +++ b/KernelLand/Kernel/vfs/open.c @@ -0,0 +1,731 @@ +/* + * Acess2 VFS + * - Open, Close and ChDir + */ +#define DEBUG 0 +#include +#include "vfs.h" +#include "vfs_int.h" +#include "vfs_ext.h" +#include + +// === CONSTANTS === +#define OPEN_MOUNT_ROOT 1 +#define MAX_PATH_SLASHES 256 +#define MAX_NESTED_LINKS 4 +#define MAX_PATH_LEN 255 + +// === IMPORTS === +extern tVFS_Mount *gVFS_RootMount; +extern int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode); +extern tVFS_Node *VFS_MemFile_Create(const char *Path); + +// === PROTOTYPES === + int VFS_int_CreateHandle( tVFS_Node *Node, tVFS_Mount *Mount, int Mode ); + +// === CODE === +/** + * \fn char *VFS_GetAbsPath(const char *Path) + * \brief Create an absolute path from a relative one + */ +char *VFS_GetAbsPath(const char *Path) +{ + char *ret; + int pathLen = strlen(Path); + char *pathComps[MAX_PATH_SLASHES]; + char *tmpStr; + int iPos = 0; + int iPos2 = 0; + const char *chroot = *Threads_GetChroot(); + int chrootLen; + const char *cwd = *Threads_GetCWD(); + int cwdLen; + + ENTER("sPath", Path); + + // Memory File + if(Path[0] == '$') { + ret = malloc(strlen(Path)+1); + if(!ret) { + Log_Warning("VFS", "VFS_GetAbsPath: malloc() returned NULL"); + return NULL; + } + strcpy(ret, Path); + LEAVE('p', ret); + return ret; + } + + // - Fetch ChRoot + if( chroot == NULL ) + chroot = ""; + chrootLen = strlen(chroot); + + // Check if the path is already absolute + if(Path[0] == '/') { + ret = malloc(chrootLen + pathLen + 1); + if(!ret) { + Log_Warning("VFS", "VFS_GetAbsPath: malloc() returned NULL"); + return NULL; + } + strcpy(ret + chrootLen, Path); + } + else { + if(cwd == NULL) { + cwd = "/"; + cwdLen = 1; + } + else { + cwdLen = strlen(cwd); + } + // Prepend the current directory + ret = malloc(chrootLen + cwdLen + 1 + pathLen + 1 ); + strcpy(ret+chrootLen, cwd); + ret[cwdLen] = '/'; + strcpy(ret+chrootLen+cwdLen+1, Path); + //Log("ret = '%s'", ret); + } + + // Parse Path + pathComps[iPos++] = tmpStr = ret+chrootLen+1; + while(*tmpStr) + { + if(*tmpStr++ == '/') + { + pathComps[iPos++] = tmpStr; + if(iPos == MAX_PATH_SLASHES) { + LOG("Path '%s' has too many elements", Path); + free(ret); + LEAVE('n'); + return NULL; + } + } + } + pathComps[iPos] = NULL; + + // Cleanup + iPos2 = iPos = 0; + while(pathComps[iPos]) + { + tmpStr = pathComps[iPos]; + // Always Increment iPos + iPos++; + // .. + if(tmpStr[0] == '.' && tmpStr[1] == '.' && (tmpStr[2] == '/' || tmpStr[2] == '\0') ) + { + if(iPos2 != 0) + iPos2 --; + continue; + } + // . + if(tmpStr[0] == '.' && (tmpStr[1] == '/' || tmpStr[1] == '\0') ) + { + continue; + } + // Empty + if(tmpStr[0] == '/' || tmpStr[0] == '\0') + { + continue; + } + + // Set New Position + pathComps[iPos2] = tmpStr; + iPos2++; + } + pathComps[iPos2] = NULL; + + // Build New Path + iPos2 = chrootLen + 1; iPos = 0; + ret[0] = '/'; + while(pathComps[iPos]) + { + tmpStr = pathComps[iPos]; + while(*tmpStr && *tmpStr != '/') + { + ret[iPos2++] = *tmpStr; + tmpStr++; + } + ret[iPos2++] = '/'; + iPos++; + } + if(iPos2 > 1) + ret[iPos2-1] = 0; + else + ret[iPos2] = 0; + + // Prepend the chroot + if(chrootLen) + memcpy( ret, chroot, chrootLen ); + + LEAVE('s', ret); +// Log_Debug("VFS", "VFS_GetAbsPath: RETURN '%s'", ret); + return ret; +} + +/** + * \fn char *VFS_ParsePath(const char *Path, char **TruePath) + * \brief Parses a path, resolving sysmlinks and applying permissions + */ +tVFS_Node *VFS_ParsePath(const char *Path, char **TruePath, tVFS_Mount **MountPoint) +{ + tVFS_Mount *mnt, *longestMount; + int cmp, retLength = 0; + int ofs, nextSlash; + int iNestedLinks = 0; + tVFS_Node *curNode, *tmpNode; + char *tmp; + char path_buffer[MAX_PATH_LEN+1]; + + ENTER("sPath pTruePath", Path, TruePath); + + // HACK: Memory File + if(Threads_GetUID() == 0 && Path[0] == '$') { + if(TruePath) { + *TruePath = malloc(strlen(Path)+1); + strcpy(*TruePath, Path); + } + curNode = VFS_MemFile_Create(Path); + if(MountPoint) { + *MountPoint = NULL; + } + LEAVE('p', curNode); + return curNode; + } + +restart_parse: + // For root we always fast return + if(Path[0] == '/' && Path[1] == '\0') { + if(TruePath) { + *TruePath = malloc( gVFS_RootMount->MountPointLen+1 ); + strcpy(*TruePath, gVFS_RootMount->MountPoint); + } + if(MountPoint) *MountPoint = gVFS_RootMount; + LEAVE('p', gVFS_RootMount->RootNode); + return gVFS_RootMount->RootNode; + } + + // Check if there is anything mounted + if(!gVFS_Mounts) { + Log_Error("VFS", "VFS_ParsePath - No filesystems mounted"); + return NULL; + } + + // Find Mountpoint + longestMount = gVFS_RootMount; + for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next) + { + // Quick Check + if( Path[mnt->MountPointLen] != '/' && Path[mnt->MountPointLen] != '\0') + continue; + // Length Check - If the length is smaller than the longest match sofar + if(mnt->MountPointLen < longestMount->MountPointLen) continue; + // String Compare + cmp = strcmp(Path, mnt->MountPoint); + + #if OPEN_MOUNT_ROOT + // Fast Break - Request Mount Root + if(cmp == 0) { + if(TruePath) { + *TruePath = malloc( mnt->MountPointLen+1 ); + strcpy(*TruePath, mnt->MountPoint); + } + if(MountPoint) + *MountPoint = mnt; + LEAVE('p', mnt->RootNode); + return mnt->RootNode; + } + #endif + // Not a match, continue + if(cmp != '/') continue; + longestMount = mnt; + } + + // Save to shorter variable + mnt = longestMount; + + LOG("mnt = {MountPoint:\"%s\"}", mnt->MountPoint); + + // Initialise String + if(TruePath) + { + // Assumes that the resultant path (here) will not be > strlen(Path) + 1 + *TruePath = malloc( strlen(Path) + 1 ); + strcpy(*TruePath, mnt->MountPoint); + retLength = mnt->MountPointLen; + } + + curNode = mnt->RootNode; + curNode->ReferenceCount ++; + // Parse Path + ofs = mnt->MountPointLen+1; + for(; (nextSlash = strpos(&Path[ofs], '/')) != -1; ofs += nextSlash + 1) + { + char pathEle[nextSlash+1]; + + // Empty String + if(nextSlash == 0) continue; + + memcpy(pathEle, &Path[ofs], nextSlash); + pathEle[nextSlash] = 0; + + // Check permissions on root of filesystem + if( !VFS_CheckACL(curNode, VFS_PERM_EXECUTE) ) { + //Log("Permissions fail on '%s'", Path); + goto _error; + } + + // Check if the node has a FindDir method + if( !curNode->Type->FindDir ) + { + //Log("FindDir fail on '%s'", Path); + goto _error; + } + LOG("FindDir{=%p}(%p, '%s')", curNode->Type->FindDir, curNode, pathEle); + // Get Child Node + tmpNode = curNode->Type->FindDir(curNode, pathEle); + LOG("tmpNode = %p", tmpNode); + _CloseNode( curNode ); + curNode = tmpNode; + + // Error Check + if(!curNode) { + LOG("Node '%s' not found in dir '%s'", pathEle, Path); + goto _error; + } + + // Handle Symbolic Links + if(curNode->Flags & VFS_FFLAG_SYMLINK) { + if(TruePath) { + free(*TruePath); + *TruePath = NULL; + } + if(!curNode->Type || !curNode->Type->Read) { + Log_Warning("VFS", "VFS_ParsePath - Read of symlink node %p'%s' is NULL", + curNode, Path); + goto _error; + } + + if(iNestedLinks > MAX_NESTED_LINKS) { + Log_Notice("VFS", "VFS_ParsePath - Nested link limit exceeded"); + goto _error; + } + + // Parse Symlink Path + // - Just update the path variable and restart the function + // > Count nested symlinks and limit to some value (counteracts loops) + { + int remlen = strlen(Path) - (ofs + nextSlash); + if( curNode->Size + remlen > MAX_PATH_LEN ) { + Log_Warning("VFS", "VFS_ParsePath - Symlinked path too long"); + goto _error; + } + curNode->Type->Read( curNode, 0, curNode->Size, path_buffer ); + path_buffer[ curNode->Size ] = '\0'; + LOG("path_buffer = '%s'", path_buffer); + strcat(path_buffer, Path + ofs+nextSlash); + + Path = path_buffer; +// Log_Debug("VFS", "VFS_ParsePath: Symlink translated to '%s'", Path); + iNestedLinks ++; + } + + // EVIL: Goto :) + LOG("Symlink -> '%s', restart", Path); + goto restart_parse; + } + + // Handle Non-Directories + if( !(curNode->Flags & VFS_FFLAG_DIRECTORY) ) + { + Log_Warning("VFS", "VFS_ParsePath - Path segment is not a directory"); + goto _error; + } + + // Check if path needs extending + if(!TruePath) continue; + + // Increase buffer space + tmp = realloc( *TruePath, retLength + strlen(pathEle) + 1 + 1 ); + // Check if allocation succeeded + if(!tmp) { + Log_Warning("VFS", "VFS_ParsePath - Unable to reallocate true path buffer"); + goto _error; + } + *TruePath = tmp; + // Append to path + (*TruePath)[retLength] = '/'; + strcpy(*TruePath+retLength+1, pathEle); + + LOG("*TruePath = '%s'", *TruePath); + + // - Extend Path + retLength += nextSlash + 1; + } + + // Check final finddir call + if( !curNode->Type || !curNode->Type->FindDir ) { + Log_Warning("VFS", "VFS_ParsePath - FindDir doesn't exist for element of '%s'", Path); + goto _error; + } + + // Get last node + LOG("FindDir(%p, '%s')", curNode, &Path[ofs]); + tmpNode = curNode->Type->FindDir(curNode, &Path[ofs]); + LOG("tmpNode = %p", tmpNode); + // Check if file was found + if(!tmpNode) { + LOG("Node '%s' not found in dir '%s'", &Path[ofs], Path); + goto _error; + } + _CloseNode( curNode ); + + if(TruePath) + { + // Increase buffer space + tmp = realloc(*TruePath, retLength + strlen(&Path[ofs]) + 1 + 1); + // Check if allocation succeeded + if(!tmp) { + Log_Warning("VFS", "VFS_ParsePath - Unable to reallocate true path buffer"); + goto _error; + } + *TruePath = tmp; + // Append to path + (*TruePath)[retLength] = '/'; + strcpy(*TruePath + retLength + 1, &Path[ofs]); + // - Extend Path + //retLength += strlen(tmpNode->Name) + 1; + } + + if( MountPoint ) { + *MountPoint = mnt; + } + + LEAVE('p', tmpNode); + return tmpNode; + +_error: + _CloseNode( curNode ); + + if(TruePath && *TruePath) { + free(*TruePath); + *TruePath = NULL; + } + LEAVE('n'); + return NULL; +} + +/** + * \brief Create and return a handle number for the given node and mode + */ +int VFS_int_CreateHandle( tVFS_Node *Node, tVFS_Mount *Mount, int Mode ) +{ + int i; + + ENTER("pNode pMount xMode", Node, Mount, Mode); + + i = 0; + i |= (Mode & VFS_OPENFLAG_EXEC) ? VFS_PERM_EXECUTE : 0; + i |= (Mode & VFS_OPENFLAG_READ) ? VFS_PERM_READ : 0; + i |= (Mode & VFS_OPENFLAG_WRITE) ? VFS_PERM_WRITE : 0; + + LOG("i = 0b%b", i); + + // Permissions Check + if( !VFS_CheckACL(Node, i) ) { + _CloseNode( Node ); + Log_Log("VFS", "VFS_int_CreateHandle: Permissions Failed"); + errno = EACCES; + LEAVE_RET('i', -1); + } + + i = VFS_AllocHandle( !!(Mode & VFS_OPENFLAG_USER), Node, Mode ); + if( i < 0 ) { + Log_Notice("VFS", "VFS_int_CreateHandle: Out of handles"); + errno = ENFILE; + LEAVE_RET('i', -1); + } + + VFS_GetHandle(i)->Mount = Mount; + + LEAVE_RET('x', i); +} + +/** + * \fn int VFS_Open(const char *Path, Uint Mode) + * \brief Open a file + */ +int VFS_Open(const char *Path, Uint Flags) +{ + return VFS_OpenEx(Path, Flags, 0); +} + +int VFS_OpenEx(const char *Path, Uint Flags, Uint Mode) +{ + tVFS_Node *node; + tVFS_Mount *mnt; + char *absPath; + + ENTER("sPath xFlags oMode", Path, Flags); + + // Get absolute path + absPath = VFS_GetAbsPath(Path); + if(absPath == NULL) { + Log_Warning("VFS", "VFS_Open: Path expansion failed '%s'", Path); + LEAVE_RET('i', -1); + } + LOG("absPath = \"%s\"", absPath); + + // Parse path and get mount point + node = VFS_ParsePath(absPath, NULL, &mnt); + + // Create file if requested and it doesn't exist + if( !node && (Flags & VFS_OPENFLAG_CREATE) ) + { + // TODO: Translate `Mode` into ACL and node flags + // Get parent, create node + VFS_MkNod(absPath, 0); + node = VFS_ParsePath(absPath, NULL, &mnt); + } + + // Free generated path + free(absPath); + + // Check for error + if(!node) + { + LOG("Cannot find node"); + errno = ENOENT; + LEAVE_RET('i', -1); + } + + // Check for symlinks + if( !(Flags & VFS_OPENFLAG_NOLINK) && (node->Flags & VFS_FFLAG_SYMLINK) ) + { + char tmppath[node->Size+1]; + if( node->Size > MAX_PATH_LEN ) { + Log_Warning("VFS", "VFS_Open - Symlink is too long (%i)", node->Size); + LEAVE_RET('i', -1); + } + if( !node->Type || !node->Type->Read ) { + Log_Warning("VFS", "VFS_Open - No read method on symlink"); + LEAVE_RET('i', -1); + } + // Read symlink's path + node->Type->Read( node, 0, node->Size, tmppath ); + tmppath[ node->Size ] = '\0'; + _CloseNode( node ); + // Open the target + node = VFS_ParsePath(tmppath, NULL, &mnt); + if(!node) { + LOG("Cannot find symlink target node (%s)", tmppath); + errno = ENOENT; + LEAVE_RET('i', -1); + } + } + + LEAVE_RET('x', VFS_int_CreateHandle(node, mnt, Flags)); +} + + +/** + * \brief Open a file from an open directory + */ +int VFS_OpenChild(int FD, const char *Name, Uint Mode) +{ + tVFS_Handle *h; + tVFS_Node *node; + + ENTER("xFD sName xMode", FD, Name, Mode); + + // Get handle + h = VFS_GetHandle(FD); + if(h == NULL) { + Log_Warning("VFS", "VFS_OpenChild - Invalid file handle 0x%x", FD); + errno = EINVAL; + LEAVE_RET('i', -1); + } + + // Check for directory + if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) { + Log_Warning("VFS", "VFS_OpenChild - Passed handle is not a directory"); + errno = ENOTDIR; + LEAVE_RET('i', -1); + } + + // Sanity check + if( !h->Node->Type || !h->Node->Type->FindDir ) { + Log_Error("VFS", "VFS_OpenChild - Node does not have a type/is missing FindDir"); + errno = ENOTDIR; + LEAVE_RET('i', -1); + } + + // Find Child + node = h->Node->Type->FindDir(h->Node, Name); + if(!node) { + errno = ENOENT; + LEAVE_RET('i', -1); + } + + LEAVE_RET('x', VFS_int_CreateHandle(node, h->Mount, Mode)); +} + +int VFS_OpenInode(Uint32 Mount, Uint64 Inode, int Mode) +{ + tVFS_Mount *mnt; + tVFS_Node *node; + + ENTER("iMount XInode xMode", Mount, Inode, Mode); + + // Get mount point + mnt = VFS_GetMountByIdent(Mount); + if( !mnt ) { + LOG("Mount point ident invalid"); + errno = ENOENT; + LEAVE_RET('i', -1); + } + + // Does the filesystem support this? + if( !mnt->Filesystem->GetNodeFromINode ) { + LOG("Filesystem does not support inode accesses"); + errno = ENOENT; + LEAVE_RET('i', -1); + } + + // Get node + node = mnt->Filesystem->GetNodeFromINode(mnt->RootNode, Inode); + if( !node ) { + LOG("Unable to find inode"); + errno = ENOENT; + LEAVE_RET('i', -1); + } + + LEAVE_RET('x', VFS_int_CreateHandle(node, mnt, Mode)); +} + +/** + * \fn void VFS_Close(int FD) + * \brief Closes an open file handle + */ +void VFS_Close(int FD) +{ + tVFS_Handle *h; + + // Get handle + h = VFS_GetHandle(FD); + if(h == NULL) { + Log_Warning("VFS", "Invalid file handle passed to VFS_Close, 0x%x", FD); + return; + } + + #if VALIDATE_VFS_FUNCTIPONS + if(h->Node->Close && !MM_GetPhysAddr(h->Node->Close)) { + Log_Warning("VFS", "Node %p's ->Close method is invalid (%p)", + h->Node, h->Node->Close); + return ; + } + #endif + + _CloseNode(h->Node); + + h->Node = NULL; +} + +/** + * \brief Change current working directory + */ +int VFS_ChDir(const char *Dest) +{ + char *buf; + int fd; + tVFS_Handle *h; + + // Create Absolute + buf = VFS_GetAbsPath(Dest); + if(buf == NULL) { + Log_Notice("VFS", "VFS_ChDir: Path expansion failed"); + return -1; + } + + // Check if path exists + fd = VFS_Open(buf, VFS_OPENFLAG_EXEC); + if(fd == -1) { + Log_Notice("VFS", "VFS_ChDir: Path is invalid"); + return -1; + } + + // Get node so we can check for directory + h = VFS_GetHandle(fd); + if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) { + Log("VFS_ChDir: Path is not a directory"); + VFS_Close(fd); + return -1; + } + + // Close file + VFS_Close(fd); + + { + char **cwdptr = Threads_GetCWD(); + // Free old working directory + if( *cwdptr ) free( *cwdptr ); + // Set new + *cwdptr = buf; + } + + Log("Updated CWD to '%s'", buf); + + return 1; +} + +/** + * \fn int VFS_ChRoot(char *New) + * \brief Change current root directory + */ +int VFS_ChRoot(const char *New) +{ + char *buf; + int fd; + tVFS_Handle *h; + + if(New[0] == '/' && New[1] == '\0') + return 1; // What a useless thing to ask! + + // Create Absolute + buf = VFS_GetAbsPath(New); + if(buf == NULL) { + LOG("Path expansion failed"); + return -1; + } + + // Check if path exists + fd = VFS_Open(buf, VFS_OPENFLAG_EXEC); + if(fd == -1) { + LOG("Path is invalid"); + return -1; + } + + // Get node so we can check for directory + h = VFS_GetHandle(fd); + if( !(h->Node->Flags & VFS_FFLAG_DIRECTORY) ) { + LOG("Path is not a directory"); + VFS_Close(fd); + return -1; + } + + // Close file + VFS_Close(fd); + + // Update + { + char **chroot_ptr = Threads_GetChroot(); + if( *chroot_ptr ) free( *chroot_ptr ); + *chroot_ptr = buf; + } + + LOG("Updated Root to '%s'", buf); + + return 1; +} + +// === EXPORTS === +EXPORT(VFS_Open); +EXPORT(VFS_Close); diff --git a/KernelLand/Kernel/vfs/select.c b/KernelLand/Kernel/vfs/select.c new file mode 100644 index 00000000..439afc02 --- /dev/null +++ b/KernelLand/Kernel/vfs/select.c @@ -0,0 +1,507 @@ +/* + * Acess2 VFS + * - By thePowersGang (John Hodge) + * + * select.c + * - Implements the select() system call (and supporting code) + * + * TODO: Implment timeouts (via an alarm event?) + * TODO: Remove malloc for read/write queues + */ +#define DEBUG 0 +#include +#include "vfs.h" +#include "vfs_int.h" +#include "vfs_ext.h" +#include +#include +#include + +// === CONSTANTS === +#define NUM_THREADS_PER_ALLOC 4 + +// === IMPORTS === +extern tThread *Proc_GetCurThread(void); + +// === TYPES === +typedef struct sVFS_SelectListEnt tVFS_SelectListEnt; + +// === STRUCTURES === +struct sVFS_SelectListEnt +{ + tVFS_SelectListEnt *Next; + tThread *Threads[NUM_THREADS_PER_ALLOC]; +}; + +// NOTE: Typedef is in vfs.h +struct sVFS_SelectList +{ + tMutex Lock; + tVFS_SelectListEnt FirstEnt; +}; + +// === PROTOTYPES === +// int VFS_SelectNode(tVFS_Node *Node, enum eVFS_SelectTypes Type, tTime *Timeout); +// int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel); +// int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull); +// int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable); +// int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState); + int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed); + int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel); + int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel); + int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed); +void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread); +void VFS_int_Select_SignalAll(tVFS_SelectList *List); + +// === GLOBALS === + +// === FUNCTIONS === +int VFS_SelectNode(tVFS_Node *Node, int TypeFlags, tTime *Timeout, const char *Name) +{ + tThread *thisthread = Proc_GetCurThread(); + int ret, type; + + ENTER("pNode iTypeFlags pTimeout sName", Node, TypeFlags, Timeout, Name); + + // Initialise + for( type = 0; type < 3; type ++ ) + { + tVFS_SelectList **list; + int *flag, wanted, maxAllowed; + if( !(TypeFlags & (1 << type)) ) continue; + if( VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed) ) { + LEAVE('i', -1); + return -1; + } + + // Alloc if needed + if( !*list ) *list = calloc(1, sizeof(tVFS_SelectList)); + + VFS_int_Select_AddThread(*list, thisthread, maxAllowed); + if( *flag == wanted ) + { + VFS_int_Select_RemThread(*list, thisthread); + LEAVE('i', 1); + return 1; + } + } + + // - Fast return for polling + if( Timeout && *Timeout == 0 ) return 0; + + // Wait for things + if( !Timeout || *Timeout > 0 ) + { + LOG("Semaphore_Wait()"); + // TODO: Actual timeout + Threads_WaitEvents( THREAD_EVENT_VFS ); + } + + // Get return value + ret = 0; + for( type = 0; type < 3; type ++ ) + { + tVFS_SelectList **list; + int *flag, wanted, maxAllowed; + if( !(TypeFlags & (1 << type)) ) continue; + VFS_int_Select_GetType(type, Node, &list, &flag, &wanted, &maxAllowed); + LOG("VFS_int_Select_RemThread()"); + VFS_int_Select_RemThread(*list, thisthread); + ret = ret || *flag == wanted; + } + + LEAVE('i', ret); + return ret; +} + +int VFS_Select(int MaxHandle, fd_set *ReadHandles, fd_set *WriteHandles, fd_set *ErrHandles, tTime *Timeout, Uint32 ExtraEvents, BOOL IsKernel) +{ + tThread *thisthread = Proc_GetCurThread(); + int ret; + + ENTER("iMaxHandle pReadHandles pWriteHandles pErrHandles pTimeout bIsKernel", + MaxHandle, ReadHandles, WriteHandles, ErrHandles, Timeout, IsKernel); + + // Notes: The idea is to make sure we only enter wait (Threads_WaitEvents) + // if we are going to be woken up (either by an event at a later time, + // or by an event that happened while or before we were registering). + // Hence, register must happen _before_ we check the state flag + // (See VFS_int_Select_Register), that way either we pick up the flag, + // or the semaphore is incremeneted (or both, but never none) + + // Register with nodes + ret = VFS_int_Select_Register(thisthread, MaxHandle, ReadHandles, 0, IsKernel); + ret += VFS_int_Select_Register(thisthread, MaxHandle, WriteHandles, 1, IsKernel); + ret += VFS_int_Select_Register(thisthread, MaxHandle, ErrHandles, 2, IsKernel); + + LOG("Register ret = %i", ret); + + // If there were events waiting, de-register and return + if( ret > 0 ) + { + ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel); + ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel); + ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel); + LEAVE('i', ret); + return ret; + } + + // TODO: Implement timeout + LOG("Timeout = %p", Timeout); + + // Wait (only if there is no timeout, or it is greater than zero + if( !Timeout || *Timeout > 0 ) + { + // TODO: Timeout + // TODO: Allow extra events to be waited upon + Threads_WaitEvents( THREAD_EVENT_VFS|ExtraEvents ); + } + + // Fill output (modify *Handles) + // - Also, de-register + ret = VFS_int_Select_Deregister(thisthread, MaxHandle, ReadHandles, 0, IsKernel); + ret += VFS_int_Select_Deregister(thisthread, MaxHandle, WriteHandles, 1, IsKernel); + ret += VFS_int_Select_Deregister(thisthread, MaxHandle, ErrHandles, 2, IsKernel); + LEAVE('i', ret); + return ret; +} + +// Mark a node as having data ready for reading +int VFS_MarkAvaliable(tVFS_Node *Node, BOOL IsDataAvaliable) +{ + ENTER("pNode bIsDataAvaliable", Node, IsDataAvaliable); + Node->DataAvaliable = !!IsDataAvaliable; + if( Node->DataAvaliable ) + VFS_int_Select_SignalAll(Node->ReadThreads); + LEAVE('i', 0); + return 0; +} + +// Mark a node as having a full buffer +int VFS_MarkFull(tVFS_Node *Node, BOOL IsBufferFull) +{ + ENTER("pNode bIsBufferFull", Node, IsBufferFull); + Node->BufferFull = !!IsBufferFull; + if( !Node->BufferFull ) + VFS_int_Select_SignalAll(Node->WriteThreads); + LEAVE('i', 0); + return 0; +} + +// Mark a node as errored +int VFS_MarkError(tVFS_Node *Node, BOOL IsErrorState) +{ + ENTER("pNode bIsErrorState", Node, IsErrorState); + Node->ErrorOccurred = !!IsErrorState; + if( Node->ErrorOccurred ) + VFS_int_Select_SignalAll(Node->ErrorThreads); + LEAVE('i', 0); + return 0; +} + +// --- Internal --- +int VFS_int_Select_GetType(int Type, tVFS_Node *Node, tVFS_SelectList ***List, int **Flag, int *WantedFlag, int *MaxAllowed) +{ + // Get the type of the listen + switch(Type) + { + case 0: // Read + if(List) *List = &Node->ReadThreads; + if(Flag) *Flag = &Node->DataAvaliable; + if(WantedFlag) *WantedFlag = 1; + if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for read + break; + case 1: // Write + if(List) *List = &Node->WriteThreads; + if(Flag) *Flag = &Node->BufferFull; + if(WantedFlag) *WantedFlag = 0; + if(MaxAllowed) *MaxAllowed = 1; // Max of 1 for write + break; + case 2: // Error + if(List) *List = &Node->ErrorThreads; + if(Flag) *Flag = &Node->ErrorOccurred; + if(WantedFlag) *WantedFlag = 1; + if(MaxAllowed) *MaxAllowed = -1; // No max for error listeners + break; + default: + Log_Error("VFS", "VFS_int_Select_GetType: BUG CHECK, Unknown Type %i", Type); + return 1; + } + return 0; +} + +/** + * \return Number of files with an action + */ +int VFS_int_Select_Register(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel) +{ + int i, numFlagged = 0; + tVFS_SelectList **list; + int *flag, wantedFlagValue; + int maxAllowed; + + if( !Handles ) return 0; + + ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel); + + for( i = 0; i < MaxHandle; i ++ ) + { + tVFS_Handle *handle; + + // Is the descriptor set + if( !FD_ISSET(i, Handles) ) continue; + LOG("FD #%i", i); + + handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) ); + // Is the handle valid? + if( !handle || !handle->Node ) + { + if( Type == 2 ) { // Bad FD counts as an error + numFlagged ++; + } + else { + FD_CLR(i, Handles); + } + continue; + } + + // Get the type of the listen + if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, &maxAllowed) ) { + LEAVE('i', 0); + return 0; + } + + // Alloc if needed + if( !*list ) { + *list = calloc(1, sizeof(tVFS_SelectList)); + } + + // Register + if( VFS_int_Select_AddThread(*list, Thread, maxAllowed ) ) + { + // Oops, error (or just no space) + FD_CLR(i, Handles); + } + + // Check for the flag + if( !!*flag == !!wantedFlagValue ) + numFlagged ++; + } + + LEAVE('i', numFlagged); + + return numFlagged; +} +/** + * \return Number of files with an action + */ +int VFS_int_Select_Deregister(tThread *Thread, int MaxHandle, fd_set *Handles, int Type, BOOL IsKernel) +{ + int i, numFlagged = 0; + tVFS_SelectList **list; + int *flag, wantedFlagValue; + + if( !Handles ) return 0; + + ENTER("pThread iMaxHandle pHandles iType iIsKernel", Thread, MaxHandle, Handles, Type, IsKernel); + + for( i = 0; i < MaxHandle; i ++ ) + { + tVFS_Handle *handle; + + // Is the descriptor set + if( !FD_ISSET(i, Handles) ) continue; + LOG("FD #%i", i); + + handle = VFS_GetHandle( i | (IsKernel?VFS_KERNEL_FLAG:0) ); + // Is the handle valid? + if( !handle || !handle->Node ) + { + if( Type == 2 ) { // Bad FD counts as an error + numFlagged ++; + } + else { + FD_CLR(i, Handles); + } + continue; + } + + // Get the type of the listen + + // Get the type of the listen + if( VFS_int_Select_GetType(Type, handle->Node, &list, &flag, &wantedFlagValue, NULL) ) { + LEAVE('i', 0); + return 0; + } + + // Remove + VFS_int_Select_RemThread(*list, Thread ); + + // Check for the flag + if( !!*flag == !!wantedFlagValue ) { + numFlagged ++; + } + else { + FD_CLR(i, Handles); + } + } + + LEAVE('i', numFlagged); + + return numFlagged; +} + +/** + * \return Boolean failure + */ +int VFS_int_Select_AddThread(tVFS_SelectList *List, tThread *Thread, int MaxAllowed) +{ + int i, count = 0; + tVFS_SelectListEnt *block, *prev; + + ENTER("pList pThread iMaxAllowed", List, Thread, MaxAllowed); + + // Lock to avoid concurrency issues + Mutex_Acquire(&List->Lock); + + block = &List->FirstEnt; + + // Look for free space + do + { + for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ ) + { + if( block->Threads[i] == NULL ) + { + block->Threads[i] = Thread; + Mutex_Release(&List->Lock); + LEAVE('i', 0); + return 0; + } + count ++; + if( MaxAllowed && count >= MaxAllowed ) { + LEAVE('i', 1); + return 1; + } + } + + prev = block; + block = block->Next; + } while(block); + + LOG("New block"); + + // Create new block + block = malloc( sizeof(tVFS_SelectListEnt) ); + if( !block ) { + Log_Warning("VFS", "VFS_int_Select_AddThread: malloc() failed"); + Mutex_Release(&List->Lock); + return -1; + } + block->Next = NULL; + block->Threads[0] = Thread; + for( i = 1; i < NUM_THREADS_PER_ALLOC; i ++ ) + { + block->Threads[i] = NULL; + } + + // Add to list + prev->Next = block; + + // Release + Mutex_Release(&List->Lock); + + LEAVE('i', 0); + return 0; +} + +void VFS_int_Select_RemThread(tVFS_SelectList *List, tThread *Thread) +{ + int i; + tVFS_SelectListEnt *block, *prev = NULL; + + ENTER("pList pThread", List, Thread); + + // Lock to avoid concurrency issues + Mutex_Acquire(&List->Lock); + + block = &List->FirstEnt; + + // Look for the thread + do + { + for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ ) + { + if( block->Threads[i] == Thread ) + { + block->Threads[i] = NULL; + + // Check if this block is empty + if( block != &List->FirstEnt ) + { + for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ ) + if( block->Threads[i] ) + break; + // If empty, free it + if( i == NUM_THREADS_PER_ALLOC ) { + LOG("Deleting block"); + prev->Next = block->Next; + free(block); + } + //TODO: If not empty, check if it can be merged downwards + } + + Mutex_Release(&List->Lock); + LEAVE('-'); + return ; + } + } + + prev = block; + block = block->Next; + } while(block); + + // Not on list, is this an error? + + Mutex_Release(&List->Lock); + + LOG("Not on list"); + LEAVE('-'); +} + +/** + * \brief Signal all threads on a list + */ +void VFS_int_Select_SignalAll(tVFS_SelectList *List) +{ + int i; + tVFS_SelectListEnt *block; + + if( !List ) return ; + + ENTER("pList", List); + + // Lock to avoid concurrency issues + Mutex_Acquire(&List->Lock); + + block = &List->FirstEnt; + + // Look for the thread + do + { + for( i = 0; i < NUM_THREADS_PER_ALLOC; i ++ ) + { + if( block->Threads[i] ) + { + LOG("block(%p)->Threads[%i] = %p", block, i, block->Threads[i]); + Threads_PostEvent( block->Threads[i], THREAD_EVENT_VFS ); + } + } + + block = block->Next; + } while(block); + + Mutex_Release(&List->Lock); + + LEAVE('-'); +} diff --git a/KernelLand/Kernel/workqueue.c b/KernelLand/Kernel/workqueue.c new file mode 100644 index 00000000..b1a63851 --- /dev/null +++ b/KernelLand/Kernel/workqueue.c @@ -0,0 +1,73 @@ +/* + * Acess2 Kernel + * - By John Hodge (thePowersGang) + * + * workqueue.c + * - Worker FIFO Queue (Single Consumer, Interrupt Producer) + */ +#include +#include +#include + +// === CODE === +void Workqueue_Init(tWorkqueue *Queue, const char *Name, size_t NextOfset) +{ + Queue->Name = Name; + Queue->NextOffset = NextOfset; +} + +void *Workqueue_GetWork(tWorkqueue *Queue) +{ + tThread *us; + + for( ;; ) + { + // Check for work + SHORTLOCK(&Queue->Protector); + if(Queue->Head) + { + void *ret = Queue->Head; + Queue->Head = *( (void**)ret + Queue->NextOffset/sizeof(void*) ); + if(Queue->Tail == ret) + Queue->Tail = NULL; + SHORTREL(&Queue->Protector); + return ret; + } + + // Go to sleep + SHORTLOCK(&glThreadListLock); + us = Threads_RemActive(); + us->WaitPointer = Queue; + us->Status = THREAD_STAT_QUEUESLEEP; + Queue->Sleeper = us; + SHORTREL(&Queue->Protector); + SHORTREL(&glThreadListLock); + + // Yield and sleep + Threads_Yield(); + if(us->Status == THREAD_STAT_QUEUESLEEP) { + // Why are we awake?! + } + + us->WaitPointer = NULL; + } +} + +void Workqueue_AddWork(tWorkqueue *Queue, void *Ptr) +{ + SHORTLOCK(&Queue->Protector); + + if( Queue->Tail ) + *( (void**)Queue->Tail + Queue->NextOffset/sizeof(void*) ) = Ptr; + else + Queue->Head = Ptr; + Queue->Tail = Ptr; + + if( Queue->Sleeper ) + { + if( Queue->Sleeper->Status != THREAD_STAT_ACTIVE ) + Threads_AddActive(Queue->Sleeper); + Queue->Sleeper = NULL; + } + SHORTREL(&Queue->Protector); +} diff --git a/KernelLand/Modules/Display/BochsGA/Makefile b/KernelLand/Modules/Display/BochsGA/Makefile new file mode 100644 index 00000000..2df219af --- /dev/null +++ b/KernelLand/Modules/Display/BochsGA/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = bochsvbe.o +NAME = BochsGA + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Display/BochsGA/bochsvbe.c b/KernelLand/Modules/Display/BochsGA/bochsvbe.c new file mode 100644 index 00000000..806c1c13 --- /dev/null +++ b/KernelLand/Modules/Display/BochsGA/bochsvbe.c @@ -0,0 +1,362 @@ +/** + * Acess2 Bochs graphics adapter Driver + * - By John Hodge (thePowersGang) + * + * bochsvbe.c + * - Driver core + */ +#define DEBUG 0 +#define VERSION VER2(0,10) + +#include +#include +#include +#include +#include +#include +#include + +// === TYPES === +typedef struct sBGA_Mode { + Uint16 width; + Uint16 height; + Uint16 bpp; + Uint32 fbSize; +} tBGA_Mode; + +// === CONSTANTS === +#define BGA_LFB_MAXSIZE (1024*768*4) +#define VBE_DISPI_BANK_ADDRESS 0xA0000 +#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 +#define VBE_DISPI_IOPORT_INDEX 0x01CE +#define VBE_DISPI_IOPORT_DATA 0x01CF +#define VBE_DISPI_DISABLED 0x00 +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_LFB_ENABLED 0x40 +#define VBE_DISPI_NOCLEARMEM 0x80 +enum { + VBE_DISPI_INDEX_ID, + VBE_DISPI_INDEX_XRES, + VBE_DISPI_INDEX_YRES, + VBE_DISPI_INDEX_BPP, + VBE_DISPI_INDEX_ENABLE, + VBE_DISPI_INDEX_BANK, + VBE_DISPI_INDEX_VIRT_WIDTH, + VBE_DISPI_INDEX_VIRT_HEIGHT, + VBE_DISPI_INDEX_X_OFFSET, + VBE_DISPI_INDEX_Y_OFFSET +}; + +extern void MM_DumpTables(tVAddr Start, tVAddr End); + +// === PROTOTYPES === +// Driver + int BGA_Install(char **Arguments); +void BGA_Uninstall(); +// Internal +void BGA_int_WriteRegister(Uint16 reg, Uint16 value); +Uint16 BGA_int_ReadRegister(Uint16 reg); +void BGA_int_SetBank(Uint16 bank); +void BGA_int_SetMode(Uint16 width, Uint16 height); + int BGA_int_UpdateMode(int id); + int BGA_int_FindMode(tVideo_IOCtl_Mode *info); + int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info); + int BGA_int_MapFB(void *Dest); +// Filesystem +Uint64 BGA_Read(tVFS_Node *Node, Uint64 off, Uint64 len, void *buffer); +Uint64 BGA_Write(tVFS_Node *Node, Uint64 off, Uint64 len, const void *buffer); + int BGA_IOCtl(tVFS_Node *Node, int ID, void *Data); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, BochsGA, BGA_Install, NULL, "PCI", NULL); +tVFS_NodeType gBGA_NodeType = { + .Read = BGA_Read, + .Write = BGA_Write, + .IOCtl = BGA_IOCtl + }; +tDevFS_Driver gBGA_DriverStruct = { + NULL, "BochsGA", + {.Type = &gBGA_NodeType} + }; + int giBGA_CurrentMode = -1; +tVideo_IOCtl_Pos gBGA_CursorPos = {-1,-1}; +Uint *gBGA_Framebuffer; +const tBGA_Mode *gpBGA_CurrentMode; +const tBGA_Mode gBGA_Modes[] = { + {640,480,32, 640*480*4}, + {800,600,32, 800*600*4}, + {1024,768,32, 1024*768*4} +}; +#define BGA_MODE_COUNT (sizeof(gBGA_Modes)/sizeof(gBGA_Modes[0])) +tDrvUtil_Video_BufInfo gBGA_DrvUtil_BufInfo; + +// === CODE === +/** + * \fn int BGA_Install(char **Arguments) + */ +int BGA_Install(char **Arguments) +{ + int version = 0; + tPAddr base; + tPCIDev dev; + + // Check BGA Version + version = BGA_int_ReadRegister(VBE_DISPI_INDEX_ID); + LOG("version = 0x%x", version); + + // NOTE: This driver was written for BGA versions >= 0xBOC2 + // NOTE: However, Qemu is braindead and doesn't return the actual version + if( version != 0xB0C0 && ((version & 0xFFF0) != 0xB0C0 || version < 0xB0C2) ) { + Log_Warning("BGA", "Bochs Adapter Version is not compatible (need >= 0xB0C2), instead 0x%x", version); + return MODULE_ERR_NOTNEEDED; + } + + // Get framebuffer base + dev = PCI_GetDevice(0x1234, 0x1111, 0); + if(dev == -1) + base = VBE_DISPI_LFB_PHYSICAL_ADDRESS; + else + base = PCI_GetBAR(dev, 0); + + // Map Framebuffer to hardware address + gBGA_Framebuffer = (void *) MM_MapHWPages(base, 768); // 768 pages (3Mb) + + // Install Device + if( DevFS_AddDevice( &gBGA_DriverStruct ) == -1 ) + { + Log_Warning("BGA", "Unable to register with DevFS, maybe already loaded?"); + return MODULE_ERR_MISC; + } + + return MODULE_ERR_OK; +} + +/** + * \brief Clean up driver resources before destruction + */ +void BGA_Uninstall(void) +{ + DevFS_DelDevice( &gBGA_DriverStruct ); + MM_UnmapHWPages( (tVAddr)gBGA_Framebuffer, 768 ); +} + +/** + * \fn Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) + * \brief Read from the framebuffer + */ +Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) +{ + // Check Mode + if(giBGA_CurrentMode == -1) return -1; + + // Check Offset and Length against Framebuffer Size + if(off+len > gpBGA_CurrentMode->fbSize) + return -1; + + // Copy from Framebuffer + memcpy(buffer, (void*)((Uint)gBGA_Framebuffer + (Uint)off), len); + return len; +} + +/** + * \brief Write to the framebuffer + */ +Uint64 BGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + if( giBGA_CurrentMode == -1 ) BGA_int_UpdateMode(0); + return DrvUtil_Video_WriteLFB(&gBGA_DrvUtil_BufInfo, Offset, Length, Buffer); +} + +const char *csaBGA_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL}; +/** + * \brief Handle messages to the device + */ +int BGA_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + int ret = -2; + ENTER("pNode iId pData", Node, ID, Data); + + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_VIDEO, "BochsGA", VERSION, csaBGA_IOCtls); + + case VIDEO_IOCTL_GETSETMODE: + if( Data ) BGA_int_UpdateMode(*(int*)(Data)); + ret = giBGA_CurrentMode; + break; + + case VIDEO_IOCTL_FINDMODE: + ret = BGA_int_FindMode((tVideo_IOCtl_Mode*)Data); + break; + + case VIDEO_IOCTL_MODEINFO: + ret = BGA_int_ModeInfo((tVideo_IOCtl_Mode*)Data); + break; + + case VIDEO_IOCTL_SETBUFFORMAT: + DrvUtil_Video_RemoveCursor( &gBGA_DrvUtil_BufInfo ); + ret = gBGA_DrvUtil_BufInfo.BufferFormat; + if(Data) + gBGA_DrvUtil_BufInfo.BufferFormat = *(int*)Data; + if(gBGA_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) + DrvUtil_Video_SetCursor( &gBGA_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor ); + break; + + case VIDEO_IOCTL_SETCURSOR: + DrvUtil_Video_RemoveCursor( &gBGA_DrvUtil_BufInfo ); + gBGA_CursorPos.x = ((tVideo_IOCtl_Pos*)Data)->x; + gBGA_CursorPos.y = ((tVideo_IOCtl_Pos*)Data)->y; + if(gBGA_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) + DrvUtil_Video_DrawCursor( + &gBGA_DrvUtil_BufInfo, + gBGA_CursorPos.x*giVT_CharWidth, + gBGA_CursorPos.y*giVT_CharHeight + ); + else + DrvUtil_Video_DrawCursor( + &gBGA_DrvUtil_BufInfo, + gBGA_CursorPos.x, gBGA_CursorPos.y + ); + break; + + default: + LEAVE('i', -2); + return -2; + } + + LEAVE('i', ret); + return ret; +} + +//== Internal Functions == +/** + * \brief Writes to a BGA register + */ +void BGA_int_WriteRegister(Uint16 reg, Uint16 value) +{ + outw(VBE_DISPI_IOPORT_INDEX, reg); + outw(VBE_DISPI_IOPORT_DATA, value); +} + +Uint16 BGA_int_ReadRegister(Uint16 reg) +{ + outw(VBE_DISPI_IOPORT_INDEX, reg); + return inw(VBE_DISPI_IOPORT_DATA); +} + +/** + * \brief Sets the video mode (32bpp only) + */ +void BGA_int_SetMode(Uint16 Width, Uint16 Height) +{ + ENTER("iWidth iHeight", Width, Height); + BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED); + BGA_int_WriteRegister(VBE_DISPI_INDEX_XRES, Width); + BGA_int_WriteRegister(VBE_DISPI_INDEX_YRES, Height); + BGA_int_WriteRegister(VBE_DISPI_INDEX_BPP, 32); + BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM | VBE_DISPI_LFB_ENABLED); + LEAVE('-'); +} + +/** + * \fn int BGA_int_UpdateMode(int id) + * \brief Set current vide mode given a mode id + */ +int BGA_int_UpdateMode(int id) +{ + // Sanity Check + if(id < 0 || id >= BGA_MODE_COUNT) return -1; + + BGA_int_SetMode( + gBGA_Modes[id].width, + gBGA_Modes[id].height); + + gBGA_DrvUtil_BufInfo.Framebuffer = gBGA_Framebuffer; + gBGA_DrvUtil_BufInfo.Pitch = gBGA_Modes[id].width * 4; + gBGA_DrvUtil_BufInfo.Width = gBGA_Modes[id].width; + gBGA_DrvUtil_BufInfo.Height = gBGA_Modes[id].height; + gBGA_DrvUtil_BufInfo.Depth = gBGA_Modes[id].bpp; + + giBGA_CurrentMode = id; + gpBGA_CurrentMode = &gBGA_Modes[id]; + return id; +} + +/** + * \fn int BGA_int_FindMode(tVideo_IOCtl_Mode *info) + * \brief Find a mode matching the given options + */ +int BGA_int_FindMode(tVideo_IOCtl_Mode *info) +{ + int i; + int best = 0, bestFactor = 1000; + int tmp; + int rqdProduct = info->width * info->height; + + ENTER("pinfo", info); + LOG("info = {width:%i,height:%i,bpp:%i})\n", info->width, info->height, info->bpp); + + for(i = 0; i < BGA_MODE_COUNT; i++) + { + #if DEBUG >= 2 + LOG("Mode %i (%ix%i,%ibpp), ", i, gBGA_Modes[i].width, gBGA_Modes[i].height, gBGA_Modes[i].bpp); + #endif + + if( gBGA_Modes[i].bpp != info->bpp ) + continue ; + + // Ooh! A perfect match + if(gBGA_Modes[i].width == info->width && gBGA_Modes[i].height == info->height) + { + #if DEBUG >= 2 + LOG("Perfect"); + #endif + best = i; + break; + } + + + // If not, how close are we? + tmp = gBGA_Modes[i].width * gBGA_Modes[i].height - rqdProduct; + tmp = tmp < 0 ? -tmp : tmp; // tmp = ABS(tmp) + + #if DEBUG >= 2 + LOG("tmp = %i", tmp); + #endif + + if(tmp < bestFactor) + { + bestFactor = tmp; + best = i; + } + } + + info->id = best; + info->width = gBGA_Modes[best].width; + info->height = gBGA_Modes[best].height; + info->bpp = gBGA_Modes[best].bpp; + + LEAVE('i', best); + return best; +} + +/** + * \fn int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info) + * \brief Get mode information + */ +int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info) +{ + // Sanity Check + //if( !MM_IsUser( (Uint)info, sizeof(tVideo_IOCtl_Mode) ) ) { + // return -EINVAL; + //} + + if(info->id < 0 || info->id >= BGA_MODE_COUNT) return -1; + + info->width = gBGA_Modes[info->id].width; + info->height = gBGA_Modes[info->id].height; + info->bpp = gBGA_Modes[info->id].bpp; + + return 1; +} + diff --git a/KernelLand/Modules/Display/Makefile.tpl b/KernelLand/Modules/Display/Makefile.tpl new file mode 100644 index 00000000..b5431609 --- /dev/null +++ b/KernelLand/Modules/Display/Makefile.tpl @@ -0,0 +1,3 @@ +CATEGORY = Video + +-include ../../Makefile.tpl diff --git a/KernelLand/Modules/Display/PL110/Makefile b/KernelLand/Modules/Display/PL110/Makefile new file mode 100644 index 00000000..0f90347b --- /dev/null +++ b/KernelLand/Modules/Display/PL110/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = main.o +NAME = PL110 + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Display/PL110/main.c b/KernelLand/Modules/Display/PL110/main.c new file mode 100644 index 00000000..c3b6e526 --- /dev/null +++ b/KernelLand/Modules/Display/PL110/main.c @@ -0,0 +1,345 @@ +/** + * Acess2 ARM PrimeCell Colour LCD Controller (PL110) Driver + * - By John Hodge (thePowersGang) + * + * main.c + * - Driver core + * + * + * NOTE: The PL110 is set to 24bpp, but these are stored as 32-bit words. + * This corresponds to the Acess 32bpp mode, as the Acess 24bpp is packed + */ +#define DEBUG 0 +#define VERSION ((0<<8)|10) +#include +#include +#include +#include +#include +#include +#include +#include +#include // ARM Arch + +#define ABS(a) ((a)>0?(a):-(a)) + +// === TYPEDEFS === +typedef struct sPL110 tPL110; + +struct sPL110 +{ + Uint32 LCDTiming0; + Uint32 LCDTiming1; + Uint32 LCDTiming2; + Uint32 LCDTiming3; + + Uint32 LCDUPBase; + Uint32 LCDLPBase; + Uint32 LCDIMSC; + Uint32 LCDControl; + Uint32 LCDRIS; + Uint32 LCDMIS; + Uint32 LCDICR; + Uint32 LCDUPCurr; + Uint32 LCDLPCurr; +}; + +#ifndef PL110_BASE +#define PL110_BASE 0x10020000 // Integrator +#endif + +// === CONSTANTS === +const struct { + short W, H; +} caPL110_Modes[] = { + {640,480}, + {800,600}, + {1024,768} // MAX +}; +const int ciPL110_ModeCount = sizeof(caPL110_Modes)/sizeof(caPL110_Modes[0]); + +// === PROTOTYPES === +// Driver + int PL110_Install(char **Arguments); +void PL110_Uninstall(); +// Internal +// Filesystem +Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); +Uint64 PL110_Write(tVFS_Node *node, Uint64 off, Uint64 len, const void *buffer); + int PL110_IOCtl(tVFS_Node *node, int id, void *data); +// -- Internals + int PL110_int_SetResolution(int W, int H); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, PL110, PL110_Install, NULL, NULL); +tVFS_NodeType gPL110_DevNodeType = { + .Read = PL110_Read, + .Write = PL110_Write, + .IOCtl = PL110_IOCtl + }; +tDevFS_Driver gPL110_DriverStruct = { + NULL, "PL110", + {.Type = &gPL110_DevNodeType} +}; +// -- Options +tPAddr gPL110_PhysBase = PL110_BASE; + int gbPL110_IsVersatile = 1; +// -- KeyVal parse rules +const tKeyVal_ParseRules gPL110_KeyValueParser = { + NULL, + { + {"Base", "P", &gPL110_PhysBase}, + {"IsVersatile", "i", &gbPL110_IsVersatile}, + {NULL, NULL, NULL} + } +}; +// -- Driver state + int giPL110_CurrentMode = 0; + int giPL110_BufferMode; + int giPL110_Width = 640; + int giPL110_Height = 480; +size_t giPL110_FramebufferSize; +tPL110 *gpPL110_IOMem; +tPAddr gPL110_FramebufferPhys; +void *gpPL110_Framebuffer; +// -- Misc +tDrvUtil_Video_BufInfo gPL110_DrvUtil_BufInfo; +tVideo_IOCtl_Pos gPL110_CursorPos; + +// === CODE === +/** + */ +int PL110_Install(char **Arguments) +{ +// KeyVal_Parse(&gPL110_KeyValueParser, Arguments); + + gpPL110_IOMem = (void*)MM_MapHWPages(gPL110_PhysBase, 1); + + PL110_int_SetResolution(caPL110_Modes[0].W, caPL110_Modes[0].H); + + DevFS_AddDevice( &gPL110_DriverStruct ); + + return 0; +} + +/** + * \brief Clean up resources for driver unloading + */ +void PL110_Uninstall() +{ +} + +/** + * \brief Read from the framebuffer + */ +Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) +{ + return 0; +} + +/** + * \brief Write to the framebuffer + */ +Uint64 PL110_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + gPL110_DrvUtil_BufInfo.BufferFormat = giPL110_BufferMode; + return DrvUtil_Video_WriteLFB(&gPL110_DrvUtil_BufInfo, Offset, Length, Buffer); +} + +const char *csaPL110_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL}; + +/** + * \brief Handle messages to the device + */ +int PL110_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + int ret = -2; + ENTER("pNode iID pData", Node, ID, Data); + + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaPL110_IOCtls); + + case VIDEO_IOCTL_SETBUFFORMAT: + DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo ); + ret = giPL110_BufferMode; + if(Data) giPL110_BufferMode = *(int*)Data; + if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) + DrvUtil_Video_SetCursor( &gPL110_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor ); + break; + + case VIDEO_IOCTL_GETSETMODE: + if(Data) + { + int newMode; + + if( !CheckMem(Data, sizeof(int)) ) + LEAVE_RET('i', -1); + + newMode = *(int*)Data; + + if(newMode < 0 || newMode >= ciPL110_ModeCount) + LEAVE_RET('i', -1); + + if(newMode != giPL110_CurrentMode) + { + giPL110_CurrentMode = newMode; + PL110_int_SetResolution( caPL110_Modes[newMode].W, caPL110_Modes[newMode].H ); + } + } + ret = giPL110_CurrentMode; + break; + + case VIDEO_IOCTL_FINDMODE: + { + tVideo_IOCtl_Mode *mode = Data; + int closest, closestArea, reqArea = 0; + if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode))) + LEAVE_RET('i', -1); + if( mode->bpp != 32 ) + LEAVE_RET('i', 0); + if( mode->flags != 0 ) + LEAVE_RET('i', 0); + + ret = 0; + + for( int i = 0; i < ciPL110_ModeCount; i ++ ) + { + int area; + if(mode->width == caPL110_Modes[i].W && mode->height == caPL110_Modes[i].H) { + mode->id = i; + ret = 1; + break; + } + + area = caPL110_Modes[i].W * caPL110_Modes[i].H; + if(!reqArea) { + reqArea = mode->width * mode->height; + closest = i; + closestArea = area; + } + else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) { + closest = i; + closestArea = area; + } + } + + if( ret == 0 ) + { + mode->id = closest; + ret = 1; + } + mode->width = caPL110_Modes[mode->id].W; + mode->height = caPL110_Modes[mode->id].H; + break; + } + + case VIDEO_IOCTL_MODEINFO: + { + tVideo_IOCtl_Mode *mode = Data; + if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode))) + LEAVE_RET('i', -1); + if(mode->id < 0 || mode->id >= ciPL110_ModeCount) + LEAVE_RET('i', 0); + + + mode->bpp = 32; + mode->flags = 0; + mode->width = caPL110_Modes[mode->id].W; + mode->height = caPL110_Modes[mode->id].H; + + ret = 1; + break; + } + + case VIDEO_IOCTL_SETCURSOR: + if( !Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) ) + LEAVE_RET('i', -1); + + DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo ); + + gPL110_CursorPos = *(tVideo_IOCtl_Pos*)Data; + if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) + DrvUtil_Video_DrawCursor( + &gPL110_DrvUtil_BufInfo, + gPL110_CursorPos.x*giVT_CharWidth, + gPL110_CursorPos.y*giVT_CharHeight + ); + else + DrvUtil_Video_DrawCursor( + &gPL110_DrvUtil_BufInfo, + gPL110_CursorPos.x, + gPL110_CursorPos.y + ); + break; + + default: + LEAVE('i', -2); + return -2; + } + + LEAVE('i', ret); + return ret; +} + +// +// +// + +/** + * \brief Set the LCD controller resolution + * \param W Width (aligned to 16 pixels, cipped to 1024) + * \param H Height (clipped to 768) + * \return Boolean failure + */ +int PL110_int_SetResolution(int W, int H) +{ + W = (W + 15) & ~0xF; + if(W <= 0 || H <= 0) { + Log_Warning("PL110", "Attempted to set invalid resolution (%ix%i)", W, H); + return 1; + } + if(W > 1024) W = 1024; + if(H > 768) H = 768; + + gpPL110_IOMem->LCDTiming0 = ((W/16)-1) << 2; + gpPL110_IOMem->LCDTiming1 = H-1; + gpPL110_IOMem->LCDTiming2 = (14 << 27); + gpPL110_IOMem->LCDTiming3 = 0; + + if( gpPL110_Framebuffer ) { + MM_UnmapHWPages((tVAddr)gpPL110_Framebuffer, (giPL110_FramebufferSize+0xFFF)>>12); + } + giPL110_FramebufferSize = W*H*4; + + gpPL110_Framebuffer = (void*)MM_AllocDMA( (giPL110_FramebufferSize+0xFFF)>>12, 32, &gPL110_FramebufferPhys ); + gpPL110_IOMem->LCDUPBase = gPL110_FramebufferPhys; + gpPL110_IOMem->LCDLPBase = 0; + + // Power on, BGR mode, ???, ???, enabled + Uint32 controlWord = (1 << 11)|(1 << 8)|(1 << 5)|(5 << 1)|1; + // According to qemu, the Versatile version has these two the wrong + // way around + if( gbPL110_IsVersatile ) + { + gpPL110_IOMem->LCDIMSC = controlWord; // Actually LCDControl + gpPL110_IOMem->LCDControl = 0; // Actually LCDIMSC + } + else + { + gpPL110_IOMem->LCDIMSC = 0; + gpPL110_IOMem->LCDControl = controlWord; + } + + giPL110_Width = W; + giPL110_Height = H; + + // Update the DrvUtil buffer info + gPL110_DrvUtil_BufInfo.Framebuffer = gpPL110_Framebuffer; + gPL110_DrvUtil_BufInfo.Pitch = W * 4; + gPL110_DrvUtil_BufInfo.Width = W; + gPL110_DrvUtil_BufInfo.Height = H; + gPL110_DrvUtil_BufInfo.Depth = 32; + + return 0; +} diff --git a/KernelLand/Modules/Display/Tegra2Vid/Makefile b/KernelLand/Modules/Display/Tegra2Vid/Makefile new file mode 100644 index 00000000..ecab050e --- /dev/null +++ b/KernelLand/Modules/Display/Tegra2Vid/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = main.o +NAME = Tegra2Vid + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Display/Tegra2Vid/main.c b/KernelLand/Modules/Display/Tegra2Vid/main.c new file mode 100644 index 00000000..d9405be0 --- /dev/null +++ b/KernelLand/Modules/Display/Tegra2Vid/main.c @@ -0,0 +1,326 @@ +/** + * main.c + * - Driver core + */ +#define DEBUG 0 +#define VERSION ((0<<8)|10) +#include +#include +#include +#include +#include +#include +#include +#include +#include // ARM Arch +#include "tegra2.h" + +#define ABS(a) ((a)>0?(a):-(a)) + +// === PROTOTYPES === +// Driver + int Tegra2Vid_Install(char **Arguments); +void Tegra2Vid_Uninstall(); +// Internal +// Filesystem +Uint64 Tegra2Vid_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); +Uint64 Tegra2Vid_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); + int Tegra2Vid_IOCtl(tVFS_Node *node, int id, void *data); +// -- Internals + int Tegra2Vid_int_SetMode(int Mode); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, Tegra2Vid, Tegra2Vid_Install, NULL, NULL); +tDevFS_Driver gTegra2Vid_DriverStruct = { + NULL, "Tegra2Vid", + { + .Read = Tegra2Vid_Read, + .Write = Tegra2Vid_Write, + .IOCtl = Tegra2Vid_IOCtl + } +}; +// -- Options +tPAddr gTegra2Vid_PhysBase = TEGRA2VID_BASE; + int gbTegra2Vid_IsVersatile = 1; +// -- KeyVal parse rules +const tKeyVal_ParseRules gTegra2Vid_KeyValueParser = { + NULL, + { + {"Base", "P", &gTegra2Vid_PhysBase}, + {NULL, NULL, NULL} + } +}; +// -- Driver state + int giTegra2Vid_CurrentMode = 0; + int giTegra2Vid_BufferMode; +size_t giTegra2Vid_FramebufferSize; +Uint32 *gpTegra2Vid_IOMem; +tPAddr gTegra2Vid_FramebufferPhys; +void *gpTegra2Vid_Framebuffer; +// -- Misc +tDrvUtil_Video_BufInfo gTegra2Vid_DrvUtil_BufInfo; +tVideo_IOCtl_Pos gTegra2Vid_CursorPos; + +// === CODE === +/** + */ +int Tegra2Vid_Install(char **Arguments) +{ +// KeyVal_Parse(&gTegra2Vid_KeyValueParser, Arguments); + + gpTegra2Vid_IOMem = (void*)MM_MapHWPages(gTegra2Vid_PhysBase, 256/4); + { + Log_Debug("Tegra2Vid", "Display CMD Registers"); + for( int i = 0x000; i <= 0x01A; i ++ ) + Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); + for( int i = 0x028; i <= 0x043; i ++ ) + Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); + Log_Debug("Tegra2Vid", "Display COM Registers"); + for( int i = 0x300; i <= 0x329; i ++ ) + Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); + Log_Debug("Tegra2Vid", "Display DISP Registers"); + for( int i = 0x400; i <= 0x446; i ++ ) + Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); + for( int i = 0x480; i <= 0x484; i ++ ) + Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); + for( int i = 0x4C0; i <= 0x4C1; i ++ ) + Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); + + Log_Debug("Tegra2Vid", "WINC_A Registers"); + for( int i = 0x700; i <= 0x714; i ++ ) + Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); + Log_Debug("Tegra2Vid", "WINBUF_A"); + for( int i = 0x800; i <= 0x80A; i ++ ) + Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); + } +// return 1; + + giTegra2Vid_FramebufferSize = + (gpTegra2Vid_IOMem[DC_WIN_A_SIZE_0]&0xFFFF) + *(gpTegra2Vid_IOMem[DC_WIN_A_SIZE_0]>>16)*4; + + Log_Debug("Tegra2Vid", "giTegra2Vid_FramebufferSize = 0x%x", giTegra2Vid_FramebufferSize); + gpTegra2Vid_Framebuffer = MM_MapHWPages( + gpTegra2Vid_IOMem[DC_WINBUF_A_START_ADDR_0], + (giTegra2Vid_FramebufferSize+PAGE_SIZE-1)/PAGE_SIZE + ); + memset(gpTegra2Vid_Framebuffer, 0x1F, 0x1000); + + +// Tegra2Vid_int_SetMode(4); + + DevFS_AddDevice( &gTegra2Vid_DriverStruct ); + + return 0; +} + +/** + * \brief Clean up resources for driver unloading + */ +void Tegra2Vid_Uninstall() +{ +} + +/** + * \brief Read from the framebuffer + */ +Uint64 Tegra2Vid_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) +{ + return 0; +} + +/** + * \brief Write to the framebuffer + */ +Uint64 Tegra2Vid_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + gTegra2Vid_DrvUtil_BufInfo.BufferFormat = giTegra2Vid_BufferMode; + return DrvUtil_Video_WriteLFB(&gTegra2Vid_DrvUtil_BufInfo, Offset, Length, Buffer); +} + +const char *csaTegra2Vid_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL}; + +/** + * \brief Handle messages to the device + */ +int Tegra2Vid_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + int ret = -2; + ENTER("pNode iID pData", Node, ID, Data); + + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaTegra2Vid_IOCtls); + + case VIDEO_IOCTL_SETBUFFORMAT: + DrvUtil_Video_RemoveCursor( &gTegra2Vid_DrvUtil_BufInfo ); + ret = giTegra2Vid_BufferMode; + if(Data) giTegra2Vid_BufferMode = *(int*)Data; + if(gTegra2Vid_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) + DrvUtil_Video_SetCursor( &gTegra2Vid_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor ); + break; + + case VIDEO_IOCTL_GETSETMODE: + if(Data) + { + int newMode; + + if( !CheckMem(Data, sizeof(int)) ) + LEAVE_RET('i', -1); + + newMode = *(int*)Data; + + if(newMode < 0 || newMode >= ciTegra2Vid_ModeCount) + LEAVE_RET('i', -1); + + if(newMode != giTegra2Vid_CurrentMode) + { + giTegra2Vid_CurrentMode = newMode; + Tegra2Vid_int_SetMode( newMode ); + } + } + ret = giTegra2Vid_CurrentMode; + break; + + case VIDEO_IOCTL_FINDMODE: + { + tVideo_IOCtl_Mode *mode = Data; + int closest, closestArea, reqArea = 0; + if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode))) + LEAVE_RET('i', -1); + if( mode->bpp != 32 ) + LEAVE_RET('i', 0); + if( mode->flags != 0 ) + LEAVE_RET('i', 0); + + ret = 0; + + for( int i = 0; i < ciTegra2Vid_ModeCount; i ++ ) + { + int area; + if(mode->width == caTegra2Vid_Modes[i].W && mode->height == caTegra2Vid_Modes[i].H) { + mode->id = i; + ret = 1; + break; + } + + area = caTegra2Vid_Modes[i].W * caTegra2Vid_Modes[i].H; + if(!reqArea) { + reqArea = mode->width * mode->height; + closest = i; + closestArea = area; + } + else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) { + closest = i; + closestArea = area; + } + } + + if( ret == 0 ) + { + mode->id = closest; + ret = 1; + } + mode->width = caTegra2Vid_Modes[mode->id].W; + mode->height = caTegra2Vid_Modes[mode->id].H; + break; + } + + case VIDEO_IOCTL_MODEINFO: + { + tVideo_IOCtl_Mode *mode = Data; + if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode))) + LEAVE_RET('i', -1); + if(mode->id < 0 || mode->id >= ciTegra2Vid_ModeCount) + LEAVE_RET('i', 0); + + + mode->bpp = 32; + mode->flags = 0; + mode->width = caTegra2Vid_Modes[mode->id].W; + mode->height = caTegra2Vid_Modes[mode->id].H; + + ret = 1; + break; + } + + case VIDEO_IOCTL_SETCURSOR: + if( !Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) ) + LEAVE_RET('i', -1); + + DrvUtil_Video_RemoveCursor( &gTegra2Vid_DrvUtil_BufInfo ); + + gTegra2Vid_CursorPos = *(tVideo_IOCtl_Pos*)Data; + if(gTegra2Vid_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) + DrvUtil_Video_DrawCursor( + &gTegra2Vid_DrvUtil_BufInfo, + gTegra2Vid_CursorPos.x*giVT_CharWidth, + gTegra2Vid_CursorPos.y*giVT_CharHeight + ); + else + DrvUtil_Video_DrawCursor( + &gTegra2Vid_DrvUtil_BufInfo, + gTegra2Vid_CursorPos.x, + gTegra2Vid_CursorPos.y + ); + break; + + default: + LEAVE('i', -2); + return -2; + } + + LEAVE('i', ret); + return ret; +} + +// +// +// + +int Tegra2Vid_int_SetMode(int Mode) +{ + const struct sTegra2_Disp_Mode *mode = &caTegra2Vid_Modes[Mode]; + int w = mode->W, h = mode->H; // Horizontal/Vertical Active + *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_FRONT_PORCH_0) = (mode->VFP << 16) | mode->HFP; + *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_SYNC_WIDTH_0) = (mode->HS << 16) | mode->HS; + *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_BACK_PORCH_0) = (mode->VBP << 16) | mode->HBP; + *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_DISP_ACTIVE_0) = (mode->H << 16) | mode->W; + + *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_POSITION_0) = 0; + *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_SIZE_0) = (mode->H << 16) | mode->W; + *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_DISP_COLOR_CONTROL_0) = 0x8; // BASE888 + *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_COLOR_DEPTH_0) = 12; // Could be 13 (BGR/RGB) + *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_PRESCALED_SIZE_0) = (mode->H << 16) | mode->W; + + Log_Debug("Tegra2Vid", "Mode %i (%ix%i) selected", Mode, w, h); + + if( !gpTegra2Vid_Framebuffer || w*h*4 != giTegra2Vid_FramebufferSize ) + { + if( gpTegra2Vid_Framebuffer ) + { + // TODO: Free framebuffer for reallocation + } + + giTegra2Vid_FramebufferSize = w*h*4; + + gpTegra2Vid_Framebuffer = (void*)MM_AllocDMA( + (giTegra2Vid_FramebufferSize + PAGE_SIZE-1) / PAGE_SIZE, + 32, + &gTegra2Vid_FramebufferPhys + ); + // TODO: Catch allocation failures + Log_Debug("Tegra2Vid", "0x%x byte framebuffer at %p (%P phys)", + giTegra2Vid_FramebufferSize, + gpTegra2Vid_Framebuffer, + gTegra2Vid_FramebufferPhys + ); + + // Tell hardware + *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_START_ADDR_0) = gTegra2Vid_FramebufferPhys; + *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_ADDR_V_OFFSET_0) = 0; // Y offset + *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_ADDR_H_OFFSET_0) = 0; // X offset + } + + return 0; +} diff --git a/KernelLand/Modules/Display/Tegra2Vid/tegra2.h b/KernelLand/Modules/Display/Tegra2Vid/tegra2.h new file mode 100644 index 00000000..a3f126b6 --- /dev/null +++ b/KernelLand/Modules/Display/Tegra2Vid/tegra2.h @@ -0,0 +1,75 @@ +/* + * Acess2 NVidia Tegra2 Display Driver + * - By John Hodge (thePowersGang) + * + * tegra2.h + * - Driver definitions + */ +#ifndef _TEGRA2_DISP_H_ +#define _TEGRA2_DISP_H_ + +#define TEGRA2VID_BASE 0x54200000 // 0x40000 Large (256 KB) + +const struct sTegra2_Disp_Mode +{ + Uint16 W, H; + Uint16 HFP, VFP; + Uint16 HS, VS; + Uint16 HBP, VBP; +} caTegra2Vid_Modes[] = { + // TODO: VESA timings + {720, 487, 16,33, 63, 33, 59, 133}, // NTSC 2 + {720, 576, 12,33, 63, 33, 69, 193}, // PAL 2 (VFP shown as 2/33, used 33) + {720, 483, 16, 6, 63, 6, 59, 30}, // 480p + {1280, 720, 70, 5, 804, 6, 220, 20}, // 720p + {1920,1080, 44, 4, 884, 5, 148, 36}, // 1080p + // TODO: Can all but HA/VA be constant and those select the resolution? +}; +const int ciTegra2Vid_ModeCount = sizeof(caTegra2Vid_Modes)/sizeof(caTegra2Vid_Modes[0]); + +enum eTegra2_Disp_Regs +{ + DC_DISP_DISP_SIGNAL_OPTIONS0_0 = 0x400, + DC_DISP_DISP_SIGNAL_OPTIONS1_0, // 401 + DC_DISP_DISP_WIN_OPTIONS_0, // 402 + DC_DISP_MEM_HIGH_PRIORITY_0, // 403 + DC_DISP_MEM_HIGH_PRIORITY_TIMER_0, // 404 + DC_DISP_DISP_TIMING_OPTIONS_0, // 405 + DC_DISP_REF_TO_SYNC_0, // 406 (TrimSlice 0x0001 000B) + DC_DISP_SYNC_WIDTH_0, // 407 (TrimSlice 0x0004 003A) + DC_DISP_BACK_PORCH_0, // 408 (TrimSlice 0x0004 003A) + DC_DISP_DISP_ACTIVE_0, // 409 (TrimSlice 0x0300 0400) + DC_DISP_FRONT_PORCH_0, // 40A (TrimSlice 0x0004 003A) + + DC_DISP_H_PULSE0_CONTROL_0, // 40B + + DC_DISP_DISP_COLOR_CONTROL_0 = 0x430, + + DC_WINC_A_COLOR_PALETTE_0 = 0x500, + DC_WINC_A_PALETTE_COLOR_EXT_0 = 0x600, + DC_WIN_A_WIN_OPTIONS_0 = 0x700, + DC_WIN_A_BYTE_SWAP_0, // 701 + DC_WIN_A_BUFFER_CONTROL_0, // 702 + DC_WIN_A_COLOR_DEPTH_0, // 703 + DC_WIN_A_POSITION_0, // 704 + DC_WIN_A_SIZE_0, // 705 (TrimSlice 0x0300 0400) + DC_WIN_A_PRESCALED_SIZE_0, + DC_WIN_A_H_INITIAL_DDA_0, + DC_WIN_A_V_INITIAL_DDA_0, + DC_WIN_A_DDA_INCREMENT_0, + DC_WIN_A_LINE_STRIDE_0, + DC_WIN_A_BUF_STRIDE_0, + DC_WIN_A_BUFFER_ADDR_MODE_0, + DC_WIN_A_DV_CONTROL_0, + DC_WIN_A_BLEND_NOKEY_0, + + DC_WINBUF_A_START_ADDR_0 = 0x800, + DC_WINBUF_A_START_ADDR_NS_0, + DC_WINBUF_A_ADDR_H_OFFSET_0, + DC_WINBUF_A_ADDR_H_OFFSET_NS_0, + DC_WINBUF_A_ADDR_V_OFFSET_0, + DC_WINBUF_A_ADDR_V_OFFSET_NS_0, +}; + +#endif + diff --git a/KernelLand/Modules/Display/VESA/Makefile b/KernelLand/Modules/Display/VESA/Makefile new file mode 100644 index 00000000..d8da2334 --- /dev/null +++ b/KernelLand/Modules/Display/VESA/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = main.o +NAME = VESA + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Display/VESA/common.h b/KernelLand/Modules/Display/VESA/common.h new file mode 100644 index 00000000..eeaddd56 --- /dev/null +++ b/KernelLand/Modules/Display/VESA/common.h @@ -0,0 +1,59 @@ +/** + */ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +// === TYPES === +typedef struct sFarPtr +{ + Uint16 ofs; + Uint16 seg; +} tFarPtr; + +typedef struct sVesa_Mode +{ + Uint16 code; + Uint16 width, height; + Uint16 pitch, bpp; + Uint16 flags; + Uint32 fbSize; + Uint32 framebuffer; +} tVesa_Mode; + +typedef struct sVesa_CallModeInfo +{ + Uint16 attributes; + Uint8 winA,winB; + Uint16 granularity; + Uint16 winsize; + Uint16 segmentA, segmentB; + tFarPtr realFctPtr; + Uint16 pitch; // Bytes per scanline + + Uint16 Xres, Yres; + Uint8 Wchar, Ychar, planes, bpp, banks; + Uint8 memory_model, bank_size, image_pages; + Uint8 reserved0; + + Uint8 red_mask, red_position; + Uint8 green_mask, green_position; + Uint8 blue_mask, blue_position; + Uint8 rsv_mask, rsv_position; + Uint8 directcolor_attributes; + + Uint32 physbase; // Your LFB address ;) + Uint32 reserved1; + Sint16 reserved2; +} tVesa_CallModeInfo; + +typedef struct sVesa_CallInfo +{ + char signature[4]; // == "VESA" + Uint16 Version; // == 0x0300 for Vesa 3.0 + tFarPtr OEMString; // isa vbeFarPtr + Uint8 Capabilities[4]; + tFarPtr VideoModes; // isa vbeParPtr + Uint16 TotalMemory; // as # of 64KB blocks +} tVesa_CallInfo; + +#endif diff --git a/KernelLand/Modules/Display/VESA/main.c b/KernelLand/Modules/Display/VESA/main.c new file mode 100644 index 00000000..a85d3137 --- /dev/null +++ b/KernelLand/Modules/Display/VESA/main.c @@ -0,0 +1,418 @@ +/* + * AcessOS 1 + * Video BIOS Extensions (Vesa) Driver + */ +#define DEBUG 0 +#define VERSION 0x100 + +#include +#include +#include +#include +#include +#include +#include "common.h" + +// === CONSTANTS === +#define FLAG_LFB 0x1 +#define VESA_DEFAULT_FRAMEBUFFER (KERNEL_BASE|0xA0000) +#define BLINKING_CURSOR 1 +#if BLINKING_CURSOR +# define VESA_CURSOR_PERIOD 1000 +#endif + +// === PROTOTYPES === + int Vesa_Install(char **Arguments); +Uint64 Vesa_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 Vesa_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); + int Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data); + int Vesa_Int_SetMode(int Mode); + int Vesa_Int_FindMode(tVideo_IOCtl_Mode *data); + int Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data); +void Vesa_int_HideCursor(void); +void Vesa_int_ShowCursor(void); +void Vesa_FlipCursor(void *Arg); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, Vesa, Vesa_Install, NULL, "PCI", "VM8086", NULL); +tVFS_NodeType gVesa_NodeType = { + .Read = Vesa_Read, + .Write = Vesa_Write, + .IOCtl = Vesa_IOCtl + }; +tDevFS_Driver gVesa_DriverStruct = { + NULL, "Vesa", + {.Type = &gVesa_NodeType} + }; +tMutex glVesa_Lock; +tVM8086 *gpVesa_BiosState; + int giVesaDriverId = -1; +// --- Video Modes --- + int giVesaCurrentMode = 0; +tVesa_Mode *gVesa_Modes; +tVesa_Mode *gpVesaCurMode; + int giVesaModeCount = 0; + int gbVesaModesChecked; +// --- Framebuffer --- +char *gpVesa_Framebuffer = (void*)VESA_DEFAULT_FRAMEBUFFER; + int giVesaPageCount = 0; //!< Framebuffer size in pages +// --- Cursor Control --- + int giVesaCursorX = -1; + int giVesaCursorY = -1; + int giVesaCursorTimer = -1; // Invalid timer + int gbVesa_CursorVisible = 0; +// --- 2D Video Stream Handlers --- +tDrvUtil_Video_BufInfo gVesa_BufInfo; + +// === CODE === +int Vesa_Install(char **Arguments) +{ + tVesa_CallInfo *info; + tFarPtr infoPtr; + Uint16 *modes; + int i; + + // Allocate Info Block + gpVesa_BiosState = VM8086_Init(); + info = VM8086_Allocate(gpVesa_BiosState, 512, &infoPtr.seg, &infoPtr.ofs); + // Set Requested Version + memcpy(info->signature, "VBE2", 4); + // Set Registers + gpVesa_BiosState->AX = 0x4F00; + gpVesa_BiosState->ES = infoPtr.seg; gpVesa_BiosState->DI = infoPtr.ofs; + // Call Interrupt + VM8086_Int(gpVesa_BiosState, 0x10); + if(gpVesa_BiosState->AX != 0x004F) { + Log_Warning("VESA", "Vesa_Install - VESA/VBE Unsupported (AX = 0x%x)", gpVesa_BiosState->AX); + return MODULE_ERR_NOTNEEDED; + } + + //Log_Debug("VESA", "info->VideoModes = %04x:%04x", info->VideoModes.seg, info->VideoModes.ofs); + modes = (Uint16 *) VM8086_GetPointer(gpVesa_BiosState, info->VideoModes.seg, info->VideoModes.ofs); + + // Read Modes + for( giVesaModeCount = 0; modes[giVesaModeCount] != 0xFFFF; giVesaModeCount++ ); + gVesa_Modes = (tVesa_Mode *)malloc( giVesaModeCount * sizeof(tVesa_Mode) ); + + Log_Debug("VESA", "%i Modes", giVesaModeCount); + + // Insert Text Mode + gVesa_Modes[0].width = 80; + gVesa_Modes[0].height = 25; + gVesa_Modes[0].bpp = 12; + gVesa_Modes[0].code = 0x3; + gVesa_Modes[0].flags = 1; + gVesa_Modes[0].fbSize = 80*25*2; + gVesa_Modes[0].framebuffer = 0xB8000; + + for( i = 1; i < giVesaModeCount; i++ ) + { + gVesa_Modes[i].code = modes[i]; + } + +// VM8086_Deallocate( info ); + + // Install Device + giVesaDriverId = DevFS_AddDevice( &gVesa_DriverStruct ); + if(giVesaDriverId == -1) return MODULE_ERR_MISC; + + return MODULE_ERR_OK; +} + +void Vesa_int_FillModeList(void) +{ + if( !gbVesaModesChecked ) + { + int i; + tVesa_CallModeInfo *modeinfo; + tFarPtr modeinfoPtr; + + modeinfo = VM8086_Allocate(gpVesa_BiosState, 512, &modeinfoPtr.seg, &modeinfoPtr.ofs); + for( i = 1; i < giVesaModeCount; i ++ ) + { + // Get Mode info + gpVesa_BiosState->AX = 0x4F01; + gpVesa_BiosState->CX = gVesa_Modes[i].code; + gpVesa_BiosState->ES = modeinfoPtr.seg; + gpVesa_BiosState->DI = modeinfoPtr.ofs; + VM8086_Int(gpVesa_BiosState, 0x10); + + // Parse Info + gVesa_Modes[i].flags = 0; + if ( (modeinfo->attributes & 0x90) == 0x90 ) + { + gVesa_Modes[i].flags |= FLAG_LFB; + gVesa_Modes[i].framebuffer = modeinfo->physbase; + gVesa_Modes[i].fbSize = modeinfo->Yres*modeinfo->pitch; + } else { + gVesa_Modes[i].framebuffer = 0; + gVesa_Modes[i].fbSize = 0; + } + + gVesa_Modes[i].pitch = modeinfo->pitch; + gVesa_Modes[i].width = modeinfo->Xres; + gVesa_Modes[i].height = modeinfo->Yres; + gVesa_Modes[i].bpp = modeinfo->bpp; + + #if DEBUG + Log_Log("VESA", "0x%x - %ix%ix%i", + gVesa_Modes[i].code, gVesa_Modes[i].width, gVesa_Modes[i].height, gVesa_Modes[i].bpp); + #endif + } + +// VM8086_Deallocate( modeinfo ); + + gbVesaModesChecked = 1; + } +} + +/* Read from the framebuffer + */ +Uint64 Vesa_Read(tVFS_Node *Node, Uint64 off, Uint64 len, void *buffer) +{ + #if DEBUG >= 2 + Log("Vesa_Read: () - NULL\n"); + #endif + return 0; +} + +/** + * \brief Write to the framebuffer + */ +Uint64 Vesa_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + if( gVesa_Modes[giVesaCurrentMode].framebuffer == 0 ) { + Log_Warning("VESA", "Vesa_Write - Non-LFB Modes not yet supported."); + return 0; + } + + return DrvUtil_Video_WriteLFB(&gVesa_BufInfo, Offset, Length, Buffer); +} + +const char *csaVESA_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL}; +/** + * \brief Handle messages to the device + */ +int Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + int ret; + //Log_Debug("VESA", "Vesa_Ioctl: (Node=%p, ID=%i, Data=%p)", Node, ID, Data); + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_VIDEO, "VESA", VERSION, csaVESA_IOCtls); + + case VIDEO_IOCTL_GETSETMODE: + if( !Data ) return giVesaCurrentMode; + return Vesa_Int_SetMode( *(int*)Data ); + + case VIDEO_IOCTL_FINDMODE: + return Vesa_Int_FindMode((tVideo_IOCtl_Mode*)Data); + case VIDEO_IOCTL_MODEINFO: + return Vesa_Int_ModeInfo((tVideo_IOCtl_Mode*)Data); + + case VIDEO_IOCTL_SETBUFFORMAT: + Vesa_int_HideCursor(); + ret = gVesa_BufInfo.BufferFormat; + if(Data) gVesa_BufInfo.BufferFormat = *(int*)Data; + if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) + DrvUtil_Video_SetCursor( &gVesa_BufInfo, &gDrvUtil_TextModeCursor ); + Vesa_int_ShowCursor(); + return ret; + + case VIDEO_IOCTL_SETCURSOR: // Set cursor position + Vesa_int_HideCursor(); + giVesaCursorX = ((tVideo_IOCtl_Pos*)Data)->x; + giVesaCursorY = ((tVideo_IOCtl_Pos*)Data)->y; + Vesa_int_ShowCursor(); + return 0; + + case VIDEO_IOCTL_SETCURSORBITMAP: + DrvUtil_Video_SetCursor( &gVesa_BufInfo, Data ); + return 0; + } + return 0; +} + +/** + * \brief Updates the video mode + */ +int Vesa_Int_SetMode(int mode) +{ + // Sanity Check values + if(mode < 0 || mode > giVesaModeCount) return -1; + + // Check for fast return + if(mode == giVesaCurrentMode) return 1; + + Vesa_int_FillModeList(); + + Time_RemoveTimer(giVesaCursorTimer); + giVesaCursorTimer = -1; + + Mutex_Acquire( &glVesa_Lock ); + + gpVesa_BiosState->AX = 0x4F02; + gpVesa_BiosState->BX = gVesa_Modes[mode].code; + if(gVesa_Modes[mode].flags & FLAG_LFB) { + gpVesa_BiosState->BX |= 0x4000; // Bit 14 - Use LFB + } + + // Set Mode + VM8086_Int(gpVesa_BiosState, 0x10); + + // Map Framebuffer + if( (tVAddr)gpVesa_Framebuffer != VESA_DEFAULT_FRAMEBUFFER ) + MM_UnmapHWPages((tVAddr)gpVesa_Framebuffer, giVesaPageCount); + giVesaPageCount = (gVesa_Modes[mode].fbSize + 0xFFF) >> 12; + gpVesa_Framebuffer = (void*)MM_MapHWPages(gVesa_Modes[mode].framebuffer, giVesaPageCount); + + Log_Log("VESA", "Setting mode to %i (%ix%i %ibpp) %p[0x%x] maps %P", + mode, + gVesa_Modes[mode].width, gVesa_Modes[mode].height, + gVesa_Modes[mode].bpp, + gpVesa_Framebuffer, giVesaPageCount << 12, gVesa_Modes[mode].framebuffer + ); + + // Record Mode Set + giVesaCurrentMode = mode; + gpVesaCurMode = &gVesa_Modes[giVesaCurrentMode]; + + Mutex_Release( &glVesa_Lock ); + + gVesa_BufInfo.Framebuffer = gpVesa_Framebuffer; + gVesa_BufInfo.Pitch = gVesa_Modes[mode].pitch; + gVesa_BufInfo.Width = gVesa_Modes[mode].width; + gVesa_BufInfo.Height = gVesa_Modes[mode].height; + gVesa_BufInfo.Depth = gVesa_Modes[mode].bpp; + + return 1; +} + +int Vesa_Int_FindMode(tVideo_IOCtl_Mode *data) +{ + int i; + int best = -1, bestFactor = 1000; + int factor, tmp; + + ENTER("idata->width idata->height idata->bpp", data->width, data->height, data->bpp); + + Vesa_int_FillModeList(); + + for(i=0;iwidth && gVesa_Modes[i].height == data->height) + { + //if( (data->bpp == 32 || data->bpp == 24) + // && (gVesa_Modes[i].bpp == 32 || gVesa_Modes[i].bpp == 24) ) + if( data->bpp == gVesa_Modes[i].bpp ) + { + LOG("Perfect!"); + best = i; + break; + } + } + + tmp = gVesa_Modes[i].width * gVesa_Modes[i].height; + tmp -= data->width * data->height; + tmp = tmp < 0 ? -tmp : tmp; + factor = tmp * 1000 / (data->width * data->height); + + if( data->bpp == 8 && gVesa_Modes[i].bpp != 8 ) continue; + if( data->bpp == 16 && gVesa_Modes[i].bpp != 16 ) continue; + + if( (data->bpp == 32 || data->bpp == 24) + && (gVesa_Modes[i].bpp == 32 || gVesa_Modes[i].bpp == 24) ) + { + if( data->bpp == gVesa_Modes[i].bpp ) + factor /= 2; + } + else { + if( data->bpp != gVesa_Modes[i].bpp ) + continue ; + } + + LOG("factor = %i", factor); + + if(factor < bestFactor) + { + bestFactor = factor; + best = i; + } + } + data->id = best; + data->width = gVesa_Modes[best].width; + data->height = gVesa_Modes[best].height; + data->bpp = gVesa_Modes[best].bpp; + LEAVE('i', best); + return best; +} + +int Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data) +{ + if(data->id < 0 || data->id > giVesaModeCount) return -1; + + Vesa_int_FillModeList(); + + data->width = gVesa_Modes[data->id].width; + data->height = gVesa_Modes[data->id].height; + data->bpp = gVesa_Modes[data->id].bpp; + return 1; +} + +void Vesa_int_HideCursor(void) +{ + DrvUtil_Video_RemoveCursor( &gVesa_BufInfo ); + #if BLINKING_CURSOR + if(giVesaCursorTimer != -1) { + Time_RemoveTimer(giVesaCursorTimer); + giVesaCursorTimer = -1; + } + #endif +} + +void Vesa_int_ShowCursor(void) +{ + gbVesa_CursorVisible = (giVesaCursorX >= 0); + if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) + { + DrvUtil_Video_DrawCursor( + &gVesa_BufInfo, + giVesaCursorX*giVT_CharWidth, + giVesaCursorY*giVT_CharHeight + ); + #if BLINKING_CURSOR + giVesaCursorTimer = Time_CreateTimer(VESA_CURSOR_PERIOD, Vesa_FlipCursor, NULL); + #endif + } + else + DrvUtil_Video_DrawCursor( + &gVesa_BufInfo, + giVesaCursorX, + giVesaCursorY + ); +} + +/** + * \brief Swaps the text cursor on/off + */ +void Vesa_FlipCursor(void *Arg) +{ + if( gVesa_BufInfo.BufferFormat != VIDEO_BUFFMT_TEXT ) + return ; + + if( gbVesa_CursorVisible ) + DrvUtil_Video_RemoveCursor(&gVesa_BufInfo); + else + DrvUtil_Video_DrawCursor(&gVesa_BufInfo, + giVesaCursorX*giVT_CharWidth, + giVesaCursorY*giVT_CharHeight + ); + gbVesa_CursorVisible = !gbVesa_CursorVisible; + + #if BLINKING_CURSOR + giVesaCursorTimer = Time_CreateTimer(VESA_CURSOR_PERIOD, Vesa_FlipCursor, Arg); + #endif +} + diff --git a/KernelLand/Modules/Filesystems/Ext2/Makefile b/KernelLand/Modules/Filesystems/Ext2/Makefile new file mode 100644 index 00000000..471ba49e --- /dev/null +++ b/KernelLand/Modules/Filesystems/Ext2/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = ext2.o read.o dir.o write.o +NAME = Ext2 + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Filesystems/Ext2/dir.c b/KernelLand/Modules/Filesystems/Ext2/dir.c new file mode 100644 index 00000000..5eb476f4 --- /dev/null +++ b/KernelLand/Modules/Filesystems/Ext2/dir.c @@ -0,0 +1,397 @@ +/* + * Acess OS + * Ext2 Driver Version 1 + */ +/** + * \file dir.c + * \brief Second Extended Filesystem Driver + * \todo Implement file full write support + */ +#define DEBUG 1 +#define VERBOSE 0 +#include "ext2_common.h" + +// === MACROS === +#define BLOCK_DIR_OFS(_data, _block) ((Uint16*)(_data)[(_block)]) + +// === PROTOTYPES === +char *Ext2_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *FileName); + int Ext2_MkNod(tVFS_Node *Node, const char *Name, Uint Flags); + int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName); + int Ext2_Link(tVFS_Node *Parent, tVFS_Node *Node, const char *Name); +// --- Helpers --- +tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId); + +// === GLOBALS === +tVFS_NodeType gExt2_DirType = { + .TypeName = "ext2-dir", + .ReadDir = Ext2_ReadDir, + .FindDir = Ext2_FindDir, + .MkNod = Ext2_MkNod, + .Relink = Ext2_Relink, + .Link = Ext2_Link, + .Close = Ext2_CloseFile + }; +tVFS_NodeType gExt2_FileType = { + .TypeName = "ext2-file", + .Read = Ext2_Read, + .Write = Ext2_Write, + .Close = Ext2_CloseFile + }; + +// === CODE === +/** + * \brief Reads a directory entry + * \param Node Directory node + * \param Pos Position of desired element + */ +char *Ext2_ReadDir(tVFS_Node *Node, int Pos) +{ + tExt2_Inode inode; + tExt2_DirEnt dirent; + Uint64 Base; // Block's Base Address + int block = 0; + Uint ofs = 0; + int entNum = 0; + tExt2_Disk *disk = Node->ImplPtr; + Uint size; + + ENTER("pNode iPos", Node, Pos); + + // Read directory's inode + Ext2_int_ReadInode(disk, Node->Inode, &inode); + size = inode.i_size; + + LOG("inode.i_block[0] = 0x%x", inode.i_block[0]); + + // Find Entry + // Get First Block + // - Do this ourselves as it is a simple operation + Base = inode.i_block[0] * disk->BlockSize; + // Scan directory + while(Pos -- && size > 0) + { + VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent); + ofs += dirent.rec_len; + size -= dirent.rec_len; + entNum ++; + + if(ofs >= disk->BlockSize) { + block ++; + if( ofs > disk->BlockSize ) { + Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring", + entNum-1, Node->Inode); + } + ofs = 0; + Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block ); + } + } + + // Check for the end of the list + if(size <= 0) { + LEAVE('n'); + return NULL; + } + + // Read Entry + VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent ); + //LOG("dirent.inode = %i", dirent.inode); + //LOG("dirent.rec_len = %i", dirent.rec_len); + //LOG("dirent.name_len = %i", dirent.name_len); + dirent.name[ dirent.name_len ] = '\0'; // Cap off string + + + // Ignore . and .. (these are done in the VFS) + if( (dirent.name[0] == '.' && dirent.name[1] == '\0') + || (dirent.name[0] == '.' && dirent.name[1] == '.' && dirent.name[2]=='\0')) { + LEAVE('p', VFS_SKIP); + return VFS_SKIP; // Skip + } + + LEAVE('s', dirent.name); + // Create new node + return strdup(dirent.name); +} + +/** + * \brief Gets information about a file + * \param Node Parent Node + * \param Filename Name of wanted file + * \return VFS Node of file + */ +tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename) +{ + tExt2_Disk *disk = Node->ImplPtr; + tExt2_Inode inode; + tExt2_DirEnt dirent; + Uint64 Base; // Block's Base Address + int block = 0; + Uint ofs = 0; + int entNum = 0; + Uint size; + int filenameLen = strlen(Filename); + + // Read directory's inode + Ext2_int_ReadInode(disk, Node->Inode, &inode); + size = inode.i_size; + + // Get First Block + // - Do this ourselves as it is a simple operation + Base = inode.i_block[0] * disk->BlockSize; + // Find File + while(size > 0) + { + VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent); + dirent.name[ dirent.name_len ] = '\0'; // Cap off string + // If it matches, create a node and return it + if(dirent.name_len == filenameLen && strcmp(dirent.name, Filename) == 0) + return Ext2_int_CreateNode( disk, dirent.inode ); + // Increment pointers + ofs += dirent.rec_len; + size -= dirent.rec_len; + entNum ++; + + // Check for end of block + if(ofs >= disk->BlockSize) { + block ++; + if( ofs > disk->BlockSize ) { + Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring", + entNum-1, Node->Inode); + } + ofs = 0; + Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block ); + } + } + + return NULL; +} + +/** + * \fn int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags) + * \brief Create a new node + */ +int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags) +{ + #if 0 + tVFS_Node *child; + Uint64 inodeNum; + tExt2_Inode inode; + inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode); + + memset(&inode, 0, sizeof(tExt2_Inode)); + + // File type + inode.i_mode = 0664; + if( Flags & VFS_FFLAG_READONLY ) + inode.i_mode &= ~0222; + if( Flags & VFS_FFLAG_SYMLINK ) + inode.i_mode |= EXT2_S_IFLNK; + else if( Flags & VFS_FFLAG_DIRECTORY ) + inode.i_mode |= EXT2_S_IFDIR | 0111; + + inode.i_uid = Threads_GetUID(); + inode.i_gid = Threads_GetGID(); + inode.i_ctime = + inode.i_mtime = + inode.i_atime = now() / 1000; + + child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum); + return Ext2_Link(Parent, child, Name); + #else + return 1; + #endif +} + +/** + * \brief Rename a file + * \param Node This (directory) node + * \param OldName Old name of file + * \param NewName New name for file + * \return Boolean Failure - See ::tVFS_Node.Relink for info + */ +int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName) +{ + return 1; +} + +/** + * \brief Links an existing node to a new name + * \param Parent Parent (directory) node + * \param Node Node to link + * \param Name New name for the node + * \return Boolean Failure - See ::tVFS_Node.Link for info + */ +int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name) +{ + #if 0 + tExt2_Disk *disk = Node->ImplPtr; + tExt2_Inode inode; + tExt2_DirEnt dirent; + tExt2_DirEnt newEntry; + Uint64 Base; // Block's Base Address + int block = 0, ofs = 0; + Uint size; + void *blockData; + int bestMatch = -1, bestSize, bestBlock, bestOfs; + int nEntries; + + blockData = malloc(disk->BlockSize); + + // Read child inode (get's the file type) + Ext2_int_ReadInode(disk, Child->Inode, &inode); + + // Create a stub entry + newEntry.inode = Child->Inode; + newEntry.name_len = strlen(Name); + newEntry.rec_len = (newEntry.name_len+3+8)&~3; + newEntry.type = inode.i_mode >> 12; + memcpy(newEntry.name, Name, newEntry.name_len); + + // Read directory's inode + Ext2_int_ReadInode(disk, Node->Inode, &inode); + size = inode.i_size; + + // Get a lock on the inode + Ext2_int_LockInode(disk, Node->Inode); + + // Get First Block + // - Do this ourselves as it is a simple operation + base = inode.i_block[0] * disk->BlockSize; + VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData ); + block = 0; + // Find File + while(size > 0) + { + dirent = blockData + ofs; + // Sanity Check the entry + if(ofs + dirent->rec_len > disk->BlockSize) { + Log_Warning("EXT2", + "Directory entry %i of inode 0x%x extends over a block boundary", + nEntries, (Uint)Node->Inode); + } + else { + + // Free entry + if(dirent->type == 0) { + if( dirent->rec_len >= newEntry.rec_len + && (bestMatch == -1 || bestSize > dirent->rec_len) ) + { + bestMatch = nEntries; + bestSize = dirent->rec_len; + bestBlock = block; + bestOfs = ofs; + } + } + // Non free - check name to avoid duplicates + else { + if(strncmp(Name, dirent->name, dirent->name_len) == 0) { + Ext2_int_UnlockInode(disk, Node->Inode); + return 1; // ERR_??? + } + } + } + + // Increment the pointer + nEntries ++; + ofs += dirent->rec_len; + if( ofs >= disk->BlockSize ) { + // Read the next block if needed + BLOCK_DIR_OFS(Node->Data, block) = nEntries; + block ++; + ofs = 0; + base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); + VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData ); + } + } + + // Check if a free slot was found + if( bestMatch >= 0 ) { + // Read-Modify-Write + bestBlock = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock); + if( block > 0 ) + bestMatch = BLOCK_DIR_OFS(Node->Data, bestBlock); + VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData ); + dirent = blockData + bestOfs; + memcpy(dirent, newEntry, newEntry.rec_len); + VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData ); + } + else { + // Allocate block, Write + block = Ext2_int_AllocateBlock(Disk, block); + Log_Warning("EXT2", ""); + } + + Ext2_int_UnlockInode(disk, Node->Inode); + return 0; + #else + return 1; + #endif +} + +// ---- INTERNAL FUNCTIONS ---- +/** + * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID) + * \brief Create a new VFS Node + */ +tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID) +{ + tExt2_Inode inode; + tVFS_Node retNode; + tVFS_Node *tmpNode; + + if( !Ext2_int_ReadInode(Disk, InodeID, &inode) ) + return NULL; + + if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) ) + return tmpNode; + + + // Set identifiers + retNode.Inode = InodeID; + retNode.ImplPtr = Disk; + + // Set file length + retNode.Size = inode.i_size; + retNode.Data = NULL; + + // Set Access Permissions + retNode.UID = inode.i_uid; + retNode.GID = inode.i_gid; + retNode.NumACLs = 3; + retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid); + + // Set Function Pointers + retNode.Type = &gExt2_FileType; + + switch(inode.i_mode & EXT2_S_IFMT) + { + // Symbolic Link + case EXT2_S_IFLNK: + retNode.Flags = VFS_FFLAG_SYMLINK; + break; + // Regular File + case EXT2_S_IFREG: + retNode.Flags = 0; + retNode.Size |= (Uint64)inode.i_dir_acl << 32; + break; + // Directory + case EXT2_S_IFDIR: + retNode.Type = &gExt2_DirType; + retNode.Flags = VFS_FFLAG_DIRECTORY; + retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) ); + break; + // Unknown, Write protect it to be safe + default: + retNode.Flags = VFS_FFLAG_READONLY; + break; + } + + // Set Timestamps + retNode.ATime = inode.i_atime * 1000; + retNode.MTime = inode.i_mtime * 1000; + retNode.CTime = inode.i_ctime * 1000; + + // Save in node cache and return saved node + return Inode_CacheNode(Disk->CacheID, &retNode); +} diff --git a/KernelLand/Modules/Filesystems/Ext2/ext2.c b/KernelLand/Modules/Filesystems/Ext2/ext2.c new file mode 100644 index 00000000..7c4bf973 --- /dev/null +++ b/KernelLand/Modules/Filesystems/Ext2/ext2.c @@ -0,0 +1,337 @@ +/* + * Acess OS + * Ext2 Driver Version 1 + */ +/** + * \file fs/ext2.c + * \brief Second Extended Filesystem Driver + * \todo Implement file full write support + */ +#define DEBUG 1 +#define VERBOSE 0 +#include "ext2_common.h" +#include + +// === IMPORTS === +extern tVFS_NodeType gExt2_DirType; + +// === PROTOTYPES === + int Ext2_Install(char **Arguments); +// Interface Functions +tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options); +void Ext2_Unmount(tVFS_Node *Node); +void Ext2_CloseFile(tVFS_Node *Node); +// Internal Helpers + int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode); +Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum); +Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent); +void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk); + +// === SEMI-GLOBALS === +MODULE_DEFINE(0, 0x5B /*v0.90*/, FS_Ext2, Ext2_Install, NULL); +tExt2_Disk gExt2_disks[6]; + int giExt2_count = 0; +tVFS_Driver gExt2_FSInfo = { + "ext2", 0, Ext2_InitDevice, Ext2_Unmount, NULL + }; + +// === CODE === +/** + * \fn int Ext2_Install(char **Arguments) + * \brief Install the Ext2 Filesystem Driver + */ +int Ext2_Install(char **Arguments) +{ + VFS_AddDriver( &gExt2_FSInfo ); + return MODULE_ERR_OK; +} + +/** + \brief Initializes a device to be read by by the driver + \param Device String - Device to read from + \param Options NULL Terminated array of option strings + \return Root Node +*/ +tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options) +{ + tExt2_Disk *disk; + int fd; + int groupCount; + tExt2_SuperBlock sb; + tExt2_Inode inode; + + ENTER("sDevice pOptions", Device, Options); + + // Open Disk + fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE); //Open Device + if(fd == -1) { + Log_Warning("EXT2", "Unable to open '%s'", Device); + LEAVE('n'); + return NULL; + } + + // Read Superblock at offset 1024 + VFS_ReadAt(fd, 1024, 1024, &sb); // Read Superblock + + // Sanity Check Magic value + if(sb.s_magic != 0xEF53) { + Log_Warning("EXT2", "Volume '%s' is not an EXT2 volume (0x%x != 0xEF53)", + Device, sb.s_magic); + VFS_Close(fd); + LEAVE('n'); + return NULL; + } + + // Get Group count + groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group); + LOG("groupCount = %i", groupCount); + + // Allocate Disk Information + disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount); + if(!disk) { + Log_Warning("EXT2", "Unable to allocate disk structure"); + VFS_Close(fd); + LEAVE('n'); + return NULL; + } + disk->FD = fd; + memcpy(&disk->SuperBlock, &sb, 1024); + disk->GroupCount = groupCount; + + // Get an inode cache handle + disk->CacheID = Inode_GetHandle(); + + // Get Block Size + LOG("s_log_block_size = 0x%x", sb.s_log_block_size); + disk->BlockSize = 1024 << sb.s_log_block_size; + + // Read Group Information + VFS_ReadAt( + disk->FD, + sb.s_first_data_block * disk->BlockSize + 1024, + sizeof(tExt2_Group)*groupCount, + disk->Groups + ); + + #if VERBOSE + LOG("Block Group 0"); + LOG(".bg_block_bitmap = 0x%x", disk->Groups[0].bg_block_bitmap); + LOG(".bg_inode_bitmap = 0x%x", disk->Groups[0].bg_inode_bitmap); + LOG(".bg_inode_table = 0x%x", disk->Groups[0].bg_inode_table); + LOG("Block Group 1"); + LOG(".bg_block_bitmap = 0x%x", disk->Groups[1].bg_block_bitmap); + LOG(".bg_inode_bitmap = 0x%x", disk->Groups[1].bg_inode_bitmap); + LOG(".bg_inode_table = 0x%x", disk->Groups[1].bg_inode_table); + #endif + + // Get root Inode + Ext2_int_ReadInode(disk, 2, &inode); + + // Create Root Node + memset(&disk->RootNode, 0, sizeof(tVFS_Node)); + disk->RootNode.Inode = 2; // Root inode ID + disk->RootNode.ImplPtr = disk; // Save disk pointer + disk->RootNode.Size = -1; // Fill in later (on readdir) + disk->RootNode.Flags = VFS_FFLAG_DIRECTORY; + + disk->RootNode.Type = &gExt2_DirType; + + // Complete root node + disk->RootNode.UID = inode.i_uid; + disk->RootNode.GID = inode.i_gid; + disk->RootNode.NumACLs = 1; + disk->RootNode.ACLs = &gVFS_ACL_EveryoneRW; + + #if DEBUG + LOG("inode.i_size = 0x%x", inode.i_size); + LOG("inode.i_block[0] = 0x%x", inode.i_block[0]); + #endif + + LEAVE('p', &disk->RootNode); + return &disk->RootNode; +} + +/** + * \fn void Ext2_Unmount(tVFS_Node *Node) + * \brief Close a mounted device + */ +void Ext2_Unmount(tVFS_Node *Node) +{ + tExt2_Disk *disk = Node->ImplPtr; + + VFS_Close( disk->FD ); + Inode_ClearCache( disk->CacheID ); + memset(disk, 0, sizeof(tExt2_Disk)+disk->GroupCount*sizeof(tExt2_Group)); + free(disk); +} + +/** + * \fn void Ext2_CloseFile(tVFS_Node *Node) + * \brief Close a file (Remove it from the cache) + */ +void Ext2_CloseFile(tVFS_Node *Node) +{ + tExt2_Disk *disk = Node->ImplPtr; + Inode_UncacheNode(disk->CacheID, Node->Inode); + return ; +} + +//================================== +//= INTERNAL FUNCTIONS = +//================================== +/** + * \fn int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode) + * \brief Read an inode into memory + */ +int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode) +{ + int group, subId; + + ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode); + + if(InodeId == 0) return 0; + + InodeId --; // Inodes are numbered starting at 1 + + group = InodeId / Disk->SuperBlock.s_inodes_per_group; + subId = InodeId % Disk->SuperBlock.s_inodes_per_group; + + LOG("group=%i, subId = %i", group, subId); + + // Read Inode + VFS_ReadAt(Disk->FD, + Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId, + sizeof(tExt2_Inode), + Inode); + + LEAVE('i', 1); + return 1; +} + +/** + * \brief Write a modified inode out to disk + */ +int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode) +{ + int group, subId; + ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode); + + if(InodeId == 0) { + LEAVE('i', 0); + return 0; + } + + InodeId --; // Inodes are numbered starting at 1 + + group = InodeId / Disk->SuperBlock.s_inodes_per_group; + subId = InodeId % Disk->SuperBlock.s_inodes_per_group; + + LOG("group=%i, subId = %i", group, subId); + + // Write Inode + VFS_WriteAt(Disk->FD, + Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId, + sizeof(tExt2_Inode), + Inode + ); + + LEAVE('i', 1); + return 1; +} + +/** + * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum) + * \brief Get the address of a block from an inode's list + * \param Disk Disk information structure + * \param Blocks Pointer to an inode's block list + * \param BlockNum Block index in list + */ +Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum) +{ + Uint32 *iBlocks; + int dwPerBlock = Disk->BlockSize / 4; + + // Direct Blocks + if(BlockNum < 12) + return (Uint64)Blocks[BlockNum] * Disk->BlockSize; + + // Single Indirect Blocks + iBlocks = malloc( Disk->BlockSize ); + VFS_ReadAt(Disk->FD, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks); + + BlockNum -= 12; + if(BlockNum < dwPerBlock) + { + BlockNum = iBlocks[BlockNum]; + free(iBlocks); + return (Uint64)BlockNum * Disk->BlockSize; + } + + BlockNum -= dwPerBlock; + // Double Indirect Blocks + if(BlockNum < dwPerBlock*dwPerBlock) + { + VFS_ReadAt(Disk->FD, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks); + VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks); + BlockNum = iBlocks[BlockNum%dwPerBlock]; + free(iBlocks); + return (Uint64)BlockNum * Disk->BlockSize; + } + + BlockNum -= dwPerBlock*dwPerBlock; + // Triple Indirect Blocks + VFS_ReadAt(Disk->FD, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks); + VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/(dwPerBlock*dwPerBlock)]*Disk->BlockSize, Disk->BlockSize, iBlocks); + VFS_ReadAt(Disk->FD, (Uint64)iBlocks[(BlockNum/dwPerBlock)%dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks); + BlockNum = iBlocks[BlockNum%dwPerBlock]; + free(iBlocks); + return (Uint64)BlockNum * Disk->BlockSize; +} + +/** + * \fn Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent) + * \brief Allocate an inode (from the current group preferably) + * \param Disk EXT2 Disk Information Structure + * \param Parent Inode ID of the parent (used to locate the child nearby) + */ +Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent) +{ +// Uint block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group; + Log_Warning("EXT2", "Ext2_int_AllocateInode is unimplemented"); + return 0; +} + +/** + * \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk) + * \brief Updates the superblock + */ +void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk) +{ + int bpg = Disk->SuperBlock.s_blocks_per_group; + int ngrp = Disk->SuperBlock.s_blocks_count / bpg; + int i; + + // Update Primary + VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock); + + // Secondaries + // at Block Group 1, 3^n, 5^n, 7^n + + // 1 + if(ngrp <= 1) return; + VFS_WriteAt(Disk->FD, 1*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock); + + #define INT_MAX (((long long int)1<<(sizeof(int)*8))-1) + + // Powers of 3 + for( i = 3; i < ngrp && i < INT_MAX/3; i *= 3 ) + VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock); + + // Powers of 5 + for( i = 5; i < ngrp && i < INT_MAX/5; i *= 5 ) + VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock); + + // Powers of 7 + for( i = 7; i < ngrp && i < INT_MAX/7; i *= 7 ) + VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock); +} diff --git a/KernelLand/Modules/Filesystems/Ext2/ext2_common.h b/KernelLand/Modules/Filesystems/Ext2/ext2_common.h new file mode 100644 index 00000000..6ef9d7c8 --- /dev/null +++ b/KernelLand/Modules/Filesystems/Ext2/ext2_common.h @@ -0,0 +1,47 @@ +/* + * Acess OS + * Ext2 Driver Version 1 + */ +/** + * \file ext2_common.h + * \brief Second Extended Filesystem Driver + */ +#ifndef _EXT2_COMMON_H +#define _EXT2_COMMON_H +#include +#include +#include "ext2fs.h" + +#define EXT2_UPDATE_WRITEBACK 1 + +// === STRUCTURES === +typedef struct { + int FD; + int CacheID; + tVFS_Node RootNode; + + tExt2_SuperBlock SuperBlock; + Uint BlockSize; + + int GroupCount; + tExt2_Group Groups[]; +} tExt2_Disk; + +// === FUNCTIONS === +// --- Common --- +extern void Ext2_CloseFile(tVFS_Node *Node); +extern Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum); +extern void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk); +extern int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode); +extern int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode); +// --- Dir --- +extern char *Ext2_ReadDir(tVFS_Node *Node, int Pos); +extern tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *FileName); +extern int Ext2_MkNod(tVFS_Node *Node, const char *Name, Uint Flags); +extern int Ext2_Link(tVFS_Node *Parent, tVFS_Node *Node, const char *Name); +// --- Read --- +extern Uint64 Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer); +// --- Write --- +extern Uint64 Ext2_Write(tVFS_Node *node, Uint64 offset, Uint64 length, const void *buffer); + +#endif diff --git a/KernelLand/Modules/Filesystems/Ext2/ext2fs.h b/KernelLand/Modules/Filesystems/Ext2/ext2fs.h new file mode 100644 index 00000000..a3aa301e --- /dev/null +++ b/KernelLand/Modules/Filesystems/Ext2/ext2fs.h @@ -0,0 +1,162 @@ +/** + * Acess2 + * \file ext2fs.h + * \brief EXT2 Filesystem Driver + */ +#ifndef _EXT2FS_H_ +#define _EXT2FS_H_ + +/** + \name Inode Flag Values + \{ +*/ +#define EXT2_S_IFMT 0xF000 //!< Format Mask +#define EXT2_S_IFSOCK 0xC000 //!< Socket +#define EXT2_S_IFLNK 0xA000 //!< Symbolic Link +#define EXT2_S_IFREG 0x8000 //!< Regular File +#define EXT2_S_IFBLK 0x6000 //!< Block Device +#define EXT2_S_IFDIR 0x4000 //!< Directory +#define EXT2_S_IFCHR 0x2000 //!< Character Device +#define EXT2_S_IFIFO 0x1000 //!< FIFO +#define EXT2_S_ISUID 0x0800 //!< SUID +#define EXT2_S_ISGID 0x0400 //!< SGID +#define EXT2_S_ISVTX 0x0200 //!< sticky bit +#define EXT2_S_IRWXU 0700 //!< user access rights mask +#define EXT2_S_IRUSR 0400 //!< Owner Read +#define EXT2_S_IWUSR 0200 //!< Owner Write +#define EXT2_S_IXUSR 0100 //!< Owner Execute +#define EXT2_S_IRWXG 0070 //!< Group Access rights mask +#define EXT2_S_IRGRP 0040 //!< Group Read +#define EXT2_S_IWGRP 0020 //!< Group Write +#define EXT2_S_IXGRP 0010 //!< Group Execute +#define EXT2_S_IRWXO 0007 //!< Global Access rights mask +#define EXT2_S_IROTH 0004 //!< Global Read +#define EXT2_S_IWOTH 0002 //!< Global Write +#define EXT2_S_IXOTH 0001 //!< Global Execute +//! \} + +#define EXT2_NAME_LEN 255 //!< Maximum Name Length + +// === TYPEDEFS === +typedef struct ext2_inode_s tExt2_Inode; //!< Inode Type +typedef struct ext2_super_block_s tExt2_SuperBlock; //!< Superblock Type +typedef struct ext2_group_desc_s tExt2_Group; //!< Group Descriptor Type +typedef struct ext2_dir_entry_s tExt2_DirEnt; //!< Directory Entry Type + +// === STRUCTURES === +/** + * \brief EXT2 Superblock Structure + */ +struct ext2_super_block_s { + Uint32 s_inodes_count; //!< Inodes count + Uint32 s_blocks_count; //!< Blocks count + Uint32 s_r_blocks_count; //!< Reserved blocks count + Uint32 s_free_blocks_count; //!< Free blocks count + + Uint32 s_free_inodes_count; //!< Free inodes count + Uint32 s_first_data_block; //!< First Data Block + Uint32 s_log_block_size; //!< Block size + Sint32 s_log_frag_size; //!< Fragment size + + Uint32 s_blocks_per_group; //!< Number Blocks per group + Uint32 s_frags_per_group; //!< Number Fragments per group + Uint32 s_inodes_per_group; //!< Number Inodes per group + Uint32 s_mtime; //!< Mount time + + Uint32 s_wtime; //!< Write time + Uint16 s_mnt_count; //!< Mount count + Sint16 s_max_mnt_count; //!< Maximal mount count + Uint16 s_magic; //!< Magic signature + Uint16 s_state; //!< File system state + Uint16 s_errors; //!< Behaviour when detecting errors + Uint16 s_pad; //!< Padding + + Uint32 s_lastcheck; //!< time of last check + Uint32 s_checkinterval; //!< max. time between checks + Uint32 s_creator_os; //!< Formatting OS + Uint32 s_rev_level; //!< Revision level + + Uint16 s_def_resuid; //!< Default uid for reserved blocks + Uint16 s_def_resgid; //!< Default gid for reserved blocks + Uint32 s_reserved[235]; //!< Padding to the end of the block +}; + +/** + * \struct ext2_inode_s + * \brief EXT2 Inode Definition + */ +struct ext2_inode_s { + Uint16 i_mode; //!< File mode + Uint16 i_uid; //!< Owner Uid + Uint32 i_size; //!< Size in bytes + Uint32 i_atime; //!< Access time + Uint32 i_ctime; //!< Creation time + Uint32 i_mtime; //!< Modification time + Uint32 i_dtime; //!< Deletion Time + Uint16 i_gid; //!< Group Id + Uint16 i_links_count; //!< Links count + Uint32 i_blocks; //!< Number of blocks allocated for the file + Uint32 i_flags; //!< File flags + union { + Uint32 linux_reserved1; //!< Linux: Reserved + Uint32 hurd_translator; //!< HURD: Translator + Uint32 masix_reserved1; //!< Masix: Reserved + } osd1; //!< OS dependent 1 + Uint32 i_block[15]; //!< Pointers to blocks + Uint32 i_version; //!< File version (for NFS) + Uint32 i_file_acl; //!< File ACL + Uint32 i_dir_acl; //!< Directory ACL / Extended File Size + Uint32 i_faddr; //!< Fragment address + union { + struct { + Uint8 l_i_frag; //!< Fragment number + Uint8 l_i_fsize; //!< Fragment size + Uint16 i_pad1; //!< Padding + Uint32 l_i_reserved2[2]; //!< Reserved + } linux2; + struct { + Uint8 h_i_frag; //!< Fragment number + Uint8 h_i_fsize; //!< Fragment size + Uint16 h_i_mode_high; //!< Mode High Bits + Uint16 h_i_uid_high; //!< UID High Bits + Uint16 h_i_gid_high; //!< GID High Bits + Uint32 h_i_author; //!< Creator ID + } hurd2; + struct { + Uint8 m_i_frag; //!< Fragment number + Uint8 m_i_fsize; //!< Fragment size + Uint16 m_pad1; //!< Padding + Uint32 m_i_reserved2[2]; //!< reserved + } masix2; + } osd2; //!< OS dependent 2 +}; + +/** + * \struct ext2_group_desc_s + * \brief EXT2 Group Descriptor + */ +struct ext2_group_desc_s { + Uint32 bg_block_bitmap; //!< Blocks bitmap block + Uint32 bg_inode_bitmap; //!< Inodes bitmap block + Uint32 bg_inode_table; //!< Inodes table block + Uint16 bg_free_blocks_count; //!< Free blocks count + Uint16 bg_free_inodes_count; //!< Free inodes count + Uint16 bg_used_dirs_count; //!< Directories count + Uint16 bg_pad; //!< Padding + Uint32 bg_reserved[3]; //!< Reserved +}; + +/** + * \brief EXT2 Directory Entry + * \note The name may take up less than 255 characters + */ +struct ext2_dir_entry_s { + Uint32 inode; //!< Inode number + Uint16 rec_len; //!< Directory entry length + Uint8 name_len; //!< Short Name Length + Uint8 type; //!< File Type (Duplicate of ext2_inode_s.i_mode) + char name[EXT2_NAME_LEN+1]; //!< File name +}; +#define EXT2_DIRENT_SIZE (sizeof(struct ext2_dir_entry_s)-EXT2_NAME_LEN+1) + +#endif diff --git a/KernelLand/Modules/Filesystems/Ext2/read.c b/KernelLand/Modules/Filesystems/Ext2/read.c new file mode 100644 index 00000000..81f5ee64 --- /dev/null +++ b/KernelLand/Modules/Filesystems/Ext2/read.c @@ -0,0 +1,88 @@ +/* + * Acess OS + * Ext2 Driver Version 1 + */ +/** + * \file read.c + * \brief Second Extended Filesystem Driver + * \todo Implement file full write support + */ +#define DEBUG 1 +#define VERBOSE 0 +#include "ext2_common.h" + +// === PROTOTYPES === +Uint64 Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer); + +// === CODE === +/** + * \fn Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Read from a file + */ +Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tExt2_Disk *disk = Node->ImplPtr; + tExt2_Inode inode; + Uint64 base; + Uint block; + Uint64 remLen; + + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + + // Get Inode + Ext2_int_ReadInode(disk, Node->Inode, &inode); + + // Sanity Checks + if(Offset >= inode.i_size) { + LEAVE('i', 0); + return 0; + } + if(Offset + Length > inode.i_size) + Length = inode.i_size - Offset; + + block = Offset / disk->BlockSize; + Offset = Offset / disk->BlockSize; + base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); + if(base == 0) { + Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode); + LEAVE('i', 0); + return 0; + } + + // Read only block + if(Length <= disk->BlockSize - Offset) + { + VFS_ReadAt( disk->FD, base+Offset, Length, Buffer); + LEAVE('X', Length); + return Length; + } + + // Read first block + remLen = Length; + VFS_ReadAt( disk->FD, base + Offset, disk->BlockSize - Offset, Buffer); + remLen -= disk->BlockSize - Offset; + Buffer += disk->BlockSize - Offset; + block ++; + + // Read middle blocks + while(remLen > disk->BlockSize) + { + base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); + if(base == 0) { + Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode); + LEAVE('i', 0); + return 0; + } + VFS_ReadAt( disk->FD, base, disk->BlockSize, Buffer); + Buffer += disk->BlockSize; + remLen -= disk->BlockSize; + block ++; + } + + // Read last block + base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); + VFS_ReadAt( disk->FD, base, remLen, Buffer); + + LEAVE('X', Length); + return Length; +} diff --git a/KernelLand/Modules/Filesystems/Ext2/write.c b/KernelLand/Modules/Filesystems/Ext2/write.c new file mode 100644 index 00000000..03109e8a --- /dev/null +++ b/KernelLand/Modules/Filesystems/Ext2/write.c @@ -0,0 +1,368 @@ +/* + * Acess OS + * Ext2 Driver Version 1 + */ +/** + * \file write.c + * \brief Second Extended Filesystem Driver + * \todo Implement file full write support + */ +#define DEBUG 1 +#define VERBOSE 0 +#include "ext2_common.h" + +// === PROTOYPES === +Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock); +void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block); + int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block); + +// === CODE === +/** + * \fn Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Write to a file + */ +Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + tExt2_Disk *disk = Node->ImplPtr; + tExt2_Inode inode; + Uint64 base; + Uint64 retLen; + Uint block; + Uint64 allocSize; + int bNewBlocks = 0; + + Debug_HexDump("Ext2_Write", Buffer, Length); + + Ext2_int_ReadInode(disk, Node->Inode, &inode); + + // Get the ammount of space already allocated + // - Round size up to block size + // - block size is a power of two, so this will work + allocSize = (inode.i_size + disk->BlockSize-1) & ~(disk->BlockSize-1); + + // Are we writing to inside the allocated space? + if( Offset > allocSize ) return 0; + + if( Offset < allocSize ) + { + // Will we go out of it? + if(Offset + Length > allocSize) { + bNewBlocks = 1; + retLen = allocSize - Offset; + } else + retLen = Length; + + // Within the allocated space + block = Offset / disk->BlockSize; + Offset %= disk->BlockSize; + base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); + + // Write only block (if only one) + if(Offset + retLen <= disk->BlockSize) { + VFS_WriteAt(disk->FD, base+Offset, retLen, Buffer); + if(!bNewBlocks) return Length; + goto addBlocks; // Ugh! A goto, but it seems unavoidable + } + + // Write First Block + VFS_WriteAt(disk->FD, base+Offset, disk->BlockSize-Offset, Buffer); + Buffer += disk->BlockSize-Offset; + retLen -= disk->BlockSize-Offset; + block ++; + + // Write middle blocks + while(retLen > disk->BlockSize) + { + base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); + VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer); + Buffer += disk->BlockSize; + retLen -= disk->BlockSize; + block ++; + } + + // Write last block + base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); + VFS_WriteAt(disk->FD, base, retLen, Buffer); + if(!bNewBlocks) return Length; // Writing in only allocated space + } + else + base = Ext2_int_GetBlockAddr(disk, inode.i_block, allocSize/disk->BlockSize-1); + +addBlocks: + Log_Notice("EXT2", "File extending is untested"); + + // Allocate blocks and copy data to them + retLen = Length - (allocSize-Offset); + while( retLen > disk->BlockSize ) + { + // Allocate a block + block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize); + if(!block) return Length - retLen; + // Add it to this inode + if( !Ext2_int_AppendBlock(disk, &inode, block) ) { + Ext2_int_DeallocateBlock(disk, block); + goto ret; + } + // Copy data to the node + base = block * disk->BlockSize; + VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer); + // Update pointer and size remaining + inode.i_size += disk->BlockSize; + Buffer += disk->BlockSize; + retLen -= disk->BlockSize; + } + // Last block :D + block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize); + if(!block) goto ret; + if( !Ext2_int_AppendBlock(disk, &inode, block) ) { + Ext2_int_DeallocateBlock(disk, block); + goto ret; + } + base = block * disk->BlockSize; + VFS_WriteAt(disk->FD, base, retLen, Buffer); + inode.i_size += retLen; + retLen = 0; + +ret: // Makes sure the changes to the inode are committed + Ext2_int_WriteInode(disk, Node->Inode, &inode); + return Length - retLen; +} + +/** + * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock) + * \brief Allocate a block from the best possible location + * \param Disk EXT2 Disk Information Structure + * \param PrevBlock Previous block ID in the file + */ +Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock) +{ + int bpg = Disk->SuperBlock.s_blocks_per_group; + Uint blockgroup = PrevBlock / bpg; + Uint bitmap[Disk->BlockSize/sizeof(Uint)]; + Uint bitsperblock = 8*Disk->BlockSize; + int i, j = 0; + Uint block; + + // Are there any free blocks? + if(Disk->SuperBlock.s_free_blocks_count == 0) return 0; + + if(Disk->Groups[blockgroup].bg_free_blocks_count > 0) + { + // Search block group's bitmap + for(i = 0; i < bpg; i++) + { + // Get the block in the bitmap block + j = i & (bitsperblock-1); + + // Read in if needed + if(j == 0) { + VFS_ReadAt( + Disk->FD, + (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock, + Disk->BlockSize, + bitmap + ); + } + + // Fast Check + if( bitmap[j/32] == 0xFFFFFFFF ) { + j = (j + 31) & ~31; + continue; + } + + // Is the bit set? + if( bitmap[j/32] & (1 << (j%32)) ) + continue; + + // Ooh! We found one + break; + } + if( i < bpg ) { + Warning("[EXT2 ] Inconsistency detected, Group Free Block count is non-zero when no free blocks exist"); + goto checkAll; // Search the entire filesystem for a free block + // Goto needed for neatness + } + + // Mark as used + bitmap[j/32] |= (1 << (j%32)); + VFS_WriteAt( + Disk->FD, + (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock, + Disk->BlockSize, + bitmap + ); + block = i; + Disk->Groups[blockgroup].bg_free_blocks_count --; + #if EXT2_UPDATE_WRITEBACK + //Ext2_int_UpdateBlockGroup(Disk, blockgroup); + #endif + } + else + { + checkAll: + Log_Warning("EXT2", "TODO - Implement using blocks outside the current block group"); + return 0; + } + + // Reduce global count + Disk->SuperBlock.s_free_blocks_count --; + #if EXT2_UPDATE_WRITEBACK + Ext2_int_UpdateSuperblock(Disk); + #endif + + return block; +} + +/** + * \brief Deallocates a block + */ +void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block) +{ +} + +/** + * \brief Append a block to an inode + */ +int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block) +{ + int nBlocks; + int dwPerBlock = Disk->BlockSize / 4; + Uint32 *blocks; + Uint32 id1, id2; + + nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize; + + // Direct Blocks + if( nBlocks < 12 ) { + Inode->i_block[nBlocks] = Block; + return 0; + } + + blocks = malloc( Disk->BlockSize ); + if(!blocks) return 1; + + nBlocks -= 12; + // Single Indirect + if( nBlocks < dwPerBlock) + { + // Allocate/Get Indirect block + if( nBlocks == 0 ) { + Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]); + if( !Inode->i_block[12] ) { + free(blocks); + return 1; + } + memset(blocks, 0, Disk->BlockSize); + } + else + VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks); + + blocks[nBlocks] = Block; + + VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks); + free(blocks); + return 0; + } + + nBlocks += dwPerBlock; + // Double Indirect + if( nBlocks < dwPerBlock*dwPerBlock ) + { + // Allocate/Get Indirect block + if( nBlocks == 0 ) { + Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]); + if( !Inode->i_block[13] ) { + free(blocks); + return 1; + } + memset(blocks, 0, Disk->BlockSize); + } + else + VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks); + + // Allocate / Get Indirect lvl2 Block + if( nBlocks % dwPerBlock == 0 ) { + id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]); + if( !id1 ) { + free(blocks); + return 1; + } + blocks[nBlocks/dwPerBlock] = id1; + // Write back indirect 1 block + VFS_WriteAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks); + memset(blocks, 0, Disk->BlockSize); + } + else { + id1 = blocks[nBlocks / dwPerBlock]; + VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks); + } + + blocks[nBlocks % dwPerBlock] = Block; + + VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks); + free(blocks); + return 0; + } + + nBlocks -= dwPerBlock*dwPerBlock; + // Triple Indirect + if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock ) + { + // Allocate/Get Indirect block + if( nBlocks == 0 ) { + Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]); + if( !Inode->i_block[14] ) { + free(blocks); + return 1; + } + memset(blocks, 0, Disk->BlockSize); + } + else + VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks); + + // Allocate / Get Indirect lvl2 Block + if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 ) + { + id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]); + if( !id1 ) { + free(blocks); + return 1; + } + blocks[nBlocks/dwPerBlock] = id1; + // Write back indirect 1 block + VFS_WriteAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks); + memset(blocks, 0, Disk->BlockSize); + } + else { + id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)]; + VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks); + } + + // Allocate / Get Indirect Level 3 Block + if( nBlocks % dwPerBlock == 0 ) { + id2 = Ext2_int_AllocateBlock(Disk, id1); + if( !id2 ) { + free(blocks); + return 1; + } + blocks[(nBlocks/dwPerBlock)%dwPerBlock] = id2; + // Write back indirect 1 block + VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks); + memset(blocks, 0, Disk->BlockSize); + } + else { + id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock]; + VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks); + } + + blocks[nBlocks % dwPerBlock] = Block; + + VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks); + free(blocks); + return 0; + } + + Warning("[EXT2 ] Inode %i cannot have a block appended to it, all indirects used"); + free(blocks); + return 1; +} diff --git a/KernelLand/Modules/Filesystems/FAT/Makefile b/KernelLand/Modules/Filesystems/FAT/Makefile new file mode 100644 index 00000000..dfa290b8 --- /dev/null +++ b/KernelLand/Modules/Filesystems/FAT/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = fat.o +NAME = FAT + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Filesystems/FAT/fat.c b/KernelLand/Modules/Filesystems/FAT/fat.c new file mode 100644 index 00000000..f5d19161 --- /dev/null +++ b/KernelLand/Modules/Filesystems/FAT/fat.c @@ -0,0 +1,1538 @@ +/* + * Acess 2 + * FAT12/16/32 Driver Version (Incl LFN) + * + * NOTE: This driver will only support _reading_ long file names, not + * writing. I don't even know why I'm adding write-support. FAT sucks. + * + * Known Bugs: + * - LFN Is buggy in FAT_ReadDir + * + * Notes: + * - There's hard-coded 512 byte sectors everywhere, that needs to be + * cleaned. + * - Thread safety is out the window with the write and LFN code + */ +/** + * \todo Implement changing of the parent directory when a file is written to + * \todo Implement file creation / deletion + */ +#define DEBUG 0 +#define VERBOSE 1 + +#define CACHE_FAT 0 //!< Caches the FAT in memory +#define USE_LFN 1 //!< Enables the use of Long File Names +#define SUPPORT_WRITE 0 //!< Enables write support + +#include +#include +#include +#include "fs_fat.h" + +#define FAT_FLAG_DIRTY 0x10000 +#define FAT_FLAG_DELETE 0x20000 + +// === TYPES === +#if USE_LFN +/** + * \brief Long-Filename cache entry + */ +typedef struct sFAT_LFNCacheEnt +{ + int ID; + // TODO: Handle UTF16 names correctly + char Data[256]; +} tFAT_LFNCacheEnt; +/** + * \brief Long-Filename cache + */ +typedef struct sFAT_LFNCache +{ + int NumEntries; + tFAT_LFNCacheEnt Entries[]; +} tFAT_LFNCache; +#endif + +// === PROTOTYPES === +// --- Driver Core + int FAT_Install(char **Arguments); +tVFS_Node *FAT_InitDevice(const char *device, const char **options); +void FAT_Unmount(tVFS_Node *Node); +// --- Helpers + int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster); +Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 Cluster); +#if SUPPORT_WRITE +Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous); +Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster); +#endif +void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer); +// --- File IO +Uint64 FAT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +#if SUPPORT_WRITE +void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer); +Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +#endif +// --- Directory IO +char *FAT_ReadDir(tVFS_Node *Node, int ID); +tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name); +tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode); +#if SUPPORT_WRITE + int FAT_Mknod(tVFS_Node *Node, const char *Name, Uint Flags); + int FAT_Relink(tVFS_Node *node, const char *OldName, const char *NewName); +#endif +void FAT_CloseFile(tVFS_Node *node); + +// === Options === + int giFAT_MaxCachedClusters = 1024*512/4; + +// === SEMI-GLOBALS === +MODULE_DEFINE(0, (0<<8)|50 /*v0.50*/, VFAT, FAT_Install, NULL, NULL); +tFAT_VolInfo gFAT_Disks[8]; + int giFAT_PartCount = 0; +tVFS_Driver gFAT_FSInfo = {"fat", 0, FAT_InitDevice, FAT_Unmount, FAT_GetNodeFromINode, NULL}; +tVFS_NodeType gFAT_DirType = { + .TypeName = "FAT-Dir", + .ReadDir = FAT_ReadDir, + .FindDir = FAT_FindDir, + #if SUPPORT_WRITE + .MkNod = FAT_Mknod, + .Relink = FAT_Relink, + #endif + .Close = FAT_CloseFile + }; +tVFS_NodeType gFAT_FileType = { + .TypeName = "FAT-File", + .Read = FAT_Read, + #if SUPPORT_WRITE + .Write = FAT_Write, + #endif + .Close = FAT_CloseFile + }; + +// === CODE === +/** + * \fn int FAT_Install(char **Arguments) + * \brief Install the FAT Driver + */ +int FAT_Install(char **Arguments) +{ + VFS_AddDriver( &gFAT_FSInfo ); + return MODULE_ERR_OK; +} + +/** + * \brief Reads the boot sector of a disk and prepares the structures for it + */ +tVFS_Node *FAT_InitDevice(const char *Device, const char **Options) +{ + fat_bootsect *bs; + int i; + Uint32 FATSz, RootDirSectors, TotSec; + tVFS_Node *node = NULL; + tFAT_VolInfo *diskInfo = &gFAT_Disks[giFAT_PartCount]; + + // Temporary Pointer + bs = &diskInfo->bootsect; + + // Open device and read boot sector + diskInfo->fileHandle = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE); + if(diskInfo->fileHandle == -1) { + Log_Notice("FAT", "Unable to open device '%s'", Device); + return NULL; + } + + VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs); + + if(bs->bps == 0 || bs->spc == 0) { + Log_Notice("FAT", "Error in FAT Boot Sector"); + return NULL; + } + + // FAT Type Determining + // - From Microsoft FAT Specifcation + RootDirSectors = ((bs->files_in_root*32) + (bs->bps - 1)) / bs->bps; + + if(bs->fatSz16 != 0) + FATSz = bs->fatSz16; + else + FATSz = bs->spec.fat32.fatSz32; + + if(bs->totalSect16 != 0) + TotSec = bs->totalSect16; + else + TotSec = bs->totalSect32; + + diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc; + + if(diskInfo->ClusterCount < 4085) + diskInfo->type = FAT12; + else if(diskInfo->ClusterCount < 65525) + diskInfo->type = FAT16; + else + diskInfo->type = FAT32; + + #if VERBOSE + { + char *sFatType, *sSize; + Uint iSize = diskInfo->ClusterCount * bs->spc * bs->bps / 1024; + + switch(diskInfo->type) + { + case FAT12: sFatType = "FAT12"; break; + case FAT16: sFatType = "FAT16"; break; + case FAT32: sFatType = "FAT32"; break; + default: sFatType = "UNKNOWN"; break; + } + if(iSize <= 2*1024) { + sSize = "KiB"; + } + else if(iSize <= 2*1024*1024) { + sSize = "MiB"; + iSize >>= 10; + } + else { + sSize = "GiB"; + iSize >>= 20; + } + Log_Notice("FAT", "'%s' %s, %i %s", Device, sFatType, iSize, sSize); + } + #endif + + // Get Name + if(diskInfo->type == FAT32) { + for(i=0;i<11;i++) + diskInfo->name[i] = (bs->spec.fat32.label[i] == ' ' ? '\0' : bs->spec.fat32.label[i]); + } + else { + for(i=0;i<11;i++) + diskInfo->name[i] = (bs->spec.fat16.label[i] == ' ' ? '\0' : bs->spec.fat16.label[i]); + } + diskInfo->name[11] = '\0'; + + // Compute Root directory offset + if(diskInfo->type == FAT32) + diskInfo->rootOffset = bs->spec.fat32.rootClust; + else + diskInfo->rootOffset = (FATSz * bs->fatCount) / bs->spc; + + diskInfo->firstDataSect = bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors; + + //Allow for Caching the FAT + #if CACHE_FAT + if( diskInfo->ClusterCount <= giFAT_MaxCachedClusters ) + { + Uint32 Ofs; + diskInfo->FATCache = (Uint32*)malloc(sizeof(Uint32)*diskInfo->ClusterCount); + if(diskInfo->FATCache == NULL) { + Log_Warning("FAT", "Heap Exhausted"); + return NULL; + } + Ofs = bs->resvSectCount*512; + if(diskInfo->type == FAT12) + { + Uint32 val; + int j; + char buf[1536]; + for(i = 0; i < diskInfo->ClusterCount/2; i++) { + j = i & 511; //%512 + if( j == 0 ) { + VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf); + Ofs += 3*512; + } + val = *((int*)(buf+j*3)); + diskInfo->FATCache[i*2] = val & 0xFFF; + diskInfo->FATCache[i*2+1] = (val>>12) & 0xFFF; + } + } + else if(diskInfo->type == FAT16) + { + Uint16 buf[256]; + for(i=0;iClusterCount;i++) { + if( (i & 255) == 0 ) { + VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf); + Ofs += 512; + } + diskInfo->FATCache[i] = buf[i&255]; + } + } + else if(diskInfo->type == FAT32) + { + Uint32 buf[128]; + for(i=0;iClusterCount;i++) { + if( (i & 127) == 0 ) { + VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf); + Ofs += 512; + } + diskInfo->FATCache[i] = buf[i&127]; + } + } + LOG("FAT Fully Cached"); + } + #endif /*CACHE_FAT*/ + + diskInfo->BytesPerCluster = bs->spc * bs->bps; + + // Initalise inode cache for filesystem + diskInfo->inodeHandle = Inode_GetHandle(); + LOG("Inode Cache handle is %i", diskInfo->inodeHandle); + + // == VFS Interface + node = &diskInfo->rootNode; + //node->Size = bs->files_in_root; + node->Size = -1; + node->Inode = diskInfo->rootOffset; // 0:31 - Cluster, 32:63 - Parent Directory Cluster + node->ImplPtr = diskInfo; // Disk info pointer + node->ImplInt = 0; // 0:15 - Directory Index, 16: Dirty Flag, 17: Deletion Flag + + node->ReferenceCount = 1; + + node->UID = 0; node->GID = 0; + node->NumACLs = 1; + node->ACLs = &gVFS_ACL_EveryoneRWX; + node->Flags = VFS_FFLAG_DIRECTORY; + node->CTime = node->MTime = node->ATime = now(); + + node->Type = &gFAT_DirType; + + giFAT_PartCount ++; + return node; +} + +/** + * \brief Closes a mount and marks it as free + * \param Node Mount Root + * + * \todo Remove FAT Cache + * \todo Clear LFN Cache + * \todo Check that all files are closed and flushed + */ +void FAT_Unmount(tVFS_Node *Node) +{ + tFAT_VolInfo *disk = Node->ImplPtr; + + // Close Disk Handle + VFS_Close( disk->fileHandle ); + // Clear Node Cache + Inode_ClearCache(disk->inodeHandle); + // Mark as unused + disk->fileHandle = -2; + return; +} + +/** + * \brief Converts an offset in a file into a disk address + * \param Node File (or directory) node + * \param Offset Offset in the file + * \param Addr Return Address + * \param Cluster Set to the current cluster (or the last one if \a Offset + * is past EOC) - Not touched if the node is the root + * directory. + * \return Zero on success, non-zero on error + */ +int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster) +{ + Uint32 cluster; + Uint64 addr; + int skip; + tFAT_VolInfo *disk = Node->ImplPtr; + + ENTER("pNode XOffset", Node, Offset); + + cluster = Node->Inode & 0xFFFFFFF; // Cluster ID + LOG("cluster = 0x%07x", cluster); + + // Do Cluster Skip + // - Pre FAT32 had a reserved area for the root. + if( disk->type == FAT32 || cluster != disk->rootOffset ) + { + skip = Offset / disk->BytesPerCluster; + LOG("skip = %i", skip); + // Skip previous clusters + for(; skip-- ; ) + { + if(Cluster) *Cluster = cluster; + cluster = FAT_int_GetFatValue(disk, cluster); + // Check for end of cluster chain + if(cluster == 0xFFFFFFFF) { LEAVE('i', 1); return 1;} + } + if(Cluster) *Cluster = cluster; + } + else { + // Increment by clusters in offset + cluster += Offset / disk->BytesPerCluster; + } + + LOG("cluster = %08x", cluster); + + // Bounds Checking (Used to spot corruption) + if(cluster > disk->ClusterCount + 2) + { + Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)", + cluster, disk->ClusterCount+2); + LEAVE('i', 1); + return 1; + } + + // Compute Offsets + // - Pre FAT32 cluster base (in sectors) + if( cluster == disk->rootOffset && disk->type != FAT32 ) { + addr = disk->bootsect.resvSectCount * disk->bootsect.bps; + addr += cluster * disk->BytesPerCluster; + } + else { + addr = disk->firstDataSect * disk->bootsect.bps; + addr += (cluster - 2) * disk->BytesPerCluster; + } + // In-cluster offset + addr += Offset % disk->BytesPerCluster; + + LOG("addr = 0x%08x", addr); + *Addr = addr; + LEAVE('i', 0); + return 0; +} + +/* + * ==================== + * FAT Manipulation + * ==================== + */ +/** + * \fn Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster) + * \brief Fetches a value from the FAT + */ +Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster) +{ + Uint32 val = 0; + Uint32 ofs; + ENTER("pDisk xCluster", Disk, cluster); + Mutex_Acquire( &Disk->lFAT ); + #if CACHE_FAT + if( Disk->ClusterCount <= giFAT_MaxCachedClusters ) + { + val = Disk->FATCache[cluster]; + if(Disk->type == FAT12 && val == EOC_FAT12) val = -1; + if(Disk->type == FAT16 && val == EOC_FAT16) val = -1; + if(Disk->type == FAT32 && val == EOC_FAT32) val = -1; + } + else + { + #endif + ofs = Disk->bootsect.resvSectCount*512; + if(Disk->type == FAT12) { + VFS_ReadAt(Disk->fileHandle, ofs+(cluster/2)*3, 3, &val); + val = (cluster & 1 ? val>>12 : val & 0xFFF); + if(val == EOC_FAT12) val = -1; + } else if(Disk->type == FAT16) { + VFS_ReadAt(Disk->fileHandle, ofs+cluster*2, 2, &val); + if(val == EOC_FAT16) val = -1; + } else { + VFS_ReadAt(Disk->fileHandle, ofs+cluster*4, 4, &val); + if(val == EOC_FAT32) val = -1; + } + #if CACHE_FAT + } + #endif /*CACHE_FAT*/ + Mutex_Release( &Disk->lFAT ); + LEAVE('x', val); + return val; +} + +#if SUPPORT_WRITE +/** + * \brief Allocate a new cluster + */ +Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous) +{ + Uint32 ret = Previous; + #if CACHE_FAT + if( Disk->ClusterCount <= giFAT_MaxCachedClusters ) + { + Uint32 eoc; + + LOCK(Disk->lFAT); + for(ret = Previous; ret < Disk->ClusterCount; ret++) + { + if(Disk->FATCache[ret] == 0) + goto append; + } + for(ret = 0; ret < Previous; ret++) + { + if(Disk->FATCache[ret] == 0) + goto append; + } + + RELEASE(Disk->lFAT); + return 0; + + append: + switch(Disk->type) + { + case FAT12: eoc = EOC_FAT12; break; + case FAT16: eoc = EOC_FAT16; break; + case FAT32: eoc = EOC_FAT32; break; + default: return 0; + } + + Disk->FATCache[ret] = eoc; + Disk->FATCache[Previous] = ret; + + RELEASE(Disk->lFAT); + return ret; + } + else + { + #endif + Uint32 val; + Uint32 ofs = Disk->bootsect.resvSectCount*512; + Log_Warning("FAT", "TODO: Implement cluster allocation with non cached FAT"); + return 0; + + switch(Disk->type) + { + case FAT12: + VFS_ReadAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val); + if( Previous & 1 ) { + val &= 0xFFF000; + val |= ret; + } + else { + val &= 0xFFF; + val |= ret<<12; + } + VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val); + + VFS_ReadAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val); + if( Cluster & 1 ) { + val &= 0xFFF000; + val |= eoc; + } + else { + val &= 0x000FFF; + val |= eoc<<12; + } + VFS_WriteAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val); + break; + case FAT16: + VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret); + VFS_WriteAt(Disk->fileHandle, ofs+ret*2, 2, &eoc); + break; + case FAT32: + VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret); + VFS_WriteAt(Disk->fileHandle, ofs+ret*4, 4, &eoc); + break; + } + return ret; + #if CACHE_FAT + } + #endif +} + +/** + * \brief Free's a cluster + * \return The original contents of the cluster + */ +Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster) +{ + Uint32 ret; + #if CACHE_FAT + if( Disk->ClusterCount <= giFAT_MaxCachedClusters ) + { + LOCK(Disk->lFAT); + + ret = Disk->FATCache[Cluster]; + Disk->FATCache[Cluster] = 0; + + RELEASE(Disk->lFAT); + } + else + { + #endif + Uint32 val; + Uint32 ofs = Disk->bootsect.resvSectCount*512; + LOCK(Disk->lFAT); + switch(Disk->type) + { + case FAT12: + VFS_ReadAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val); + if( Cluster & 1 ) { + ret = val & 0xFFF0000; + val &= 0xFFF; + } + else { + ret = val & 0xFFF; + val &= 0xFFF000; + } + VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val); + break; + case FAT16: + VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret); + val = 0; + VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val); + break; + case FAT32: + VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret); + val = 0; + VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val); + break; + } + RELEASE(Disk->lFAT); + #if CACHE_FAT + } + #endif + if(Disk->type == FAT12 && ret == EOC_FAT12) ret = -1; + if(Disk->type == FAT16 && ret == EOC_FAT16) ret = -1; + if(Disk->type == FAT32 && ret == EOC_FAT32) ret = -1; + return ret; +} +#endif + +/* + * ==================== + * Cluster IO + * ==================== + */ +/** + * \brief Read a cluster + * \param Disk Disk (Volume) to read from + * \param Length Length to read + * \param Buffer Destination for read data + */ +void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer) +{ + ENTER("pDisk xCluster iLength pBuffer", Disk, Cluster, Length, Buffer); + VFS_ReadAt( + Disk->fileHandle, + (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc ) + * Disk->bootsect.bps, + Length, + Buffer + ); + LEAVE('-'); +} + +/* ==================== + * File IO + * ==================== + */ +/** + * \fn Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer) + * \brief Reads data from a specified file + */ +Uint64 FAT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + int preSkip, count; + Uint64 final_bytes; + int i, cluster, pos; + tFAT_VolInfo *disk = Node->ImplPtr; + char tmpBuf[disk->BytesPerCluster]; + int bpc = disk->BytesPerCluster; + + ENTER("pNode Xoffset Xlength pbuffer", Node, Offset, Length, Buffer); + + // Sanity Check offset + if(Offset > Node->Size) { + LOG("Seek past EOF (%i > %i)", Offset, Node->Size); + LEAVE('i', 0); + return 0; + } + + // Cluster is stored in the low 32-bits of the Inode field + cluster = Node->Inode & 0xFFFFFFFF; + + // Clamp Size + if(Offset + Length > Node->Size) { + LOG("Reading past EOF (%lli + %lli > %lli), clamped to %lli", + Offset, Length, Node->Size, Node->Size - Offset); + Length = Node->Size - Offset; + } + + // Skip previous clusters + preSkip = Offset / bpc; + Offset %= bpc; + LOG("preSkip = %i, Offset = %i", preSkip, (int)Offset); + for(i = preSkip; i--; ) + { + cluster = FAT_int_GetFatValue(disk, cluster); + if(cluster == -1) { + Log_Warning("FAT", "Offset is past end of cluster chain mark"); + LEAVE('i', 0); + return 0; + } + } + + // Reading from within one cluster + if((int)Offset + (int)Length <= bpc) + { + LOG("single cluster only"); + FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf); + memcpy( Buffer, (void*)( tmpBuf + Offset%bpc ), Length ); + LEAVE('X', Length); + return Length; + } + + // Align read to a cluster + if( Offset > 0 ) + { + pos = bpc - Offset; + FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf); + memcpy( Buffer, (void*)( tmpBuf + Offset ), pos ); + LOG("pos = %i, Reading the rest of the clusters"); + // Get next cluster in the chain + cluster = FAT_int_GetFatValue(disk, cluster); + if(cluster == -1) { + Log_Warning("FAT", "Read past End of Cluster Chain (Align)"); + LEAVE('X', pos); + return pos; + } + } + else + pos = 0; + + // Get Count of Clusters to read +// count = DivMod64U(Length - pos, bpc, &final_bytes); + count = (Length - pos) / bpc; + final_bytes = (Length - pos) % bpc; + LOG("Offset = %i, Length = %i, count = %i, final_bytes = %i", (int)Offset, (int)Length, count, final_bytes); + + // Read the rest of the cluster data + for( ; count; count -- ) + { + if(cluster == -1) { + Log_Warning("FAT", "Read past End of Cluster Chain (Bulk)"); + LEAVE('X', pos); + return pos; + } + // Read cluster + FAT_int_ReadCluster(disk, cluster, bpc, (void*)(Buffer+pos)); + pos += bpc; + // Get next cluster in the chain + cluster = FAT_int_GetFatValue(disk, cluster); + } + + if( final_bytes > 0 ) + { + if(cluster == -1) { + Log_Warning("FAT", "Read past End of Cluster Chain (Final)"); + LEAVE('X', pos); + return pos; + } + // Read final cluster + FAT_int_ReadCluster( disk, cluster, bpc, tmpBuf ); + memcpy( (void*)(Buffer+pos), tmpBuf, Length-pos ); + } + + #if DEBUG + //Debug_HexDump("FAT_Read", Buffer, Length); + #endif + + LEAVE('X', Length); + return Length; +} + +#if SUPPORT_WRITE +/** + * \brief Write a cluster to disk + */ +void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer) +{ + ENTER("pDisk xCluster pBuffer", Disk, Cluster, Buffer); + VFS_ReadAt( + Disk->fileHandle, + (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc ) + * Disk->bootsect.bps, + Disk->BytesPerCluster, + Buffer + ); + LEAVE('-'); +} + +/** + * \brief Write to a file + * \param Node File Node + * \param Offset Offset within file + * \param Length Size of data to write + * \param Buffer Data source + */ +Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tFAT_VolInfo *disk = Node->ImplPtr; + char tmpBuf[disk->BytesPerCluster]; + int remLength = Length; + Uint32 cluster, tmpCluster; + int bNewCluster = 0; + + if(Offset > Node->Size) return 0; + + // Seek Clusters + cluster = Node->Inode & 0xFFFFFFFF; + while( Offset > disk->BytesPerCluster ) + { + cluster = FAT_int_GetFatValue( disk, cluster ); + if(cluster == -1) { + Log_Warning("FAT", "EOC Unexpectedly Reached"); + return 0; + } + Offset -= disk->BytesPerCluster; + } + if( Offset == disk->BytesPerCluster ) + { + Uint32 tmp = FAT_int_AllocateCluster(disk, cluster); + if(!tmp) return 0; + cluster = tmp; + Offset -= disk->BytesPerCluster; + } + + if( Offset + Length < disk->BytesPerCluster ) + { + char tmpBuf[disk->BytesPerCluster]; + + // Read-Modify-Write + FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf ); + memcpy( tmpBuf + Offset, Buffer, Length ); + FAT_int_WriteCluster( disk, cluster, tmpBuf ); + + return Length; + } + + // Clean up changes within a cluster + if( Offset ) + { + // Read-Modify-Write + FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf ); + memcpy( tmpBuf + Offset, Buffer, disk->BytesPerCluster - Offset ); + FAT_int_WriteCluster( disk, cluster, tmpBuf ); + + remLength -= disk->BytesPerCluster - Offset; + Buffer += disk->BytesPerCluster - Offset; + + // Get next cluster (allocating if needed) + tmpCluster = FAT_int_GetFatValue(disk, cluster); + if(tmpCluster == -1) { + tmpCluster = FAT_int_AllocateCluster(disk, cluster); + if( tmpCluster == 0 ) { + return Length - remLength; + } + } + cluster = tmpCluster; + } + + while( remLength > disk->BytesPerCluster ) + { + FAT_int_WriteCluster( disk, cluster, Buffer ); + Buffer += disk->BytesPerCluster; + + // Get next cluster (allocating if needed) + tmpCluster = FAT_int_GetFatValue(disk, cluster); + if(tmpCluster == -1) { + bNewCluster = 1; + tmpCluster = FAT_int_AllocateCluster(disk, cluster); + if( tmpCluster == 0 ) { + return Length - remLength; + } + } + cluster = tmpCluster; + } + + // Finish off + tmpBuf = malloc( disk->BytesPerCluster ); + if( bNewCluster ) + memset(tmpBuf, 0, disk->BytesPerCluster); + else + FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf ); + memcpy( tmpBuf, Buffer, remLength ); + FAT_int_WriteCluster( disk, cluster, tmpBuf ); + free( tmpBuf ); + + return Length; +} +#endif + +/* ==================== + * File Names & Nodes + * ==================== + */ +/** + * \brief Converts a FAT directory entry name into a proper filename + * \param dest Destination array (must be at least 13 bytes in size) + * \param src 8.3 filename (concatenated, e.g 'FILE1 TXT') + */ +void FAT_int_ProperFilename(char *dest, const char *src) +{ + int inpos, outpos; + + // Name + outpos = 0; + for( inpos = 0; inpos < 8; inpos++ ) { + if(src[inpos] == ' ') break; + dest[outpos++] = src[inpos]; + } + inpos = 8; + // Check for empty extensions + if(src[8] != ' ') + { + dest[outpos++] = '.'; + for( ; inpos < 11; inpos++) { + if(src[inpos] == ' ') break; + dest[outpos++] = src[inpos]; + } + } + dest[outpos++] = '\0'; + + //LOG("dest='%s'", dest); +} + +/** + * \fn char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName) + * \brief Converts either a LFN or a 8.3 Name into a proper name + * \param ft Pointer to the file's entry in the parent directory + * \param LongFileName Long file name pointer + * \return Filename as a heap string + */ +char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName) +{ + char *ret; + ENTER("pft sLongFileName", ft, LongFileName); + //Log_Debug("FAT", "FAT_int_CreateName(ft=%p, LongFileName=%p'%s')", ft, LongFileName); + #if USE_LFN + if(LongFileName && LongFileName[0] != '\0') + { + ret = strdup(LongFileName); + } + else + { + #endif + ret = (char*) malloc(13); + if( !ret ) { + Log_Warning("FAT", "FAT_int_CreateName: malloc(13) failed"); + return NULL; + } + FAT_int_ProperFilename(ret, ft->name); + #if USE_LFN + } + #endif + LEAVE('s', ret); + return ret; +} + +/** + * \brief Creates a tVFS_Node structure for a given file entry + * \param Parent Parent directory VFS node + * \param Entry File table entry for the new node + * \param Pos Position in the parent of the new node + */ +tVFS_Node *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry, int Pos) +{ + tVFS_Node node; + tVFS_Node *ret; + tFAT_VolInfo *disk = Parent->ImplPtr; + + ENTER("pParent pFT", Parent, Entry); + LOG("disk = %p", disk); + + memset(&node, 0, sizeof(tVFS_Node)); + + // Set Other Data + // 0-27: Cluster, 32-59: Parent Cluster + node.Inode = Entry->cluster | (Entry->clusterHi<<16) | (Parent->Inode << 32); + LOG("node.Inode = %llx", node.Inode); + // Position in parent directory + node.ImplInt = Pos & 0xFFFF; + // Disk Pointer + node.ImplPtr = disk; + node.Size = Entry->size; + LOG("Entry->size = %i", Entry->size); + // root:root + node.UID = 0; node.GID = 0; + node.NumACLs = 1; + + node.Flags = 0; + if(Entry->attrib & ATTR_DIRECTORY) node.Flags |= VFS_FFLAG_DIRECTORY; + if(Entry->attrib & ATTR_READONLY) { + node.Flags |= VFS_FFLAG_READONLY; + node.ACLs = &gVFS_ACL_EveryoneRX; // R-XR-XR-X + } + else { + node.ACLs = &gVFS_ACL_EveryoneRWX; // RWXRWXRWX + } + + // Create timestamps + node.ATime = timestamp(0,0,0, + ((Entry->adate&0x1F) - 1), // Days + ((Entry->adate&0x1E0) - 1), // Months + 1980+((Entry->adate&0xFF00)>>8) // Years + ); + + node.CTime = Entry->ctimems * 10; // Miliseconds + node.CTime += timestamp( + ((Entry->ctime&0x1F)<<1), // Seconds + ((Entry->ctime&0x3F0)>>5), // Minutes + ((Entry->ctime&0xF800)>>11), // Hours + ((Entry->cdate&0x1F)-1), // Days + ((Entry->cdate&0x1E0)-1), // Months + 1980+((Entry->cdate&0xFF00)>>8) // Years + ); + + node.MTime = timestamp( + ((Entry->mtime&0x1F)<<1), // Seconds + ((Entry->mtime&0x3F0)>>5), // Minutes + ((Entry->mtime&0xF800)>>11), // Hours + ((Entry->mdate&0x1F)-1), // Days + ((Entry->mdate&0x1E0)-1), // Months + 1980+((Entry->mdate&0xFF00)>>8) // Years + ); + + // Set pointers + if(node.Flags & VFS_FFLAG_DIRECTORY) { + //Log_Debug("FAT", "Directory %08x has size 0x%x", node.Inode, node.Size); + node.Type = &gFAT_DirType; + node.Size = -1; + } + else { + node.Type = &gFAT_FileType; + } + + ret = Inode_CacheNode(disk->inodeHandle, &node); + LEAVE('p', ret); + return ret; +} + +/* + * ==================== + * Directory IO + * ==================== + */ + +/** + * \brief Reads a sector from the disk + * \param Node Directory node to read + * \param Sector Sector number in the directory to read + * \param Buffer Destination buffer for the read data + */ +int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer) +{ + Uint64 addr; + tFAT_VolInfo *disk = Node->ImplPtr; + + ENTER("pNode iSector pEntry", Node, Sector, Buffer); + + // Parse address + if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL)) + { + LEAVE('i', 1); + return 1; + } + + LOG("addr = 0x%llx", addr); + // Read Sector + if(VFS_ReadAt(disk->fileHandle, addr, 512, Buffer) != 512) + { + LEAVE('i', 1); + return 1; + } + + LEAVE('i', 0); + return 0; +} + +#if SUPPORT_WRITE +/** + * \brief Writes an entry to the disk + * \todo Support expanding a directory + * \param Node Directory node + * \param ID ID of entry to update + * \param Entry Entry data + * \return Zero on success, non-zero on error + */ +int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry) +{ + Uint64 addr = 0; + int tmp; + Uint32 cluster = 0; + tFAT_VolInfo *disk = Node->ImplPtr; + + ENTER("pNode iID pEntry", Node, ID, Entry); + + tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster); + if( tmp ) + { + //TODO: Allocate a cluster + cluster = FAT_int_AllocateCluster(Node->ImplPtr, cluster); + if(cluster == -1) { + Log_Warning("FAT", "Unable to allocate an other cluster for %p", Node); + LEAVE('i', 1); + return 1; + } + FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster); + } + + + LOG("addr = 0x%llx", addr); + + // Read Sector + VFS_WriteAt(disk->fileHandle, addr, sizeof(fat_filetable), Entry); // Read Dir Data + + LEAVE('i', 0); + return 0; +} +#endif + +#if USE_LFN +/** + * \fn char *FAT_int_GetLFN(tVFS_Node *node) + * \brief Return pointer to LFN cache entry + * \param Node Directory node + * \param ID ID of the short name + * \return Pointer to the LFN cache entry + */ +char *FAT_int_GetLFN(tVFS_Node *Node, int ID) +{ + tFAT_LFNCache *cache; + int i, firstFree; + + Mutex_Acquire( &Node->Lock ); + + // TODO: Thread Safety (Lock things) + cache = Node->Data; + + // Create a cache if it isn't there + if(!cache) { + cache = Node->Data = malloc( sizeof(tFAT_LFNCache) + sizeof(tFAT_LFNCacheEnt) ); + cache->NumEntries = 1; + cache->Entries[0].ID = ID; + cache->Entries[0].Data[0] = '\0'; + Mutex_Release( &Node->Lock ); + //Log_Debug("FAT", "Return = %p (new)", cache->Entries[0].Data); + return cache->Entries[0].Data; + } + + // Scan for this entry + firstFree = -1; + for( i = 0; i < cache->NumEntries; i++ ) + { + if( cache->Entries[i].ID == ID ) { + Mutex_Release( &Node->Lock ); + //Log_Debug("FAT", "Return = %p (match)", cache->Entries[i].Data); + return cache->Entries[i].Data; + } + if( cache->Entries[i].ID == -1 && firstFree == -1 ) + firstFree = i; + } + + if(firstFree == -1) { + // Use `i` for temp length + i = sizeof(tFAT_LFNCache) + (cache->NumEntries+1)*sizeof(tFAT_LFNCacheEnt); + Node->Data = realloc( Node->Data, i ); + if( !Node->Data ) { + Log_Error("FAT", "realloc() fail, unable to allocate %i for LFN cache", i); + Mutex_Release( &Node->Lock ); + return NULL; + } + //Log_Debug("FAT", "Realloc (%i)\n", i); + cache = Node->Data; + i = cache->NumEntries; + cache->NumEntries ++; + } + else { + i = firstFree; + } + + // Create new entry + cache->Entries[ i ].ID = ID; + cache->Entries[ i ].Data[0] = '\0'; + + Mutex_Release( &Node->Lock ); + //Log_Debug("FAT", "Return = %p (firstFree, i = %i)", cache->Entries[i].Data, i); + return cache->Entries[ i ].Data; +} + +/** + * \fn void FAT_int_DelLFN(tVFS_Node *node) + * \brief Delete a LFN cache entry + * \param Node Directory node + * \param ID File Entry ID + */ +void FAT_int_DelLFN(tVFS_Node *Node, int ID) +{ + tFAT_LFNCache *cache = Node->Data; + int i; + + // Fast return + if(!cache) return; + + // Scan for a current entry + for( i = 0; i < cache->NumEntries; i++ ) + { + if( cache->Entries[i].ID == ID ) + cache->Entries[i].ID = -1; + } + return ; +} +#endif + +/** + * \fn char *FAT_ReadDir(tVFS_Node *Node, int ID) + * \param Node Node structure of directory + * \param ID Directory position + * \return Filename as a heap string, NULL or VFS_SKIP + */ +char *FAT_ReadDir(tVFS_Node *Node, int ID) +{ + fat_filetable fileinfo[16]; // sizeof(fat_filetable)=32, so 16 per sector + int a = 0; + char *ret; + #if USE_LFN + char *lfn = NULL; + #endif + + ENTER("pNode iID", Node, ID); + + if(FAT_int_ReadDirSector(Node, ID/16, fileinfo)) + { + LOG("End of chain, end of dir"); + LEAVE('n'); + return NULL; + } + + // Offset in sector + a = ID % 16; + + LOG("fileinfo[%i].name[0] = 0x%x", a, (Uint8)fileinfo[a].name[0]); + + // Check if this is the last entry + if( fileinfo[a].name[0] == '\0' ) { + Node->Size = ID; + LOG("End of list"); + LEAVE('n'); + return NULL; // break + } + + // Check for empty entry + if( (Uint8)fileinfo[a].name[0] == 0xE5 ) { + LOG("Empty Entry"); + #if 0 // Stop on empty entry? + LEAVE('n'); + return NULL; // Stop + #else + LEAVE('p', VFS_SKIP); + return VFS_SKIP; // Skip + #endif + } + + #if USE_LFN + // Get Long File Name Cache + if(fileinfo[a].attrib == ATTR_LFN) + { + fat_longfilename *lfnInfo; + + lfnInfo = (fat_longfilename *) &fileinfo[a]; + + // Get cache for corresponding file + // > ID + Index gets the corresponding short node + lfn = FAT_int_GetLFN( Node, ID + (lfnInfo->id & 0x3F) ); + + // Bit 6 indicates the start of an entry + if(lfnInfo->id & 0x40) memset(lfn, 0, 256); + + a = ((lfnInfo->id & 0x3F) - 1) * 13; + //Log_Debug("FAT", "ID = 0x%02x, a = %i", lfnInfo->id, a); + + // Sanity Check (FAT implementations should not allow >255 character names) + if(a > 255) return VFS_SKIP; + + // Append new bytes + lfn[a+ 0] = lfnInfo->name1[0]; lfn[a+ 1] = lfnInfo->name1[1]; + lfn[a+ 2] = lfnInfo->name1[2]; lfn[a+ 3] = lfnInfo->name1[3]; + lfn[a+ 4] = lfnInfo->name1[4]; + lfn[a+ 5] = lfnInfo->name2[0]; lfn[a+ 6] = lfnInfo->name2[1]; + lfn[a+ 7] = lfnInfo->name2[2]; lfn[a+ 8] = lfnInfo->name2[3]; + lfn[a+ 9] = lfnInfo->name2[4]; lfn[a+10] = lfnInfo->name2[5]; + lfn[a+11] = lfnInfo->name3[0]; lfn[a+12] = lfnInfo->name3[1]; + LOG("lfn = '%s'", lfn); + //Log_Debug("FAT", "lfn = '%s'", lfn); + LEAVE('p', VFS_SKIP); + return VFS_SKIP; + } + #endif + + // Check if it is a volume entry + if(fileinfo[a].attrib & 0x08) { + LEAVE('p', VFS_SKIP); + return VFS_SKIP; + } + // Ignore . + if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == ' ') { + LEAVE('p', VFS_SKIP); + return VFS_SKIP; + } + // and .. + if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == '.' && fileinfo[a].name[2] == ' ') { + LEAVE('p', VFS_SKIP); + return VFS_SKIP; + } + + LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'", + fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3], + fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7], + fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] ); + + #if USE_LFN + lfn = FAT_int_GetLFN(Node, ID); + //Log_Debug("FAT", "lfn = %p'%s'", lfn, lfn); + ret = FAT_int_CreateName(&fileinfo[a], lfn); + #else + ret = FAT_int_CreateName(&fileinfo[a], NULL); + #endif + + LEAVE('s', ret); + return ret; +} + +/** + * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name) + * \brief Finds an entry in the current directory + */ +tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name) +{ + fat_filetable fileinfo[16]; + char tmpName[13]; + #if USE_LFN + fat_longfilename *lfnInfo; + char lfn[256]; + int lfnPos=255, lfnId = -1; + #endif + int i; + tVFS_Node *tmpNode; + tFAT_VolInfo *disk = Node->ImplPtr; + Uint32 cluster; + + ENTER("pNode sname", Node, Name); + + // Fast Returns + if(!Name || Name[0] == '\0') { + LEAVE('n'); + return NULL; + } + + for( i = 0; ; i++ ) + { + if((i & 0xF) == 0) { + if(FAT_int_ReadDirSector(Node, i/16, fileinfo)) + { + LEAVE('n'); + return NULL; + } + } + + //Check if the files are free + if(fileinfo[i&0xF].name[0] == '\0') break; // End of List marker + if(fileinfo[i&0xF].name[0] == '\xE5') continue; // Free entry + + + #if USE_LFN + // Long File Name Entry + if(fileinfo[i & 0xF].attrib == ATTR_LFN) + { + lfnInfo = (fat_longfilename *) &fileinfo[i&0xF]; + if(lfnInfo->id & 0x40) { + memset(lfn, 0, 256); + lfnPos = (lfnInfo->id & 0x3F) * 13 - 1; + } + // Sanity check the position so we don't overflow + if( lfnPos < 12 ) + continue ; + lfn[lfnPos--] = lfnInfo->name3[1]; lfn[lfnPos--] = lfnInfo->name3[0]; + lfn[lfnPos--] = lfnInfo->name2[5]; lfn[lfnPos--] = lfnInfo->name2[4]; + lfn[lfnPos--] = lfnInfo->name2[3]; lfn[lfnPos--] = lfnInfo->name2[2]; + lfn[lfnPos--] = lfnInfo->name2[1]; lfn[lfnPos--] = lfnInfo->name2[0]; + lfn[lfnPos--] = lfnInfo->name1[4]; lfn[lfnPos--] = lfnInfo->name1[3]; + lfn[lfnPos--] = lfnInfo->name1[2]; lfn[lfnPos--] = lfnInfo->name1[1]; + lfn[lfnPos--] = lfnInfo->name1[0]; + if((lfnInfo->id&0x3F) == 1) + { + lfnId = i+1; + } + } + else + { + // Remove LFN if it does not apply + if(lfnId != i) lfn[0] = '\0'; + #else + if(fileinfo[i&0xF].attrib == ATTR_LFN) continue; + #endif + // Get Real Filename + FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name); + LOG("tmpName = '%s'", tmpName); + + // Only the long name is case sensitive, 8.3 is not + #if USE_LFN + if(strucmp(tmpName, Name) == 0 || strcmp(lfn, Name) == 0) + #else + if(strucmp(tmpName, Name) == 0) + #endif + { + cluster = fileinfo[i&0xF].cluster | (fileinfo[i&0xF].clusterHi << 16); + tmpNode = Inode_GetCache(disk->inodeHandle, cluster); + if(tmpNode == NULL) // Node is not cached + { + tmpNode = FAT_int_CreateNode(Node, &fileinfo[i&0xF], i); + } + LEAVE('p', tmpNode); + return tmpNode; + } + #if USE_LFN + } + #endif + } + + LEAVE('n'); + return NULL; +} + +tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode) +{ + tFAT_VolInfo *disk = Root->ImplPtr; + int ents_per_sector = 512 / sizeof(fat_filetable); + fat_filetable fileinfo[ents_per_sector]; + int sector = 0, i; + tVFS_Node stub_node; + + ENTER("pRoot XInode", Root, Inode); + + stub_node.ImplPtr = disk; + stub_node.Size = -1; + stub_node.Inode = Inode >> 32; + + for( i = 0; ; i ++ ) + { + if( i == 0 || i == ents_per_sector ) + { + if(FAT_int_ReadDirSector(&stub_node, sector, fileinfo)) + { + LOG("ReadDirSector failed"); + LEAVE('n'); + return NULL; + } + i = 0; + sector ++; + } + + // Check for free/end of list + if(fileinfo[i].name[0] == '\0') break; // End of List marker + if(fileinfo[i].name[0] == '\xE5') continue; // Free entry + + if(fileinfo[i].attrib == ATTR_LFN) continue; + + LOG("fileinfo[i].cluster = %x %04x", fileinfo[i].clusterHi, fileinfo[i].cluster); + #if DEBUG + { + char tmpName[13]; + FAT_int_ProperFilename(tmpName, fileinfo[i].name); + LOG("tmpName = '%s'", tmpName); + } + #endif + + + if(fileinfo[i].cluster != (Inode & 0xFFFF)) continue; + if(fileinfo[i].clusterHi != ((Inode >> 16) & 0xFFFF)) continue; + + LEAVE_RET('p', FAT_int_CreateNode(&stub_node, &fileinfo[i], sector*ents_per_sector+i)); + } + LOG("sector = %i, i = %i", sector, i); + LEAVE('n'); + return NULL; +} + +#if SUPPORT_WRITE +/** + * \fn int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags) + * \brief Create a new node + */ +int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags) +{ + return 0; +} + +/** + * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName) + * \brief Rename / Delete a file + */ +int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName) +{ + tVFS_Node *child; + fat_filetable ft = {0}; + int ret; + + child = FAT_FindDir(Node, OldName); + if(!child) return ENOTFOUND; + + // Delete? + if( NewName == NULL ) + { + child->ImplInt |= FAT_FLAG_DELETE; // Mark for deletion on close + + // Delete from the directory + ft.name[0] = '\xE9'; + FAT_int_WriteDirEntry(Node, child->ImplInt & 0xFFFF, &ft); + + // Return success + ret = EOK; + } + // Rename + else + { + Log_Warning("FAT", "Renaming no yet supported %p ('%s' => '%s')", + Node, OldName, NewName); + ret = ENOTIMPL; + } + + // Close child + child->Close( child ); + return ret; +} +#endif + +/** + * \fn void FAT_CloseFile(tVFS_Node *Node) + * \brief Close an open file + */ +void FAT_CloseFile(tVFS_Node *Node) +{ + tFAT_VolInfo *disk = Node->ImplPtr; + if(Node == NULL) return ; + + #if SUPPORT_WRITE + // Update the node if it's dirty (don't bother if it's marked for + // deletion) + if( (Node->ImplInt & FAT_FLAG_DIRTY) && !(Node->ImplInt & FAT_FLAG_DELETE) ) + { + tFAT_VolInfo buf[16]; + tFAT_VolInfo *ft = &buf[ (Node->ImplInt & 0xFFFF) % 16 ]; + + FAT_int_ReadDirSector(Node, (Node->ImplInt & 0xFFFF)/16, buf); + ft->size = Node->Size; + // TODO: update adate, mtime, mdate + FAT_int_WriteDirEntry(Node, Node->ImplInt & 0xFFFF, ft); + + Node->ImplInt &= ~FAT_FLAG_DIRTY; + } + #endif + + // TODO: Make this more thread safe somehow, probably by moving the + // Inode_UncacheNode higher up and saving the cluster value somewhere + if( Node->ReferenceCount == 1 ) + { + #if SUPPORT_WRITE + // Delete File + if( Node->ImplInt & FAT_FLAG_DELETE ) { + // Since the node is marked, we only need to remove it's data + Uint32 cluster = Node->Inode & 0xFFFFFFFF; + while( cluster != -1 ) + cluster = FAT_int_FreeCluster(Node->ImplPtr, cluster); + } + #endif + } + + Inode_UncacheNode(disk->inodeHandle, Node->Inode); + return ; +} diff --git a/KernelLand/Modules/Filesystems/FAT/fs_fat.h b/KernelLand/Modules/Filesystems/FAT/fs_fat.h new file mode 100644 index 00000000..6896945f --- /dev/null +++ b/KernelLand/Modules/Filesystems/FAT/fs_fat.h @@ -0,0 +1,171 @@ +/* + * Acess2 + * FAT12/16/32 Driver + * vfs/fs/fs_fat.h + */ +#ifndef _FS_FAT_H_ +#define _FS_FAT_H_ + +// === On Disk Structures === +/** + * \struct fat_bootsect_s + * \brief Bootsector format + */ +struct fat_bootsect_s +{ + Uint8 jmp[3]; //!< Jump Instruction + char oemname[8]; //!< OEM Name. Typically MSDOS1.1 + Uint16 bps; //!< Bytes per Sector. Assumed to be 512 + Uint8 spc; //!< Sectors per Cluster + Uint16 resvSectCount; //!< Number of reserved sectors at beginning of volume + // +0x10 + Uint8 fatCount; //!< Number of copies of the FAT + Uint16 files_in_root; //!< Count of files in the root directory + Uint16 totalSect16; //!< Total sector count (FAT12/16) + Uint8 mediaDesc; //!< Media Desctiptor + Uint16 fatSz16; //!< FAT Size (FAT12/16) + // +0x18 + Uint16 spt; //!< Sectors per track. Ignored (Acess uses LBA) + Uint16 heads; //!< Heads. Ignored (Acess uses LBA) + Uint32 hiddenCount; //!< ??? + Uint32 totalSect32; //!< Total sector count (FAT32) + union { + struct { + Uint8 drvNum; //!< Drive Number. BIOS Drive ID (E.g. 0x80) + Uint8 resv; //!< Reserved byte + Uint8 bootSig; //!< Boot Signature. ??? + Uint32 volId; //!< Volume ID + char label[11]; //!< Disk Label + char fsType[8]; //!< FS Type. ??? + } __attribute__((packed)) fat16; //!< FAT16 Specific information + struct { + Uint32 fatSz32; //!< 32-Bit FAT Size + Uint16 extFlags; //!< Extended flags + Uint16 fsVer; //!< Filesystem Version + Uint32 rootClust; //!< Root Cluster ID + Uint16 fsInfo; //!< FS Info. ??? + Uint16 backupBS; //!< Backup Bootsector Sector Offset + char resv[12]; //!< Reserved Data + Uint8 drvNum; //!< Drive Number + char resv2; //!< Reserved Data + Uint8 bootSig; //!< Boot Signature. ??? + Uint32 volId; //!< Volume ID + char label[11]; //!< Disk Label + char fsType[8]; //!< Filesystem Type. ??? + } __attribute__((packed)) fat32; //!< FAT32 Specific Information + }__attribute__((packed)) spec; //!< Non Shared Data + char pad[512-90]; //!< Bootsector Data (Code/Boot Signature 0xAA55) +} __attribute__((packed)); + +/** + \struct fat_filetable_s + \brief Format of a 8.3 file entry on disk +*/ +struct fat_filetable_s { + char name[11]; //!< 8.3 Name + Uint8 attrib; //!< File Attributes. + Uint8 ntres; //!< Reserved for NT - Set to 0 + Uint8 ctimems; //!< 10ths of a second ranging from 0-199 (2 seconds) + Uint16 ctime; //!< Creation Time + Uint16 cdate; //!< Creation Date + Uint16 adate; //!< Accessed Date. No Time feild though + Uint16 clusterHi; //!< High Cluster. 0 for FAT12 and FAT16 + Uint16 mtime; //!< Last Modified Time + Uint16 mdate; //!< Last Modified Date + Uint16 cluster; //!< Low Word of First cluster + Uint32 size; //!< Size of file +} __attribute__((packed)); + +/** + * \struct fat_longfilename_s + * \brief Format of a long file name entry on disk + */ +struct fat_longfilename_s { + Uint8 id; //!< ID of entry. Bit 6 is set for last entry + Uint16 name1[5]; //!< 5 characters of name + Uint8 attrib; //!< Attributes. Must be ATTR_LFN + Uint8 type; //!< Type. ??? + Uint8 checksum; //!< Checksum + Uint16 name2[6]; //!< 6 characters of name + Uint16 firstCluster; //!< Used for non LFN compatability. Set to 0 + Uint16 name3[2]; //!< Last 2 characters of name +} __attribute__((packed)); + +/** + * \name File Attributes + * \brief Flag values for ::fat_filetable_s.attrib + * \{ + */ +#define ATTR_READONLY 0x01 //!< Read-only file +#define ATTR_HIDDEN 0x02 //!< Hidden File +#define ATTR_SYSTEM 0x04 //!< System File +#define ATTR_VOLUMEID 0x08 //!< Volume ID (Deprecated) +#define ATTR_DIRECTORY 0x10 //!< Directory +/** + * \brief File needs archiving + * \note User set flag, no significance to the FS driver + */ +#define ATTR_ARCHIVE 0x20 +/** + * \brief Meta Attribute + * + * If ::fat_filetable_s.attrib equals ATTR_LFN the file is a LFN entry + */ +#define ATTR_LFN (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUMEID) +/** + * \} + */ + +/** + * \brief Internal IDs for FAT types + */ +enum eFatType +{ + FAT12, //!< FAT12 Volume + FAT16, //!< FAT16 Volume + FAT32, //!< FAT32 Volume +}; + +/** + * \name End of Cluster marks + * \brief FAT values that indicate the end of a cluster chain in + * different versions. + * \{ + */ +#define EOC_FAT12 0x0FFF //!< FAT-12 Mark +#define EOC_FAT16 0xFFFF //!< FAT-16 Mark +#define EOC_FAT32 0x00FFFFFF //!< FAT-32 Mark +/** + * \} + */ + +typedef struct fat_bootsect_s fat_bootsect; +typedef struct fat_filetable_s fat_filetable; +typedef struct fat_longfilename_s fat_longfilename; + +// === Memory Structures === +/** + * \struct drv_fat_volinfo_s + * \brief Representation of a volume in memory + */ +struct drv_fat_volinfo_s +{ + int fileHandle; //!< File Handle + int type; //!< FAT Type. See eFatType + char name[12]; //!< Volume Name (With NULL Terminator) + tMutex lFAT; //!< Lock to prevent double-writing to the FAT + Uint32 firstDataSect; //!< First data sector + Uint32 rootOffset; //!< Root Offset (clusters) + Uint32 ClusterCount; //!< Total Cluster Count + fat_bootsect bootsect; //!< Boot Sector + tVFS_Node rootNode; //!< Root Node + int BytesPerCluster; + int inodeHandle; //!< Inode Cache Handle + #if CACHE_FAT + Uint32 *FATCache; //!< FAT Cache + #endif +}; + +typedef struct drv_fat_volinfo_s tFAT_VolInfo; + +#endif diff --git a/KernelLand/Modules/Filesystems/InitRD/GenerateInitRD.php b/KernelLand/Modules/Filesystems/InitRD/GenerateInitRD.php new file mode 100644 index 00000000..e7cd4f7e --- /dev/null +++ b/KernelLand/Modules/Filesystems/InitRD/GenerateInitRD.php @@ -0,0 +1,245 @@ + 4 ? $argv[4] : false); + +$gDependencies = array(); + +$lines = file($argv[1]); + +$lDepth = 0; +$lTree = array(); +$lStack = array( array("",array()) ); +foreach($lines as $line) +{ + $line = trim($line); + // Directory + if(preg_match('/^Dir\s+"([^"]+)"\s+{$/', $line, $matches)) + { + $new = array($matches[1], array()); + array_push($lStack, $new); + $lDepth ++; + continue; + } + // End of a block + if($line == "}") + { + $lDepth --; + $lStack[$lDepth][1][] = array_pop($lStack); + continue; + } + // File + if(preg_match('/^File\s+"([^"]+)"\s+"([^"]+)"$/', $line, $matches)) + { + $lStack[$lDepth][1][] = array($matches[1], $matches[2]); + continue; + } + echo "ERROR: $line\n"; + exit(0); +} + +function hd($fp) +{ + //return "0x".str_pad( dechex(ord(fgetc($fp))), 8, "0", STR_PAD_LEFT ); + $val = unpack("I", fread($fp, 4)); + //print_r($val); exit -1; + return "0x".dechex($val[1]); +} + +function hd8($fp) +{ + return "0x".str_pad( dechex(ord(fgetc($fp))), 2, "0", STR_PAD_LEFT ); +} + +$inode = 0; +$gSymFiles = array(); +function ProcessFolder($prefix, $items) +{ + global $gOutput, $gDependencies; + global $ACESSDIR, $ARCH; + global $inode; + global $gSymFiles; + foreach($items as $i=>$item) + { + $inode ++; + if(is_array($item[1])) + { + ProcessFolder("{$prefix}_{$i}", $item[1]); + + $gOutput .= "tInitRD_File {$prefix}_{$i}_entries[] = {\n"; + foreach($item[1] as $j=>$child) + { + if($j) $gOutput .= ",\n"; + $gOutput .= "\t{\"".addslashes($child[0])."\",&{$prefix}_{$i}_{$j}}"; + } + $gOutput .= "\n};\n"; + + $size = count($item[1]); + $gOutput .= <<$child) +{ + if($j) $gOutput .= ",\n"; + $gOutput .= "\t{\"".addslashes($child[0])."\",&gInitRD_Files_{$j}}"; +} +$gOutput .= "\n};\n"; +$nRootFiles = count($lStack[0][1]); +$gOutput .= <<$item) + { + $gOutput .= ",&{$prefix}_{$i}"; + if(is_array($item[1])) + { + PutNodePointers("{$prefix}_{$i}", $item[1]); + } + } +} + +PutNodePointers("gInitRD_Files", $lStack[0][1]); + +$gOutput .= <<$file) +{ + fputs($fp, "$file\n"); +// fputs($fp, "--defsym $sym=_binary_".$sym_filename."_start\n"); +} +fclose($fp); + +if($gDepFile !== false) +{ + $fp = fopen($gDepFile, "w"); + $line = $gOutputFile.":\t".implode(" ", $gDependencies); + fputs($fp, $line); + fclose($fp); +} + +?> diff --git a/KernelLand/Modules/Filesystems/InitRD/Makefile b/KernelLand/Modules/Filesystems/InitRD/Makefile new file mode 100644 index 00000000..5aa0e653 --- /dev/null +++ b/KernelLand/Modules/Filesystems/InitRD/Makefile @@ -0,0 +1,16 @@ +# InitRD Filesystem Driver +# + +OBJ = main.o files.$(ARCH).o +EXTRA = files.c +NAME = InitRD +EXTRA = files.$(ARCH).c files.$(ARCH).c.dep files.$(ARCH).c.ldopts +LDFLAGS += @files.$(ARCH).c.ldopts + +-include ../Makefile.tpl + + +files.$(ARCH).c: GenerateInitRD.php files.lst + ARCH=$(ARCH) ACESSDIR=$(ACESSDIR) php GenerateInitRD.php files.lst $@ $@.ldopts $@.dep + +-include files.$(ARCH).c.dep diff --git a/KernelLand/Modules/Filesystems/InitRD/files.lst b/KernelLand/Modules/Filesystems/InitRD/files.lst new file mode 100644 index 00000000..16d647f2 --- /dev/null +++ b/KernelLand/Modules/Filesystems/InitRD/files.lst @@ -0,0 +1,36 @@ +Dir "SBin" { + File "init" "__BIN__/SBin/init" + File "login" "__BIN__/SBin/login" +} +Dir "Bin" { + File "CLIShell" "__BIN__/Bin/CLIShell" + File "ls" "__BIN__/Bin/ls" + File "cat" "__BIN__/Bin/cat" + File "mount" "__BIN__/Bin/mount" + File "ifconfig" "__BIN__/Bin/ifconfig" + File "telnet" "__BIN__/Bin/telnet" + File "irc" "__BIN__/Bin/irc" +} +Dir "Libs" { + File "ld-acess.so" "__BIN__/Libs/ld-acess.so" + File "libld-acess.so" "__BIN__/Libs/libld-acess.so" + File "libc.so" "__BIN__/Libs/libc.so" + File "libgcc.so" "__BIN__/Libs/libgcc.so" + File "libreadline.so" "__BIN__/Libs/libreadline.so" + File "libnet.so" "__BIN__/Libs/libnet.so" + File "liburi.so" "__BIN__/Libs/liburi.so" + File "libimage_sif.so" "__BIN__/Libs/libimage_sif.so" + File "libaxwin3.so" "__BIN__/Libs/libaxwin3.so" +} +Dir "Conf" { + File "BootConf.cfg" "__FS__/Conf/BootConf.cfg" +} +Dir "Apps" { + Dir "AxWin" { + Dir "3.0" { + File "AxWinWM" "__BIN__/Apps/AxWin/3.0/AxWinWM" + File "AxWinUI" "__BIN__/Apps/AxWin/3.0/AxWinUI" + File "AcessLogoSmall.sif" "__SRC__/Usermode/Applications/axwin3_src/AcessLogoSmall.sif" + } + } +} diff --git a/KernelLand/Modules/Filesystems/InitRD/initrd.h b/KernelLand/Modules/Filesystems/InitRD/initrd.h new file mode 100644 index 00000000..479a8418 --- /dev/null +++ b/KernelLand/Modules/Filesystems/InitRD/initrd.h @@ -0,0 +1,25 @@ +/* + */ +#ifndef _INITRD_H_ +#define _INITRD_H_ + +#include +#include + +typedef struct sInitRD_File +{ + char *Name; + tVFS_Node *Node; +} tInitRD_File; + + +// === Functions === +extern Uint64 InitRD_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Size, void *Buffer); +extern char *InitRD_ReadDir(tVFS_Node *Node, int ID); +extern tVFS_Node *InitRD_FindDir(tVFS_Node *Node, const char *Name); + +// === Globals === +tVFS_NodeType gInitRD_DirType; +tVFS_NodeType gInitRD_FileType; + +#endif diff --git a/KernelLand/Modules/Filesystems/InitRD/main.c b/KernelLand/Modules/Filesystems/InitRD/main.c new file mode 100644 index 00000000..b52ab8de --- /dev/null +++ b/KernelLand/Modules/Filesystems/InitRD/main.c @@ -0,0 +1,138 @@ +/* + * Acess OS + * InitRD Driver Version 1 + */ +#include "initrd.h" +#include + +#define DUMP_ON_MOUNT 1 + +// === IMPORTS == +extern tVFS_Node gInitRD_RootNode; +extern const int giInitRD_NumFiles; +extern tVFS_Node * const gInitRD_FileList[]; + +// === PROTOTYPES === + int InitRD_Install(char **Arguments); +tVFS_Node *InitRD_InitDevice(const char *Device, const char **Arguments); +void InitRD_Unmount(tVFS_Node *Node); +tVFS_Node *InitRD_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode); +Uint64 InitRD_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Size, void *Buffer); +char *InitRD_ReadDir(tVFS_Node *Node, int ID); +tVFS_Node *InitRD_FindDir(tVFS_Node *Node, const char *Name); +void InitRD_DumpDir(tVFS_Node *Node, int Indent); + +// === GLOBALS === +MODULE_DEFINE(0, 0x0A, FS_InitRD, InitRD_Install, NULL); +tVFS_Driver gInitRD_FSInfo = { + "initrd", 0, InitRD_InitDevice, InitRD_Unmount, InitRD_GetNodeFromINode + }; +tVFS_NodeType gInitRD_DirType = { + .ReadDir = InitRD_ReadFile, + .FindDir = InitRD_FindDir + }; +tVFS_NodeType gInitRD_FileType = { + .Read = InitRD_ReadFile + }; + +/** + * \brief Register initrd with the kernel + */ +int InitRD_Install(char **Arguments) +{ + Log_Notice("InitRD", "Installed"); + VFS_AddDriver( &gInitRD_FSInfo ); + + return MODULE_ERR_OK; +} + +/** + * \brief Mount the InitRD + */ +tVFS_Node *InitRD_InitDevice(const char *Device, const char **Arguments) +{ + #if DUMP_ON_MOUNT + InitRD_DumpDir( &gInitRD_RootNode, 0 ); + #endif + Log_Notice("InitRD", "Mounted (%i files)", giInitRD_NumFiles); + return &gInitRD_RootNode; +} + +/** + * \brief Unmount the InitRD + */ +void InitRD_Unmount(tVFS_Node *Node) +{ +} + +/** + */ +tVFS_Node *InitRD_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode) +{ + if( Inode >= giInitRD_NumFiles ) return NULL; + return gInitRD_FileList[Inode]; +} + +/** + * \brief Read from a file + */ +Uint64 InitRD_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + if(Offset > Node->Size) + return 0; + if(Offset + Length > Node->Size) + Length = Node->Size - Offset; + + memcpy(Buffer, Node->ImplPtr+Offset, Length); + + return Length; +} + +/** + * \brief Read from a directory + */ +char *InitRD_ReadDir(tVFS_Node *Node, int ID) +{ + tInitRD_File *dir = Node->ImplPtr; + + if(ID >= Node->Size) + return NULL; + + return strdup(dir[ID].Name); +} + +/** + * \brief Find an element in a directory + */ +tVFS_Node *InitRD_FindDir(tVFS_Node *Node, const char *Name) +{ + int i; + tInitRD_File *dir = Node->ImplPtr; + + LOG("Name = '%s'", Name); + + for( i = 0; i < Node->Size; i++ ) + { + if(strcmp(Name, dir[i].Name) == 0) + return dir[i].Node; + } + + return NULL; +} + +void InitRD_DumpDir(tVFS_Node *Node, int Indent) +{ + int i; + char indent[Indent+1]; + tInitRD_File *dir = Node->ImplPtr; + + for( i = 0; i < Indent; i++ ) indent[i] = ' '; + indent[i] = '\0'; + + for( i = 0; i < Node->Size; i++ ) + { + Log_Debug("InitRD", "%s- %p %s", indent, dir[i].Node, dir[i].Name); + if(dir[i].Node->Flags & VFS_FFLAG_DIRECTORY) + InitRD_DumpDir(dir[i].Node, Indent+1); + } +} diff --git a/KernelLand/Modules/Filesystems/LEAN/common.h b/KernelLand/Modules/Filesystems/LEAN/common.h new file mode 100644 index 00000000..f75b8b6a --- /dev/null +++ b/KernelLand/Modules/Filesystems/LEAN/common.h @@ -0,0 +1,101 @@ +/* + * Acess 2 LEAN Filesystem Driver + * By John Hodge (thePowersGang) + * + * lean.h - Filesystem Structure Definitions + */ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#define EXTENTS_PER_INODE 6 +#define EXTENTS_PER_INDIRECT 38 + +struct sLEAN_Superblock +{ + // TODO +}; + +struct sLEAN_IndirectBlock +{ + Uint32 Checksum; // Block checksum + Uint32 Magic; // 'INDX' + Uint64 SectorCount; // Number of blocks referenced by this block + Uint64 Inode; // Inode this block belongs to (in for robustness) + Uint64 ThisSector; // Sector number of this block + Uint64 PrevIndirect; // Backlink to the previous indirect block (or zero) + Uint64 NextIndirect; // Link to the next indirect block (or zero) + + /** + * Number of extents stored in this block (only the last block can + * have this as < \a EXTENTS_PER_INDIRECT + */ + Uint8 NumExtents; + Uint8 reserved1[3]; //!< Reserved/Padding + Uint32 reserved2; //!< Reserved/Padding + + Uint64 ExtentsStarts[EXTENTS_PER_INDIRECT]; + Uint32 ExtentsSizes[EXTENTS_PER_INDIRECT]; +} + +struct sLEAN_Inode +{ + Uint32 Checksum; //!< Checksum of the inode + Uint32 Magic; //!< Magic Value 'NODE' + + Uint8 ExtentCount; //!< Number of extents defined in the inode structure + Uint8 reserved1[3]; //!< Reserved/Padding + + Uint32 IndirectCount; //!< Number of indirect extent blocks used by the file + Uint32 LinkCount; //!< Number of directory entires that refer to this inode + + Uint32 UID; //!< Owning User + Uint32 GID; //!< Owning Group + Uint32 Attributes; //!< + Uint64 FileSize; //!< Size of the file data (not including inode and other bookkeeping) + Uint64 SectorCount; //!< Number of sectors allocated to the file + Sint64 LastAccessTime; //!< Last access time + Sint64 StatusChangeTime; //!< Last change of the status bits time + Sint64 ModifcationTime; //!< Last modifcation time + Sint64 CreationTime; //!< File creation time + + Uint64 FirstIndirect; //!< Sector number of the first indirect block + Uint64 LastIndirect; //!< Sector number of the last indirect block + + Uint64 Fork; //!< ???? + + Uint64 ExtentsStarts[EXTENTS_PER_INODE]; + Uint32 ExtentsSizes[EXTENTS_PER_INODE]; +}; + +enum eLEAN_InodeAttributes +{ + LEAN_iaXOth = 1 << 0, LEAN_iaWOth = 1 << 1, LEAN_iaROth = 1 << 2, + LEAN_iaXGrp = 1 << 3, LEAN_iaWGrp = 1 << 4, LEAN_iaRGrp = 1 << 5, + LEAN_iaXUsr = 1 << 6, LEAN_iaWUsr = 1 << 7, LEAN_iaRUsr = 1 << 8, + + LEAN_iaSVTX = 1 << 9, + LEAN_iaSGID = 1 << 10, + LEAN_iaSUID = 1 << 11, + + LEAN_iaHidden = 1 << 12, + LEAN_iaSystem = 1 << 13, + LEAN_iaArchive = 1 << 14, // Set on any write + LEAN_iaSync = 1 << 15, + LEAN_iaNoAccessTime = 1 << 16, //!< Don't update the last accessed time + LEAN_iaImmutable = 1 << 17, //!< Don't move sectors (defragger flag) + LEAN_iaPrealloc = 1 << 18, //!< Keep preallocated sectors after close + LEAN_iaInlineExtAttr = 1 << 19, //!< Reserve the first sector + + LEAN_iaFmtMask = 7 << 29, //!< Format mask + LEAN_iaFmtRegular = 1 << 29, //!< Regular File + LEAN_iaFmtDirectory = 2 << 29, //!< Directory + LEAN_iaFmtSymlink = 3 << 29, //!< Symlink + LEAN_iaFmtFork = 4 << 29, //!< Fork +}; + +struct tLEAN_ExtendedAttribute +{ + Uint32 Header; // 8.24 Name.Value Sizes +}; + +#endif diff --git a/KernelLand/Modules/Filesystems/LEAN/main.c b/KernelLand/Modules/Filesystems/LEAN/main.c new file mode 100644 index 00000000..400d4f81 --- /dev/null +++ b/KernelLand/Modules/Filesystems/LEAN/main.c @@ -0,0 +1,18 @@ +/* + * Acess 2 LEAN Filesystem Driver + * By John Hodge (thePowersGang) + */ +#include +#include + +// === CONSTANTS === + +// === PROTOTYPES === + +// === GLOBALS === + +// === CODE === +int LEAN_Install(char **Arguments) +{ + return 1; +} diff --git a/KernelLand/Modules/Filesystems/Makefile.tpl b/KernelLand/Modules/Filesystems/Makefile.tpl new file mode 100644 index 00000000..77dfc2c9 --- /dev/null +++ b/KernelLand/Modules/Filesystems/Makefile.tpl @@ -0,0 +1,2 @@ +CATEGORY = FS +-include ../../Makefile.tpl diff --git a/KernelLand/Modules/Filesystems/NFS/Makefile b/KernelLand/Modules/Filesystems/NFS/Makefile new file mode 100644 index 00000000..0b2019f0 --- /dev/null +++ b/KernelLand/Modules/Filesystems/NFS/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = main.o +NAME = NFS + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Filesystems/NFS/common.h b/KernelLand/Modules/Filesystems/NFS/common.h new file mode 100644 index 00000000..d73592fe --- /dev/null +++ b/KernelLand/Modules/Filesystems/NFS/common.h @@ -0,0 +1,20 @@ +/* + * Acess2 - NFS Driver + * By John Hodge (thePowersGang) + * This file is published under the terms of the Acess licence. See the + * file COPYING for details. + * + * common.h - Common definitions + */ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +typedef struct sNFS_Connection +{ + int FD; + tIPAddr Host; + char *Base; + tVFS_Node Node; +} tNFS_Connection; + +#endif diff --git a/KernelLand/Modules/Filesystems/NFS/main.c b/KernelLand/Modules/Filesystems/NFS/main.c new file mode 100644 index 00000000..459945fa --- /dev/null +++ b/KernelLand/Modules/Filesystems/NFS/main.c @@ -0,0 +1,74 @@ +/* + * Acess2 - NFS Driver + * By John Hodge (thePowersGang) + * This file is published under the terms of the Acess licence. See the + * file COPYING for details. + * + * main.c - Driver core + */ +#define DEBUG 1 +#define VERBOSE 0 +#include "common.h" +#include + +// === PROTOTYPES === + int NFS_Install(char **Arguments); +tVFS_Node *NFS_InitDevice(char *Devices, char **Options); +void NFS_Unmount(tVFS_Node *Node); + +// === GLOBALS === +MODULE_DEFINE(0, 0x32 /*v0.5*/, FS_NFS, NFS_Install, NULL); +tVFS_Driver gNFS_FSInfo = {"nfs", 0, NFS_InitDevice, NFS_Unmount, NULL}; + +tNFS_Connection *gpNFS_Connections; + +// === CODE === +/** + * \brief Installs the NFS driver + */ +int NFS_Install(char **Arguments) +{ + VFS_AddDriver( &gNFS_FSInfo ); + return 1; +} + +/** + * \brief Mount a NFS share + */ +tVFS_Node *NFS_InitDevice(char *Device, char **Options) +{ + char *path, *host; + tNFS_Connection *conn; + + path = strchr( Device, ':' ) + 1; + host = strndup( Device, (int)(path-Device)-1 ); + + conn = malloc( sizeof(tNFS_Connection) ); + + if( !IPTools_GetAddress(host, &conn->IP) ) { + free(conn); + return NULL; + } + free(host); + + conn->FD = IPTools_OpenUdpClient( &conn->Host ); + if(conn->FD == -1) { + free(conn); + return NULL; + } + + conn->Base = strdup( path ); + conn->RootNode.ImplPtr = conn; + conn->RootNode.Flags = VFS_FFLAG_DIRECTORY; + + conn->RootNode.ReadDir = NFS_ReadDir; + conn->RootNode.FindDir = NFS_FindDir; + conn->RootNode.Close = NULL; + + return &conn->RootNode; +} + +void NFS_Unmount(tVFS_Node *Node) +{ + +} diff --git a/KernelLand/Modules/Filesystems/NTFS/Makefile b/KernelLand/Modules/Filesystems/NTFS/Makefile new file mode 100644 index 00000000..22c16773 --- /dev/null +++ b/KernelLand/Modules/Filesystems/NTFS/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = main.o dir.o +NAME = NTFS + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Filesystems/NTFS/attributes.h b/KernelLand/Modules/Filesystems/NTFS/attributes.h new file mode 100644 index 00000000..671a36e7 --- /dev/null +++ b/KernelLand/Modules/Filesystems/NTFS/attributes.h @@ -0,0 +1,38 @@ +/* + * Acess2 - NTFS Driver + * By John Hodge (thePowersGang) + * This file is published under the terms of the Acess licence. See the + * file COPYING for details. + * + * attributes.h - MFT Attribute Types + */ +#ifndef _ATTRIBUTES_H_ +#define _ATTRIBUTES_H_ + +typedef struct +{ + Uint64 ParentDirectory; //!< Parent directory MFT entry + Sint64 CreationTime; //!< Time the file was created + Sint64 LastDataModTime; //!< Last change time for the data + Sint64 LastMtfModTime; //!< Last change time for the MFT entry + Sint64 LastAccessTime; //!< Last Access Time (unreliable on most systems) + + Uint64 AllocatedSize; //!< Allocated data size for $DATA unnamed stream + Uint64 DataSize; //!< Actual size of $DATA unnamed stream + Uint32 Flags; //!< File attribute files + + union { + struct { + Uint16 PackedSize; //!< Size of buffer needed for extended attributes + Uint16 _reserved; + } PACKED ExtAttrib; + struct { + Uint32 Tag; //!< Type of reparse point + } PACKED ReparsePoint; + } PACKED Type; + + Uint8 FilenameType; //!< Filename namespace (DOS, Windows, Unix) + WCHAR Filename[0]; +} PACKED tNTFS_Attrib_Filename; + +#endif diff --git a/KernelLand/Modules/Filesystems/NTFS/common.h b/KernelLand/Modules/Filesystems/NTFS/common.h new file mode 100644 index 00000000..598120d2 --- /dev/null +++ b/KernelLand/Modules/Filesystems/NTFS/common.h @@ -0,0 +1,153 @@ +/* + * Acess2 - NTFS Driver + * By John Hodge (thePowersGang) + * This file is published under the terms of the Acess licence. See the + * file COPYING for details. + * + * common.h - Common Types and Definitions + */ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#include +#include + +typedef Uint16 WCHAR; + +// === STRUCTURES === +/** + * In-memory representation of an NTFS Disk + */ +typedef struct sNTFS_Disk +{ + int FD; + int CacheHandle; + + int ClusterSize; + Uint64 MFTBase; + Uint32 MFTRecSize; + + tVFS_Node RootNode; +} tNTFS_Disk; + +typedef struct sNTFS_BootSector +{ + // 0 + Uint8 Jump[3]; + Uint8 SystemID[8]; // = "NTFS " + Uint16 BytesPerSector; + Uint8 SectorsPerCluster; + + // 0xE + Uint8 Unused[7]; + Uint8 MediaDescriptor; + Uint16 Unused2; + Uint16 SectorsPerTrack; + Uint16 Heads; + + // 0x1C + Uint64 Unused3; + Uint32 Unkown; // Usually 0x00800080 (according to Linux docs) + + // 0x28 + Uint64 TotalSectorCount; // Size of volume in sectors + Uint64 MFTStart; // Logical Cluster Number of Cluster 0 of MFT + Uint64 MFTMirrorStart; // Logical Cluster Number of Cluster 0 of MFT Backup + + // 0x40 + // If either of these are -ve, the size can be obtained via + // SizeInBytes = 2^(-1 * Value) + Sint8 ClustersPerMFTRecord; + Uint8 Unused4[3]; + Sint8 ClustersPerIndexRecord; + Uint8 Unused5[3]; + + Uint64 SerialNumber; + + Uint8 Padding[512-0x50]; + +} PACKED tNTFS_BootSector; + +/** + * FILE header, an entry in the MFT + */ +typedef struct sNTFS_FILE_Header +{ + Uint32 Magic; // 'FILE' + Uint16 UpdateSequenceOfs; + Uint16 UpdateSequenceSize; // Size in words of the UpdateSequenceArray + + Uint64 LSN; // $LogFile Sequence Number + + Uint16 SequenceNumber; + Uint16 HardLinkCount; + Uint16 FirstAttribOfs; // Size of header? + Uint16 Flags; // 0: In Use, 1: Directory + + Uint32 RecordSize; // Real Size of FILE Record + Uint32 RecordSpace; // Allocated Size for FILE Record + + /** + * Base address of the MFT containing this record + */ + Uint64 Reference; // "File reference to the base FILE record" ??? + + Uint16 NextAttribID; + union + { + // Only in XP + struct { + Uint16 AlignTo4Byte; + Uint16 RecordNumber; // Number of this MFT Record + Uint16 UpdateSequenceNumber; + Uint16 UpdateSequenceArray[]; + } XP; + struct { + Uint16 UpdateSequenceNumber; + Uint16 UpdateSequenceArray[]; + } All; + } OSDep; + +} PACKED tNTFS_FILE_Header; + +/** + * File Attribute, follows the FILE header + */ +typedef struct sNTFS_FILE_Attrib +{ + Uint32 Type; // See eNTFS_FILE_Attribs + Uint32 Size; // Includes header + + Uint8 ResidentFlag; // (What does this mean?) + Uint8 NameLength; + Uint16 NameOffset; + Uint16 Flags; // 0: Compressed, 14: Encrypted, 15: Sparse + Uint16 AttributeID; + + union + { + struct { + Uint32 AttribLen; // In words + Uint16 AttribOfs; + Uint8 IndexedFlag; + Uint8 Padding; + + Uint16 Name[]; // UTF-16 + // Attribute Data + } Resident; + struct { + Uint64 StartingVCN; + Uint64 LastVCN; + Uint16 DataRunOfs; + Uint16 CompressionUnitSize; + Uint32 Padding; + Uint64 AllocatedSize; + Uint64 RealSize; + Uint64 InitiatedSize; // One assumes, ammount of actual data stored + Uint16 Name[]; // UTF-16 + // Data Runs + } NonResident; + }; +} PACKED tNTFS_FILE_Attrib; + +#endif diff --git a/KernelLand/Modules/Filesystems/NTFS/dir.c b/KernelLand/Modules/Filesystems/NTFS/dir.c new file mode 100644 index 00000000..37661e82 --- /dev/null +++ b/KernelLand/Modules/Filesystems/NTFS/dir.c @@ -0,0 +1,48 @@ +/* + * Acess2 - NTFS Driver + * By John Hodge (thePowersGang) + * This file is published under the terms of the Acess licence. See the + * file COPYING for details. + * + * dir.c - Directory Handling + */ +#include "common.h" +#include "index.h" + +// === PROTOTYPES === +char *NTFS_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name); +Uint64 NTFS_int_IndexLookup(Uint64 Inode, const char *IndexName, const char *Str); + +// === CODE === +/** + * \brief Get the name of an indexed directory entry + */ +char *NTFS_ReadDir(tVFS_Node *Node, int Pos) +{ + return NULL; +} + +/** + * \brief Get an entry from a directory by name + */ +tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name) +{ + tNTFS_Disk *disk = Node->ImplPtr; + Uint64 inode = NTFS_int_IndexLookup(Node->Inode, "$I30", Name); + tVFS_Node node; + + if(!inode) return NULL; + + node.Inode = inode; + + return Inode_CacheNode(disk->CacheHandle, &node); +} + +/** + * \brief Scans an index for the requested value and returns the associated ID + */ +Uint64 NTFS_int_IndexLookup(Uint64 Inode, const char *IndexName, const char *Str) +{ + return 0; +} diff --git a/KernelLand/Modules/Filesystems/NTFS/index.h b/KernelLand/Modules/Filesystems/NTFS/index.h new file mode 100644 index 00000000..f45d8975 --- /dev/null +++ b/KernelLand/Modules/Filesystems/NTFS/index.h @@ -0,0 +1,65 @@ +/* + * Acess2 - NTFS Driver + * By John Hodge (thePowersGang) + * This file is published under the terms of the Acess licence. See the + * file COPYING for details. + * + * index.h - Index Types + */ +#ifndef _INDEX_H_ +#define _INDEX_H_ + +#include "attributes.h" + +typedef struct +{ + Uint32 EntryOffset; + Uint32 IndexLength; + Uint32 AllocateSize; + Uint8 Flags; + Uint8 _reserved[3]; +} PACKED tNTFS_IndexHeader; + +typedef struct +{ + Uint32 Type; + Uint32 CollationRule; + Uint32 IndexBlockSize; + Uint8 ClustersPerIndexBlock; + Uint8 _reserved[3]; + tNTFS_IndexHeader Header; +} PACKED tNTFS_IndexRoot; + +typedef struct +{ + union { + struct { + Uint64 File; // MFT Index of file + } PACKED Dir; + /** + * Views/Indexes + */ + struct { + Uint16 DataOffset; + Uint16 DataLength; + Uint32 _reserved; + } PACKED ViewIndex; + } PACKED Header; + + Uint16 Length; //!< Size of the index entry (multiple of 8 bytes) + Uint16 KeyLength; //!< Size of key value + Uint16 Flags; //!< Flags Bitfield + Uint16 _reserved; + + /** + * \brief Key Data + * \note Only valid if \a Flags does not have \a INDEX_ENTRY_END set + * \note In NTFS3 only \a Filename is used + */ + union { + tNTFS_Attrib_Filename Filename; + //TODO: more key types + } PACKED Key; +} PACKED tNTFS_IndexEntry; + +#endif diff --git a/KernelLand/Modules/Filesystems/NTFS/main.c b/KernelLand/Modules/Filesystems/NTFS/main.c new file mode 100644 index 00000000..dbda06e1 --- /dev/null +++ b/KernelLand/Modules/Filesystems/NTFS/main.c @@ -0,0 +1,196 @@ +/* + * Acess2 - NTFS Driver + * By John Hodge (thePowersGang) + * + * main.c - Driver core + */ +#define DEBUG 1 +#define VERBOSE 0 +#include +#include +#include "common.h" +#include + +// === IMPORTS === +extern char *NTFS_ReadDir(tVFS_Node *Node, int Pos); +extern tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name); + +// === PROTOTYPES === + int NTFS_Install(char **Arguments); +tVFS_Node *NTFS_InitDevice(const char *Devices, const char **Options); +void NTFS_Unmount(tVFS_Node *Node); +void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry); + +// === GLOBALS === +MODULE_DEFINE(0, 0x0A /*v0.1*/, FS_NTFS, NTFS_Install, NULL); +tVFS_Driver gNTFS_FSInfo = {"ntfs", 0, NTFS_InitDevice, NTFS_Unmount, NULL}; +tVFS_NodeType gNTFS_DirType = { + .TypeName = "NTFS-File", + .ReadDir = NTFS_ReadDir, + .FindDir = NTFS_FindDir, + .Close = NULL + }; + +tNTFS_Disk gNTFS_Disks; + +// === CODE === +/** + * \brief Installs the NTFS driver + */ +int NTFS_Install(char **Arguments) +{ + VFS_AddDriver( &gNTFS_FSInfo ); + return 0; +} + +/** + * \brief Mount a NTFS volume + */ +tVFS_Node *NTFS_InitDevice(const char *Device, const char **Options) +{ + tNTFS_Disk *disk; + tNTFS_BootSector bs; + + disk = malloc( sizeof(tNTFS_Disk) ); + + disk->FD = VFS_Open(Device, VFS_OPENFLAG_READ); + if(!disk->FD) { + free(disk); + return NULL; + } + + Log_Debug("FS_NTFS", "&bs = %p", &bs); + VFS_ReadAt(disk->FD, 0, 512, &bs); + + Log_Debug("FS_NTFS", "Jump = %02x%02x%02x", + bs.Jump[0], + bs.Jump[1], + bs.Jump[2]); + Log_Debug("FS_NTFS", "SystemID = %02x%02x%02x%02x%02x%02x%02x%02x (%8C)", + bs.SystemID[0], bs.SystemID[1], bs.SystemID[2], bs.SystemID[3], + bs.SystemID[4], bs.SystemID[5], bs.SystemID[6], bs.SystemID[7], + bs.SystemID + ); + Log_Debug("FS_NTFS", "BytesPerSector = %i", bs.BytesPerSector); + Log_Debug("FS_NTFS", "SectorsPerCluster = %i", bs.SectorsPerCluster); + Log_Debug("FS_NTFS", "MediaDescriptor = 0x%x", bs.MediaDescriptor); + Log_Debug("FS_NTFS", "SectorsPerTrack = %i", bs.SectorsPerTrack); + Log_Debug("FS_NTFS", "Heads = %i", bs.Heads); + Log_Debug("FS_NTFS", "TotalSectorCount = 0x%llx", bs.TotalSectorCount); + Log_Debug("FS_NTFS", "MFTStart = 0x%llx", bs.MFTStart); + Log_Debug("FS_NTFS", "MFTMirrorStart = 0x%llx", bs.MFTMirrorStart); + Log_Debug("FS_NTFS", "ClustersPerMFTRecord = %i", bs.ClustersPerMFTRecord); + Log_Debug("FS_NTFS", "ClustersPerIndexRecord = %i", bs.ClustersPerIndexRecord); + Log_Debug("FS_NTFS", "SerialNumber = 0x%llx", bs.SerialNumber); + + disk->ClusterSize = bs.BytesPerSector * bs.SectorsPerCluster; + Log_Debug("NTFS", "Cluster Size = %i KiB", disk->ClusterSize/1024); + disk->MFTBase = bs.MFTStart; + Log_Debug("NTFS", "MFT Base = %i", disk->MFTBase); + Log_Debug("NTFS", "TotalSectorCount = 0x%x", bs.TotalSectorCount); + + if( bs.ClustersPerMFTRecord < 0 ) { + disk->MFTRecSize = 1 << (-bs.ClustersPerMFTRecord); + } + else { + disk->MFTRecSize = bs.ClustersPerMFTRecord * disk->ClusterSize; + } + + disk->RootNode.Inode = 5; // MFT Ent #5 is filesystem root + disk->RootNode.ImplPtr = disk; + + disk->RootNode.UID = 0; + disk->RootNode.GID = 0; + + disk->RootNode.NumACLs = 1; + disk->RootNode.ACLs = &gVFS_ACL_EveryoneRX; + + disk->RootNode.Type = &gNTFS_DirType; + + + NTFS_DumpEntry(disk, 5); + + return &disk->RootNode; +} + +/** + * \brief Unmount an NTFS Disk + */ +void NTFS_Unmount(tVFS_Node *Node) +{ + +} + +/** + * \brief Dumps a MFT Entry + */ +void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry) +{ + void *buf = malloc( Disk->MFTRecSize ); + tNTFS_FILE_Header *hdr = buf; + tNTFS_FILE_Attrib *attr; + int i; + + if(!buf) { + Log_Warning("FS_NTFS", "malloc() fail!"); + return ; + } + + VFS_ReadAt( Disk->FD, + Disk->MFTBase * Disk->ClusterSize + Entry * Disk->MFTRecSize, + Disk->MFTRecSize, + buf); + + Log_Debug("FS_NTFS", "MFT Entry #%i", Entry); + Log_Debug("FS_NTFS", "- Magic = 0x%08x (%4C)", hdr->Magic, &hdr->Magic); + Log_Debug("FS_NTFS", "- UpdateSequenceOfs = 0x%x", hdr->UpdateSequenceOfs); + Log_Debug("FS_NTFS", "- UpdateSequenceSize = 0x%x", hdr->UpdateSequenceSize); + Log_Debug("FS_NTFS", "- LSN = 0x%x", hdr->LSN); + Log_Debug("FS_NTFS", "- SequenceNumber = %i", hdr->SequenceNumber); + Log_Debug("FS_NTFS", "- HardLinkCount = %i", hdr->HardLinkCount); + Log_Debug("FS_NTFS", "- FirstAttribOfs = 0x%x", hdr->FirstAttribOfs); + Log_Debug("FS_NTFS", "- Flags = 0x%x", hdr->Flags); + Log_Debug("FS_NTFS", "- RecordSize = 0x%x", hdr->RecordSize); + Log_Debug("FS_NTFS", "- RecordSpace = 0x%x", hdr->RecordSpace); + Log_Debug("FS_NTFS", "- Reference = 0x%llx", hdr->Reference); + Log_Debug("FS_NTFS", "- NextAttribID = 0x%04x", hdr->NextAttribID); + + attr = (void*)( (char*)hdr + hdr->FirstAttribOfs ); + i = 0; + while( (tVAddr)attr < (tVAddr)hdr + hdr->RecordSize ) + { + if(attr->Type == 0xFFFFFFFF) break; + Log_Debug("FS_NTFS", "- Attribute %i", i ++); + Log_Debug("FS_NTFS", " > Type = 0x%x", attr->Type); + Log_Debug("FS_NTFS", " > Size = 0x%x", attr->Size); + Log_Debug("FS_NTFS", " > ResidentFlag = 0x%x", attr->ResidentFlag); + Log_Debug("FS_NTFS", " > NameLength = %i", attr->NameLength); + Log_Debug("FS_NTFS", " > NameOffset = 0x%x", attr->NameOffset); + Log_Debug("FS_NTFS", " > Flags = 0x%x", attr->Flags); + Log_Debug("FS_NTFS", " > AttributeID = 0x%x", attr->AttributeID); + if( !attr->ResidentFlag ) { + Log_Debug("FS_NTFS", " > AttribLen = 0x%x", attr->Resident.AttribLen); + Log_Debug("FS_NTFS", " > AttribOfs = 0x%x", attr->Resident.AttribOfs); + Log_Debug("FS_NTFS", " > IndexedFlag = 0x%x", attr->Resident.IndexedFlag); + Log_Debug("FS_NTFS", " > Name = '%*C'", attr->NameLength, attr->Resident.Name); + Debug_HexDump("FS_NTFS", + (void*)( (tVAddr)attr + attr->Resident.AttribOfs ), + attr->Resident.AttribLen + ); + } + else { + Log_Debug("FS_NTFS", " > StartingVCN = 0x%llx", attr->NonResident.StartingVCN); + Log_Debug("FS_NTFS", " > LastVCN = 0x%llx", attr->NonResident.LastVCN); + Log_Debug("FS_NTFS", " > DataRunOfs = 0x%x", attr->NonResident.DataRunOfs); + Log_Debug("FS_NTFS", " > CompressionUnitSize = 0x%x", attr->NonResident.CompressionUnitSize); + Log_Debug("FS_NTFS", " > AllocatedSize = 0x%llx", attr->NonResident.AllocatedSize); + Log_Debug("FS_NTFS", " > RealSize = 0x%llx", attr->NonResident.RealSize); + Log_Debug("FS_NTFS", " > InitiatedSize = 0x%llx", attr->NonResident.InitiatedSize); + Log_Debug("FS_NTFS", " > Name = '%*C'", attr->NameLength, attr->NonResident.Name); + } + + attr = (void*)( (tVAddr)attr + attr->Size ); + } + + free(buf); +} diff --git a/KernelLand/Modules/IPStack/Makefile b/KernelLand/Modules/IPStack/Makefile new file mode 100644 index 00000000..0838a343 --- /dev/null +++ b/KernelLand/Modules/IPStack/Makefile @@ -0,0 +1,13 @@ +# +# + +OBJ := main.o interface.o +OBJ += link.o arp.o +OBJ += ipv4.o icmp.o +OBJ += ipv6.o +OBJ += firewall.o routing.o +OBJ += udp.o tcp.o +NAME := IPStack +CATEGORY := + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/IPStack/arp.c b/KernelLand/Modules/IPStack/arp.c new file mode 100644 index 00000000..abaea275 --- /dev/null +++ b/KernelLand/Modules/IPStack/arp.c @@ -0,0 +1,398 @@ +/* + * Acess2 IP Stack + * - Address Resolution Protocol + * - Part of the IPv4 protocol + */ +#define DEBUG 0 +#include "ipstack.h" +#include "arp.h" +#include "link.h" + +#define ARPv6 0 +#define ARP_CACHE_SIZE 64 +#define ARP_MAX_AGE (60*60*1000) // 1Hr + +// === IMPORTS === +extern tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast); +#if ARPv6 +extern tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast); +#endif + +// === PROTOTYPES === + int ARP_Initialise(); +tMacAddr ARP_Resolve4(tInterface *Interface, tIPv4 Address); +void ARP_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer); + +// === GLOBALS === +struct sARP_Cache4 { + tIPv4 IP; + tMacAddr MAC; + Sint64 LastUpdate; + Sint64 LastUsed; +} *gaARP_Cache4; + int giARP_Cache4Space; +tMutex glARP_Cache4; +#if ARPv6 +struct sARP_Cache6 { + tIPv6 IP; + tMacAddr MAC; + Sint64 LastUpdate; + Sint64 LastUsed; +} *gaARP_Cache6; + int giARP_Cache6Space; +tMutex glARP_Cache6; +#endif +volatile int giARP_LastUpdateID = 0; + +// === CODE === +/** + * \fn int ARP_Initialise() + * \brief Initalise the ARP section + */ +int ARP_Initialise() +{ + gaARP_Cache4 = malloc( ARP_CACHE_SIZE * sizeof(struct sARP_Cache4) ); + memset( gaARP_Cache4, 0, ARP_CACHE_SIZE * sizeof(struct sARP_Cache4) ); + giARP_Cache4Space = ARP_CACHE_SIZE; + + #if ARPv6 + gaARP_Cache6 = malloc( ARP_CACHE_SIZE * sizeof(struct sARP_Cache6) ); + memset( gaARP_Cache6, 0, ARP_CACHE_SIZE * sizeof(struct sARP_Cache6) ); + giARP_Cache6Space = ARP_CACHE_SIZE; + #endif + + Link_RegisterType(0x0806, ARP_int_GetPacket); + return 1; +} + +/** + * \brief Resolves a MAC address from an IPv4 address + */ +tMacAddr ARP_Resolve4(tInterface *Interface, tIPv4 Address) +{ + int lastID; + int i; + struct sArpRequest4 req; + Sint64 timeout; + + ENTER("pInterface xAddress", Interface, Address); + + // Check for broadcast + if( Address.L == -1 ) + { + LOG("Broadcast"); + LEAVE('-'); + return cMAC_BROADCAST; + } + + // Check routing tables if not on this subnet + if( IPStack_CompareAddress(4, &Address, Interface->Address, Interface->SubnetBits) == 0 ) + { + tRoute *route = IPStack_FindRoute(4, Interface, &Address); + // If the next hop is defined, use it + // - 0.0.0.0 as the next hop means "no next hop / direct" + if( route && ((tIPv4*)route->NextHop)->L != 0 ) + { + // Recursion: see /Recursion/ + LOG("Recursing with %s", IPStack_PrintAddress(4, route->NextHop)); + LEAVE('-'); + return ARP_Resolve4(Interface, *(tIPv4*)route->NextHop); + } + // No route, fall though + } + else + { + Uint32 netmask; + // Check for broadcast + netmask = IPv4_Netmask(Interface->SubnetBits); + if( (Address.L & ~netmask) == (0xFFFFFFFF & ~netmask) ) + { + LOG("Local Broadcast"); + LEAVE('-'); + return cMAC_BROADCAST; + } + } + + // Check ARP Cache + Mutex_Acquire( &glARP_Cache4 ); + for( i = 0; i < giARP_Cache4Space; i++ ) + { + if(gaARP_Cache4[i].IP.L != Address.L) continue; + + // Check if the entry needs to be refreshed + if( now() - gaARP_Cache4[i].LastUpdate > ARP_MAX_AGE ) break; + + Mutex_Release( &glARP_Cache4 ); + LOG("Return %x:%x:%x:%x:%x:%x", + gaARP_Cache4[i].MAC.B[0], gaARP_Cache4[i].MAC.B[1], + gaARP_Cache4[i].MAC.B[2], gaARP_Cache4[i].MAC.B[3], + gaARP_Cache4[i].MAC.B[4], gaARP_Cache4[i].MAC.B[5] + ); + LEAVE('-'); + return gaARP_Cache4[i].MAC; + } + Mutex_Release( &glARP_Cache4 ); + + lastID = giARP_LastUpdateID; + + // Create request + Log_Log("ARP4", "Asking for address %i.%i.%i.%i", + Address.B[0], Address.B[1], Address.B[2], Address.B[3] + ); + req.HWType = htons(0x0001); // Ethernet + req.Type = htons(0x0800); + req.HWSize = 6; + req.SWSize = 4; + req.Request = htons(1); + req.SourceMac = Interface->Adapter->MacAddr; + req.SourceIP = *(tIPv4*)Interface->Address; + req.DestMac = cMAC_BROADCAST; + req.DestIP = Address; + + // Send Request + Link_SendPacket(Interface->Adapter, 0x0806, req.DestMac, sizeof(struct sArpRequest4), &req); + + timeout = now() + Interface->TimeoutDelay; + + // Wait for a reply + for(;;) + { + while(lastID == giARP_LastUpdateID && now() < timeout) { + Threads_Yield(); + } + + if( now() >= timeout ) { + Log_Log("ARP4", "Timeout"); + break; // Timeout + } + + lastID = giARP_LastUpdateID; + + Mutex_Acquire( &glARP_Cache4 ); + for( i = 0; i < giARP_Cache4Space; i++ ) + { + if(gaARP_Cache4[i].IP.L != Address.L) continue; + + Mutex_Release( &glARP_Cache4 ); + Log_Debug("ARP4", "Return %02x:%02x:%02x:%02x:%02x:%02x", + gaARP_Cache4[i].MAC.B[0], gaARP_Cache4[i].MAC.B[1], + gaARP_Cache4[i].MAC.B[2], gaARP_Cache4[i].MAC.B[3], + gaARP_Cache4[i].MAC.B[4], gaARP_Cache4[i].MAC.B[5]); + return gaARP_Cache4[i].MAC; + } + Mutex_Release( &glARP_Cache4 ); + } + { + tMacAddr ret = {{0,0,0,0,0,0}}; + return ret; + } +} + +/** + * \brief Updates the ARP Cache entry for an IPv4 Address + */ +void ARP_UpdateCache4(tIPv4 SWAddr, tMacAddr HWAddr) +{ + int i; + int free = -1; + int oldest = 0; + + // Find an entry for the IP address in the cache + Mutex_Acquire(&glARP_Cache4); + for( i = giARP_Cache4Space; i--; ) + { + if(gaARP_Cache4[oldest].LastUpdate > gaARP_Cache4[i].LastUpdate) { + oldest = i; + } + if( gaARP_Cache4[i].IP.L == SWAddr.L ) break; + if( gaARP_Cache4[i].LastUpdate == 0 && free == -1 ) free = i; + } + // If there was no match, we need to make one + if(i == -1) { + if(free != -1) + i = free; + else + i = oldest; + } + + Log_Log("ARP4", "Caching %i.%i.%i.%i (%02x:%02x:%02x:%02x:%02x:%02x) in %i", + SWAddr.B[0], SWAddr.B[1], SWAddr.B[2], SWAddr.B[3], + HWAddr.B[0], HWAddr.B[1], HWAddr.B[2], HWAddr.B[3], HWAddr.B[4], HWAddr.B[5], + i + ); + + gaARP_Cache4[i].IP = SWAddr; + gaARP_Cache4[i].MAC = HWAddr; + gaARP_Cache4[i].LastUpdate = now(); + giARP_LastUpdateID ++; + Mutex_Release(&glARP_Cache4); +} + +#if ARPv6 +/** + * \brief Updates the ARP Cache entry for an IPv6 Address + */ +void ARP_UpdateCache6(tIPv6 SWAddr, tMacAddr HWAddr) +{ + int i; + int free = -1; + int oldest = 0; + + // Find an entry for the MAC address in the cache + Mutex_Acquire(&glARP_Cache6); + for( i = giARP_Cache6Space; i--; ) + { + if(gaARP_Cache6[oldest].LastUpdate > gaARP_Cache6[i].LastUpdate) { + oldest = i; + } + if( MAC_EQU(gaARP_Cache6[i].MAC, HWAddr) ) break; + if( gaARP_Cache6[i].LastUpdate == 0 && free == -1 ) free = i; + } + // If there was no match, we need to make one + if(i == -1) { + if(free != -1) + i = free; + else + i = oldest; + gaARP_Cache6[i].MAC = HWAddr; + } + + gaARP_Cache6[i].IP = SWAddr; + gaARP_Cache6[i].LastUpdate = now(); + giARP_LastUpdateID ++; + Mutex_Release(&glARP_Cache6); +} +#endif + +/** + * \fn void ARP_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer) + * \brief Called when an ARP packet is recieved + */ +void ARP_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer) +{ + tArpRequest4 *req4 = Buffer; + #if ARPv6 + tArpRequest6 *req6 = Buffer; + #endif + tInterface *iface; + + // Sanity Check Packet + if( Length < (int)sizeof(tArpRequest4) ) { + Log_Log("ARP", "Recieved undersized packet"); + return ; + } + if( ntohs(req4->Type) != 0x0800 ) { + Log_Log("ARP", "Recieved a packet with a bad type (0x%x)", ntohs(req4->Type)); + return ; + } + if( req4->HWSize != 6 ) { + Log_Log("ARP", "Recieved a packet with HWSize != 6 (%i)", req4->HWSize); + return; + } + #if ARP_DETECT_SPOOFS + if( !MAC_EQU(req4->SourceMac, From) ) { + Log_Log("ARP", "ARP spoofing detected " + "(%02x%02x:%02x%02x:%02x%02x != %02x%02x:%02x%02x:%02x%02x)", + req4->SourceMac.B[0], req4->SourceMac.B[1], req4->SourceMac.B[2], + req4->SourceMac.B[3], req4->SourceMac.B[4], req4->SourceMac.B[5], + From.B[0], From.B[1], From.B[2], + From.B[3], From.B[4], From.B[5] + ); + return; + } + #endif + + switch( ntohs(req4->Request) ) + { + case 1: // You want my IP? + // Check what type of IP it is + switch( req4->SWSize ) + { + case 4: + Log_Debug("ARP", "ARP Request IPv4 Address %i.%i.%i.%i from %i.%i.%i.%i", + req4->DestIP.B[0], req4->DestIP.B[1], req4->DestIP.B[2], + req4->DestIP.B[3], + req4->SourceIP.B[0], req4->SourceIP.B[1], + req4->SourceIP.B[2], req4->SourceIP.B[3]); + Log_Debug("ARP", " from MAC %02x:%02x:%02x:%02x:%02x:%02x", + req4->SourceMac.B[0], req4->SourceMac.B[1], + req4->SourceMac.B[2], req4->SourceMac.B[3], + req4->SourceMac.B[4], req4->SourceMac.B[5]); + iface = IPv4_GetInterface(Adapter, req4->DestIP, 0); + if( iface ) + { + ARP_UpdateCache4(req4->SourceIP, req4->SourceMac); + + req4->DestIP = req4->SourceIP; + req4->DestMac = req4->SourceMac; + req4->SourceIP = *(tIPv4*)iface->Address;; + req4->SourceMac = Adapter->MacAddr; + req4->Request = htons(2); + Log_Debug("ARP", "Sending back us (%02x:%02x:%02x:%02x:%02x:%02x)", + req4->SourceMac.B[0], req4->SourceMac.B[1], + req4->SourceMac.B[2], req4->SourceMac.B[3], + req4->SourceMac.B[4], req4->SourceMac.B[5]); + Link_SendPacket(Adapter, 0x0806, req4->DestMac, sizeof(tArpRequest4), req4); + } + break; + #if ARPv6 + case 6: + if( Length < (int)sizeof(tArpRequest6) ) { + Log_Log("ARP", "Recieved undersized packet (IPv6)"); + return ; + } + Log_Debug("ARP", "ARP Request IPv6 Address %08x:%08x:%08x:%08x", + ntohl(req6->DestIP.L[0]), ntohl(req6->DestIP.L[1]), + ntohl(req6->DestIP.L[2]), ntohl(req6->DestIP.L[3]) + ); + iface = IPv6_GetInterface(Adapter, req6->DestIP, 0); + if( iface ) + { + req6->DestIP = req6->SourceIP; + req6->DestMac = req6->SourceMac; + req6->SourceIP = *(tIPv6*)iface->Address; + req6->SourceMac = Adapter->MacAddr; + req6->Request = htons(2); + Log_Debug("ARP", "Sending back us (%02x:%02x:%02x:%02x:%02x:%02x)", + req4->SourceMac.B[0], req4->SourceMac.B[1], + req4->SourceMac.B[2], req4->SourceMac.B[3], + req4->SourceMac.B[4], req4->SourceMac.B[5]); + Link_SendPacket(Adapter, 0x0806, req6->DestMac, sizeof(tArpRequest6), req6); + } + break; + #endif + default: + Log_Debug("ARP", "Unknown Protocol Address size (%i)", req4->SWSize); + return ; + } + + break; + + case 2: // Ooh! A response! + // Check what type of IP it is + switch( req4->SWSize ) + { + case 4: + ARP_UpdateCache4( req4->SourceIP, From ); + break; + #if ARPv6 + case 6: + if( Length < (int)sizeof(tArpRequest6) ) { + Log_Debug("ARP", "Recieved undersized packet (IPv6)"); + return ; + } + ARP_UpdateCache6( req6->SourceIP, From ); + break; + #endif + default: + Log_Debug("ARP", "Unknown Protocol Address size (%i)", req4->SWSize); + return ; + } + + break; + + default: + Log_Warning("ARP", "Unknown Request ID %i", ntohs(req4->Request)); + break; + } +} diff --git a/KernelLand/Modules/IPStack/arp.h b/KernelLand/Modules/IPStack/arp.h new file mode 100644 index 00000000..07cdb64d --- /dev/null +++ b/KernelLand/Modules/IPStack/arp.h @@ -0,0 +1,35 @@ +/* + * Acess2 IP Stack + * - Common Header + */ +#ifndef _ARP_H_ +#define _ARP_H_ + +#include "ipstack.h" + +typedef struct sArpRequest4 tArpRequest4; +typedef struct sArpRequest6 tArpRequest6; + +struct sArpRequest4 { + Uint16 HWType; + Uint16 Type; + Uint8 HWSize, SWSize; + Uint16 Request; + tMacAddr SourceMac; + tIPv4 SourceIP; + tMacAddr DestMac; + tIPv4 DestIP; +} __attribute__((packed)); + +struct sArpRequest6 { + Uint16 HWType; + Uint16 Type; + Uint8 HWSize, SWSize; + Uint16 Request; + tMacAddr SourceMac; + tIPv6 SourceIP; + tMacAddr DestMac; + tIPv6 DestIP; +} __attribute__((packed)); + +#endif diff --git a/KernelLand/Modules/IPStack/firewall.c b/KernelLand/Modules/IPStack/firewall.c new file mode 100644 index 00000000..b0390816 --- /dev/null +++ b/KernelLand/Modules/IPStack/firewall.c @@ -0,0 +1,173 @@ +/* + * Acess2 IP Stack + * - Firewall Rules + */ +#include "ipstack.h" +#include "firewall.h" + +#define MAX_ADDRTYPE 9 + +// === IMPORTS === + +// === TYPES === +typedef struct sKeyValue tKeyValue; +typedef struct sFirewallMod tFirewallMod; +typedef struct sModuleRule tModuleRule; +typedef struct sRule tRule; +typedef struct sChain tChain; + +// === STRUCTURES === +struct sKeyValue +{ + const char *Key; + const char *Value; +}; + +struct sFirewallMod +{ + const char *Name; + + int (*Match)(tModuleRule *Rule, int AddrType, + const void *Src, const void *Dest, + Uint8 Type, Uint32 Flags, + size_t Length, const void *Data); + + tModuleRule *(*Create)(tKeyValue *Params); +}; + +struct sModuleRule +{ + tModuleRule *Next; + + tFirewallMod *Mod; + + char Data[]; +}; + +struct sRule +{ + tRule *Next; + + int PacketCount; // Number of packets seen + int ByteCount; // Number of bytes seen (IP Payload bytes) + + int bInvertSource; // Boolean NOT flag on source + void *Source; // Source address bytes + int SourceMask; // Source address mask bits + + int bInvertDest; // Boolean NOT flag on destination + void *Dest; // Destination address bytes + int DestMask; // Destination address mask bits + + tModuleRule *Modules; // Modules loaded for this rule + + char Target[]; // Target rule name +}; + +struct sChain +{ + tChain *Next; + + tRule *FirstRule; + tRule *LastRule; + + char Name[]; +}; + +// === PROTOTYPES === + int IPTables_TestChain( + const char *RuleName, + const int AddressType, + const void *Src, const void *Dest, + Uint8 Type, Uint32 Flags, + size_t Length, const void *Data + ); + +// === GLOBALS === +tChain *gapFirewall_Chains[MAX_ADDRTYPE+1]; +tChain gFirewall_DROP = {.Name="DROP"}; +tChain gFirewall_ACCEPT = {.Name="ACCEPT"}; +tChain gFirewall_RETURN = {.Name="RETURN"}; + +// === CODE === +/** + * \brief Apply a rule to a packet + * \return -1 for no match, -2 for RETURN, eFirewallAction otherwise + */ +int IPTables_DoRule( + tRule *Rule, int AddrType, + const void *Src, const void *Dest, + Uint8 Type, Uint32 Flags, + size_t Length, const void *Data) +{ + int rv; + // Check if source doesn't match + if( !IPStack_CompareAddress(AddrType, Src, Rule->Source, Rule->SourceMask) == !Rule->bInvertSource ) + return -1; + // Check if destination doesn't match + if( !IPStack_CompareAddress(AddrType, Dest, Rule->Dest, Rule->DestMask) == !Rule->bInvertDest ) + return -1; + + // TODO: Handle modules (UDP/TCP/etc) + tModuleRule *modrule; + for( modrule = Rule->Modules; modrule; modrule = modrule->Next ) + { + if( !modrule->Mod->Match ) continue; + rv = modrule->Mod->Match(modrule, AddrType, Src, Dest, Type, Flags, Length, Data); + if(rv != 0) return rv; // No match / action + } + + // Update statistics + Rule->PacketCount ++; + Rule->ByteCount += Length; + + return IPTables_TestChain(Rule->Target, AddrType, Src, Dest, Type, Flags, Length, Data); +} + +/** + * \brief Tests an IPv4 chain on a packet + * \return Boolean Disallow (0: Packet Allowed, 1: Drop, 2: Reject, 3: Continue, -1 no match) + */ +int IPTables_TestChain( + const char *RuleName, + const int AddressType, + const void *Src, const void *Dest, + Uint8 Type, Uint32 Flags, + size_t Length, const void *Data + ) +{ + int rv; + tChain *chain; + tRule *rule; + + if( AddressType >= MAX_ADDRTYPE ) return -1; // Bad address type + + // Catch builtin targets + if(strcmp(RuleName, "") == 0) return -1; // No action + if(strcmp(RuleName, "ACCEPT") == 0) return 0; // Accept packet + if(strcmp(RuleName, "DROP") == 0) return 1; // Drop packet + if(strcmp(RuleName, "RETURN") == 0) return -2; // Return from rule + + // Find the rule + for( chain = gapFirewall_Chains[AddressType]; chain; chain = chain->Next ) + { + if( strcmp(chain->Name, RuleName) == 0 ) + break; + } + if( !chain ) return -1; // Bad rule name + + // Check the rules + for( rule = chain->FirstRule; rule; rule = rule->Next ) + { + rv = IPTables_DoRule(rule, AddressType, Src, Dest, Type, Flags, Length, Data); + if( rv == -1 ) + continue ; + if( rv == -2 ) // -2 = Return from a chain/table, pretend no match + return -1; + + return rv; + } + + + return 0; // Accept all for now +} diff --git a/KernelLand/Modules/IPStack/firewall.h b/KernelLand/Modules/IPStack/firewall.h new file mode 100644 index 00000000..5a623803 --- /dev/null +++ b/KernelLand/Modules/IPStack/firewall.h @@ -0,0 +1,23 @@ +/* + */ +#ifndef _FIREWALL_H_ +#define _FIREWALL_H_ + +enum eFirewallActions +{ + FIREWALL_ACCEPT, + FIREWALL_DROP +}; + +/** + * \brief Tests a packet on a chain + */ +extern int IPTables_TestChain( + const char *RuleName, + const int AddressType, + const void *Src, const void *Dest, + Uint8 Type, Uint32 Flags, + size_t Length, const void *Data + ); + +#endif diff --git a/KernelLand/Modules/IPStack/icmp.c b/KernelLand/Modules/IPStack/icmp.c new file mode 100644 index 00000000..16037199 --- /dev/null +++ b/KernelLand/Modules/IPStack/icmp.c @@ -0,0 +1,133 @@ +/* + * Acess2 IP Stack + * - ICMP Handling + */ +#include "ipstack.h" +#include "ipv4.h" +#include "icmp.h" + +// === CONSTANTS === +#define PING_SLOTS 64 + +// === PROTOTYPES === +void ICMP_Initialise(); +void ICMP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer); + +// === GLOBALS === +struct { + tInterface *Interface; + int bArrived; +} gICMP_PingSlots[PING_SLOTS]; + +// === CODE === +/** + * \fn void ICMP_Initialise() + * \brief Initialise the ICMP Layer + */ +void ICMP_Initialise() +{ + IPv4_RegisterCallback(IP4PROT_ICMP, ICMP_GetPacket); +} + +/** + * \fn void ICMP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) + * \brief Handles a packet from the IP Layer + */ +void ICMP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) +{ + tICMPHeader *hdr = Buffer; + + //Log_Debug("ICMPv4", "Length = %i", Length); + Log_Debug("ICMPv4", "hdr->Type, hdr->Code = %i, %i", hdr->Type, hdr->Code); + //Log_Debug("ICMPv4", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum)); + Log_Debug("ICMPv4", "hdr->ID = 0x%x", ntohs(hdr->ID)); + Log_Debug("ICMPv4", "hdr->Sequence = 0x%x", ntohs(hdr->Sequence)); + + switch(hdr->Type) + { + // -- 0: Echo Reply + case ICMP_ECHOREPLY: + if(hdr->Code != 0) { + Log_Warning("ICMPv4", "Code == %i for ICMP Echo Reply, should be 0", hdr->Code); + return ; + } + if(hdr->ID != (Uint16)~hdr->Sequence) { + Log_Warning("ICMPv4", "ID and Sequence values do not match"); + //return ; + } + gICMP_PingSlots[hdr->ID].bArrived = 1; + break; + + // -- 3: Destination Unreachable + case ICMP_UNREACHABLE: + switch(hdr->Code) + { + case 3: // Port Unreachable + Log_Debug("ICMPv4", "Destination Unreachable (Port Unreachable)"); + break; + default: + Log_Debug("ICMPv4", "Destination Unreachable (Code %i)", hdr->Code); + break; + } +// IPv4_Unreachable( Interface, hdr->Code, htons(hdr->Length)-sizeof(tICMPHeader), hdr->Data ); + break; + + // -- 8: Echo Request + case ICMP_ECHOREQ: + if(hdr->Code != 0) { + Log_Warning("ICMPv4", "Code == %i for ICMP Echo Request, should be 0", hdr->Code); + return ; + } + //Log_Debug("ICMPv4", "Replying"); + hdr->Type = ICMP_ECHOREPLY; + hdr->Checksum = 0; + hdr->Checksum = htons( IPv4_Checksum( (Uint16*)hdr, Length/2 ) ); + //Log_Debug("ICMPv4", "Checksum = 0x%04x", hdr->Checksum); + IPv4_SendPacket(Interface, *(tIPv4*)Address, 1, ntohs(hdr->Sequence), Length, hdr); + break; + default: + break; + } + +} + +/** + * \brief Sends ICMP Echo and waits for the reply + * \note Times out after \a Interface->TimeoutDelay has elapsed + */ +int ICMP_Ping(tInterface *Interface, tIPv4 Addr) +{ + Sint64 ts; + Sint64 end; + char buf[32] = "\x8\0\0\0\0\0\0\0Acess2 I" + "P/TCP Stack 1.0\0"; + tICMPHeader *hdr = (void*)buf; + int i; + + for(;;) + { + for(i=0;iID = i; + hdr->Sequence = ~i; + hdr->Checksum = htons( IPv4_Checksum((Uint16*)hdr, sizeof(buf)/2) ); + + ts = now(); + + IPv4_SendPacket(Interface, Addr, 1, i, sizeof(buf), buf); + + end = ts + Interface->TimeoutDelay; + while( !gICMP_PingSlots[i].bArrived && now() < end) Threads_Yield(); + + if(now() > end) + return -1; + + return (int)( now() - ts ); +} diff --git a/KernelLand/Modules/IPStack/icmp.h b/KernelLand/Modules/IPStack/icmp.h new file mode 100644 index 00000000..8e787224 --- /dev/null +++ b/KernelLand/Modules/IPStack/icmp.h @@ -0,0 +1,34 @@ +/* + * Acess2 IP Stack + * - ICMP Handling + */ +#ifndef _ICMP_H_ +#define _ICMP_H_ + +// === TYPEDEFS === +typedef struct sICMPHeader tICMPHeader; + +// === STRUCTURES === +struct sICMPHeader +{ + Uint8 Type; + Uint8 Code; + Uint16 Checksum; + Uint16 ID; + Uint16 Sequence; + Uint8 Data[]; +}; + +// === CONSTANTS === +enum eICMPTypes +{ + ICMP_ECHOREPLY = 0, + ICMP_UNREACHABLE = 3, + ICMP_QUENCH = 4, + ICMP_REDIRECT = 5, + ICMP_ALTADDR = 6, + ICMP_ECHOREQ = 8, + ICMP_TRACE = 30 // Information Request +}; + +#endif diff --git a/KernelLand/Modules/IPStack/interface.c b/KernelLand/Modules/IPStack/interface.c new file mode 100644 index 00000000..b0b63b9a --- /dev/null +++ b/KernelLand/Modules/IPStack/interface.c @@ -0,0 +1,579 @@ +/* + * Acess2 IP Stack + * - Interface Control + */ +#define DEBUG 0 +#define VERSION VER2(0,10) +#include "ipstack.h" +#include "link.h" +#include +#include + +// === CONSTANTS === +//! Default timeout value, 30 seconds +#define DEFAULT_TIMEOUT (30*1000) + +// === IMPORTS === +extern int IPv4_Ping(tInterface *Iface, tIPv4 Addr); +//extern int IPv6_Ping(tInterface *Iface, tIPv6 Addr); +extern tVFS_Node gIP_RouteNode; + +// === PROTOTYPES === +char *IPStack_Root_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Name); + int IPStack_Root_IOCtl(tVFS_Node *Node, int ID, void *Data); + + int IPStack_AddFile(tSocketFile *File); +tInterface *IPStack_AddInterface(const char *Device, const char *Name); +tAdapter *IPStack_GetAdapter(const char *Path); + +char *IPStack_Iface_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *IPStack_Iface_FindDir(tVFS_Node *Node, const char *Name); + int IPStack_Iface_IOCtl(tVFS_Node *Node, int ID, void *Data); + +// === GLOBALS === +tVFS_NodeType gIP_InterfaceNodeType = { + .ReadDir = IPStack_Iface_ReadDir, + .FindDir = IPStack_Iface_FindDir, + .IOCtl = IPStack_Iface_IOCtl +}; +//! Loopback (127.0.0.0/8, ::1) Pseudo-Interface +tInterface gIP_LoopInterface = { + .Node = { + .ImplPtr = &gIP_LoopInterface, + .Flags = VFS_FFLAG_DIRECTORY, + .Size = -1, + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Type = &gIP_InterfaceNodeType + }, + .Adapter = NULL, + .Type = 0 +}; +tShortSpinlock glIP_Interfaces; +tInterface *gIP_Interfaces = NULL; +tInterface *gIP_Interfaces_Last = NULL; + +tSocketFile *gIP_FileTemplates; + +tAdapter gIP_LoopAdapter = { + .DeviceLen = 8, + .Device = "LOOPBACK" + }; +tMutex glIP_Adapters; +tAdapter *gIP_Adapters = NULL; + int giIP_NextIfaceId = 1; + +// === CODE === + +/** + * \brief Read from the IP Stack's Device Directory + */ +char *IPStack_Root_ReadDir(tVFS_Node *Node, int Pos) +{ + tInterface *iface; + char *name; + ENTER("pNode iPos", Node, Pos); + + + // Routing Subdir + if( Pos == 0 ) { + LEAVE('s', "routes"); + return strdup("routes"); + } + // Pseudo Interfaces + if( Pos == 1 ) { + LEAVE('s', "lo"); + return strdup("lo"); + } + Pos -= 2; + + // Traverse the list + for( iface = gIP_Interfaces; iface && Pos--; iface = iface->Next ) ; + + // Did we run off the end? + if(!iface) { + LEAVE('n'); + return NULL; + } + + name = malloc(4); + if(!name) { + Log_Warning("IPStack", "IPStack_Root_ReadDir - malloc error"); + LEAVE('n'); + return NULL; + } + + // Create the name + Pos = iface->Node.ImplInt; + if(Pos < 10) { + name[0] = '0' + Pos; + name[1] = '\0'; + } + else if(Pos < 100) { + name[0] = '0' + Pos/10; + name[1] = '0' + Pos%10; + name[2] = '\0'; + } + else { + name[0] = '0' + Pos/100; + name[1] = '0' + (Pos/10)%10; + name[2] = '0' + Pos%10; + name[3] = '\0'; + } + + LEAVE('s', name); + // Return the pre-generated name + return name; +} + +/** + * \brief Get the node of an interface + */ +tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Name) +{ + #if 0 + int i, num; + #endif + tInterface *iface; + + ENTER("pNode sName", Node, Name); + + // Routing subdir + if( strcmp(Name, "routes") == 0 ) { + LEAVE('p', &gIP_RouteNode); + return &gIP_RouteNode; + } + + // Loopback + if( strcmp(Name, "lo") == 0 ) { + LEAVE('p', &gIP_LoopInterface.Node); + return &gIP_LoopInterface.Node; + } + + for( iface = gIP_Interfaces; iface; iface = iface->Next ) + { + if( strcmp(iface->Name, Name) == 0 ) + { + LEAVE('p', &iface->Node); + return &iface->Node; + } + } + + LEAVE('p', NULL); + return NULL; +} + +static const char *casIOCtls_Root[] = { DRV_IOCTLNAMES, "add_interface", NULL }; +/** + * \brief Handles IOCtls for the IPStack root + */ +int IPStack_Root_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + int tmp; + ENTER("pNode iID pData", Node, ID, Data); + + switch(ID) + { + // --- Standard IOCtls (0-3) --- + BASE_IOCTLS(DRV_TYPE_MISC, "IPStack", VERSION, casIOCtls_Root) + + /* + * add_interface + * - Adds a new IP interface and binds it to a device + */ + case 4: + if( Threads_GetUID() != 0 ) LEAVE_RET('i', -1); + if( !CheckString( Data ) ) LEAVE_RET('i', -1); + LOG("New interface for '%s'", Data); + { + char name[4] = ""; + tInterface *iface = IPStack_AddInterface(Data, name); + if(iface == NULL) LEAVE_RET('i', -1); + tmp = iface->Node.ImplInt; + } + LEAVE_RET('i', tmp); + } + LEAVE('i', 0); + return 0; +} + +/** + * \fn tInterface *IPStack_AddInterface(char *Device) + * \brief Adds an interface to the list + */ +tInterface *IPStack_AddInterface(const char *Device, const char *Name) +{ + tInterface *iface; + tAdapter *card; + int nameLen; + + ENTER("sDevice", Device); + + card = IPStack_GetAdapter(Device); + if( !card ) { + Log_Debug("IPStack", "Unable to open card '%s'", Device); + LEAVE('n'); + return NULL; // ERR_YOURBAD + } + + nameLen = sprintf(NULL, "%i", giIP_NextIfaceId); + + iface = malloc( + sizeof(tInterface) + + nameLen + 1 + + IPStack_GetAddressSize(-1)*3 // Address, Route->Network, Route->NextHop + ); + if(!iface) { + Log_Warning("IPStack", "AddInterface - malloc() failed"); + LEAVE('n'); + return NULL; // Return ERR_MYBAD + } + + iface->Next = NULL; + iface->Type = 0; // Unset type + iface->Address = iface->Name + nameLen + 1; // Address + iface->Route.Network = iface->Address + IPStack_GetAddressSize(-1); + iface->Route.NextHop = iface->Route.Network + IPStack_GetAddressSize(-1); + + // Create Node + iface->Node.ImplPtr = iface; + iface->Node.Flags = VFS_FFLAG_DIRECTORY; + iface->Node.Size = -1; + iface->Node.NumACLs = 1; + iface->Node.ACLs = &gVFS_ACL_EveryoneRX; + iface->Node.Type = &gIP_InterfaceNodeType; + + // Set Defaults + iface->TimeoutDelay = DEFAULT_TIMEOUT; + + // Get adapter handle + iface->Adapter = card; + + // Delay setting ImplInt until after the adapter is opened + // Keeps things simple + iface->Node.ImplInt = giIP_NextIfaceId++; + sprintf(iface->Name, "%i", (int)iface->Node.ImplInt); + + // Append to list + SHORTLOCK( &glIP_Interfaces ); + if( gIP_Interfaces ) { + gIP_Interfaces_Last->Next = iface; + gIP_Interfaces_Last = iface; + } + else { + gIP_Interfaces = iface; + gIP_Interfaces_Last = iface; + } + SHORTREL( &glIP_Interfaces ); + +// gIP_DriverInfo.RootNode.Size ++; + + // Success! + LEAVE('p', iface); + return iface; +} + +/** + * \brief Adds a file to the socket list + */ +int IPStack_AddFile(tSocketFile *File) +{ + Log_Log("IPStack", "Added file '%s'", File->Name); + File->Next = gIP_FileTemplates; + gIP_FileTemplates = File; + return 0; +} + +// --- +// VFS Functions +// --- +/** + * \brief Read from an interface's directory + */ +char *IPStack_Iface_ReadDir(tVFS_Node *Node, int Pos) +{ + tSocketFile *file = gIP_FileTemplates; + while(Pos-- && file) { + file = file->Next; + } + + if(!file) return NULL; + + return strdup(file->Name); +} + +/** + * \brief Gets a named node from an interface directory + */ +tVFS_Node *IPStack_Iface_FindDir(tVFS_Node *Node, const char *Name) +{ + tSocketFile *file = gIP_FileTemplates; + + // Get file definition + for(;file;file = file->Next) + { + if( strcmp(file->Name, Name) == 0 ) break; + } + if(!file) return NULL; + + // Pass the buck! + return file->Init(Node->ImplPtr); +} + +/** + * \brief Names for interface IOCtl Calls + */ +static const char *casIOCtls_Iface[] = { + DRV_IOCTLNAMES, + "getset_type", + "get_address", "set_address", + "getset_subnet", + "get_device", + "ping", + NULL + }; +/** + * \brief Handles IOCtls for the IPStack interfaces + */ +int IPStack_Iface_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + int tmp, size; + tInterface *iface = (tInterface*)Node->ImplPtr; + ENTER("pNode iID pData", Node, ID, Data); + + switch(ID) + { + // --- Standard IOCtls (0-3) --- + BASE_IOCTLS(DRV_TYPE_MISC, "IPStack", VERSION, casIOCtls_Iface) + + /* + * getset_type + * - Get/Set the interface type + */ + case 4: + // Set Type? + if( Data ) + { + // Ok, it's set type + if( Threads_GetUID() != 0 ) { + LOG("Attempt by non-root to alter an interface (%i)", Threads_GetUID()); + LEAVE('i', -1); + return -1; + } + if( !CheckMem( Data, sizeof(int) ) ) { + LOG("Invalid pointer %p", Data); + LEAVE('i', -1); + return -1; + } + + // Set type + iface->Type = *(int*)Data; + LOG("Interface type set to %i", iface->Type); + size = IPStack_GetAddressSize(iface->Type); + // Check it's actually valid + if( iface->Type != 0 && size == 0 ) { + iface->Type = 0; + LEAVE('i', -1); + return -1; + } + + // Clear address + memset(iface->Address, 0, size); + } + LEAVE('i', iface->Type); + return iface->Type; + + /* + * get_address + * - Get the interface's address + */ + case 5: + size = IPStack_GetAddressSize(iface->Type); + if( !CheckMem( Data, size ) ) LEAVE_RET('i', -1); + memcpy( Data, iface->Address, size ); + LEAVE('i', 1); + return 1; + + /* + * set_address + * - Set the interface's address + */ + case 6: + if( Threads_GetUID() != 0 ) LEAVE_RET('i', -1); + + size = IPStack_GetAddressSize(iface->Type); + if( !CheckMem( Data, size ) ) LEAVE_RET('i', -1); + // TODO: Protect against trashing + LOG("Interface address set to '%s'", IPStack_PrintAddress(iface->Type, Data)); + memcpy( iface->Address, Data, size ); + LEAVE_RET('i', 1); + + /* + * getset_subnet + * - Get/Set the bits in the address subnet + */ + case 7: + // Do we want to set the value? + if( Data ) + { + // Are we root? (TODO: Check Owner/Group) + if( Threads_GetUID() != 0 ) LEAVE_RET('i', -1); + // Is the memory valid + if( !CheckMem(Data, sizeof(int)) ) LEAVE_RET('i', -1); + + // Is the mask sane? + if( *(int*)Data < 0 || *(int*)Data > IPStack_GetAddressSize(iface->Type)*8-1 ) + LEAVE_RET('i', -1); + LOG("Set subnet bits to %i", *(int*)Data); + // Ok, set it + iface->SubnetBits = *(int*)Data; + } + LEAVE_RET('i', iface->SubnetBits); + + /* + * get_device + * - Gets the name of the attached device + */ + case 8: + if( iface->Adapter == NULL ) + LEAVE_RET('i', 0); + if( Data == NULL ) + LEAVE_RET('i', iface->Adapter->DeviceLen); + if( !CheckMem( Data, iface->Adapter->DeviceLen+1 ) ) + LEAVE_RET('i', -1); + strcpy( Data, iface->Adapter->Device ); + LEAVE_RET('i', iface->Adapter->DeviceLen); + + /* + * ping + * - Send an ICMP Echo + */ + case 9: + switch(iface->Type) + { + case 0: + LEAVE_RET('i', 1); + + case 4: + if( !CheckMem( Data, sizeof(tIPv4) ) ) LEAVE_RET('i', -1); + tmp = IPv4_Ping(iface, *(tIPv4*)Data); + LEAVE_RET('i', tmp); + + case 6: + LEAVE_RET('i', 1); + } + break; + + } + + LEAVE('i', 0); + return 0; +} + +// --- Internal --- +/** + * \fn tAdapter *IPStack_GetAdapter(const char *Path) + * \brief Gets/opens an adapter given the path + */ +tAdapter *IPStack_GetAdapter(const char *Path) +{ + tAdapter *dev; + int tmp; + + ENTER("sPath", Path); + + // Check for loopback + if( strcmp(Path, "LOOPBACK") == 0 ) + { + // Initialise if required + if( gIP_LoopAdapter.DeviceFD == 0 ) + { + dev = &gIP_LoopAdapter; + + dev->NRef = 1; + dev->DeviceLen = 8; + + dev->DeviceFD = VFS_Open( "/Devices/fifo/anon", VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE ); + if( dev->DeviceFD == -1 ) { + Log_Warning("IPStack", "Unable to open FIFO '/Devices/fifo/anon' for loopback"); + return NULL; + } + + dev->MacAddr.B[0] = 'A'; + dev->MacAddr.B[1] = 'c'; + dev->MacAddr.B[2] = 'e'; + dev->MacAddr.B[3] = 's'; + dev->MacAddr.B[4] = 's'; + dev->MacAddr.B[5] = '2'; + + // Start watcher + Link_WatchDevice( dev ); + } + LEAVE('p', &gIP_LoopAdapter); + return &gIP_LoopAdapter; + } + + Mutex_Acquire( &glIP_Adapters ); + + // Check if this adapter is already open + for( dev = gIP_Adapters; dev; dev = dev->Next ) + { + if( strcmp(dev->Device, Path) == 0 ) { + dev->NRef ++; + Mutex_Release( &glIP_Adapters ); + LEAVE('p', dev); + return dev; + } + } + + // Ok, so let's open it + dev = malloc( sizeof(tAdapter) + strlen(Path) + 1 ); + if(!dev) { + Log_Warning("IPStack", "GetAdapter - malloc() failed"); + Mutex_Release( &glIP_Adapters ); + LEAVE('n'); + return NULL; + } + + // Fill Structure + strcpy( dev->Device, Path ); + dev->NRef = 1; + dev->DeviceLen = strlen(Path); + + // Open Device + dev->DeviceFD = VFS_Open( dev->Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE ); + if( dev->DeviceFD == -1 ) { + free( dev ); + Mutex_Release( &glIP_Adapters ); + LEAVE('n'); + return NULL; + } + + // Check that it is a network interface + tmp = VFS_IOCtl(dev->DeviceFD, 0, NULL); + LOG("Device type = %i", tmp); + if( tmp != DRV_TYPE_NETWORK ) { + Log_Warning("IPStack", "IPStack_GetAdapter: '%s' is not a network interface", dev->Device); + VFS_Close( dev->DeviceFD ); + free( dev ); + Mutex_Release( &glIP_Adapters ); + LEAVE('n'); + return NULL; + } + + // Get MAC Address + VFS_IOCtl(dev->DeviceFD, NET_IOCTL_GETMAC, &dev->MacAddr); + + // Add to list + dev->Next = gIP_Adapters; + gIP_Adapters = dev; + + Mutex_Release( &glIP_Adapters ); + + // Start watcher + Link_WatchDevice( dev ); + + LEAVE('p', dev); + return dev; +} diff --git a/KernelLand/Modules/IPStack/ipstack.h b/KernelLand/Modules/IPStack/ipstack.h new file mode 100644 index 00000000..e51fded9 --- /dev/null +++ b/KernelLand/Modules/IPStack/ipstack.h @@ -0,0 +1,125 @@ +/* + * Acess2 IP Stack + * - Common Header + */ +#ifndef _IPSTACK_H_ +#define _IPSTACK_H_ + +#include +#include + +typedef union uIPv4 tIPv4; +typedef union uIPv6 tIPv6; +typedef struct sMacAddr tMacAddr; +typedef struct sAdapter tAdapter; +typedef struct sInterface tInterface; +typedef struct sSocketFile tSocketFile; + +typedef void (*tIPCallback)(tInterface *Interface, void *Address, int Length, void *Buffer); + +enum eInterfaceTypes { + AF_NULL, + AF_INET4 = 4, // tIPv4 + AF_INET6 = 6 // tIPv6 +}; + +union uIPv4 { + Uint32 L; + Uint8 B[4]; +} __attribute__((packed)); + +union uIPv6 { + Uint16 W[8]; + Uint32 L[4]; + Uint8 B[16]; +} __attribute__((packed)); + +struct sMacAddr { + Uint8 B[6]; +} __attribute__((packed)); + +/** + * \brief Route definition structure + */ +typedef struct sRoute { + struct sRoute *Next; + + tVFS_Node Node; //!< Node for route manipulation + + tInterface *Interface; //!< Interface for this route + int AddressType; //!< 0: Invalid, 4: IPv4, 6: IPv4 + void *Network; //!< Network - Pointer to tIPv4/tIPv6/... at end of structure + int SubnetBits; //!< Number of bits in \a Network that are valid + void *NextHop; //!< Next Hop address - Pointer to tIPv4/tIPv6/... at end of structure + int Metric; //!< Route priority +} tRoute; + +struct sInterface { + struct sInterface *Next; //!< Next interface in list + + tVFS_Node Node; //!< Node to use the interface + + tAdapter *Adapter; //!< Adapter the interface is associated with + int TimeoutDelay; //!< Time in miliseconds before a packet times out + int Type; //!< Interface type, see ::eInterfaceTypes + + void *Address; //!< IP address (stored after the Name) + int SubnetBits; //!< Number of bits that denote the address network + + tRoute Route; //!< Interface route + + char Name[]; +}; + +/** + * \brief Represents a network adapter + */ +struct sAdapter { + struct sAdapter *Next; + + int DeviceFD; //!< File descriptor of the device + int NRef; //!< Number of times it's been referenced + + tMacAddr MacAddr; //!< Physical address of the adapter + int DeviceLen; //!< Device name length + char Device[]; //!< Device name +}; + +/** + * \brief Describes a socket file definition + */ +struct sSocketFile +{ + struct sSocketFile *Next; + const char *Name; + + tVFS_Node *(*Init)(tInterface *Interface); +}; + +static const tMacAddr cMAC_BROADCAST = {{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}; +static const tMacAddr cMAC_ZERO = {{0x00,0x00,0x00,0x00,0x00,0x00}}; + +#define MAC_SET(t,v) memcpy(&(t),&(v),sizeof(tMacAddr)) +#define IP4_SET(t,v) (t).L = (v).L; +#define IP6_SET(t,v) memcpy(&(t),&(v),sizeof(tIPv6)) + +#define MAC_EQU(a,b) (memcmp(&(a),&(b),sizeof(tMacAddr))==0) +#define IP4_EQU(a,b) ((a).L==(b).L) +#define IP6_EQU(a,b) (memcmp(&(a),&(b),sizeof(tIPv6))==0) + +// === FUNCTIONS === +#define htonb(v) (v) +#define htons(v) BigEndian16(v) +#define htonl(v) BigEndian32(v) +#define ntonb(v) (v) +#define ntohs(v) BigEndian16(v) +#define ntohl(v) BigEndian32(v) + +extern int IPStack_AddFile(tSocketFile *File); +extern int IPStack_GetAddressSize(int AddressType); +extern int IPStack_CompareAddress(int AddressType, const void *Address1, const void *Address2, int CheckBits); +extern const char *IPStack_PrintAddress(int AddressType, const void *Address); + +extern tRoute *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address); + +#endif diff --git a/KernelLand/Modules/IPStack/ipv4.c b/KernelLand/Modules/IPStack/ipv4.c new file mode 100644 index 00000000..86301e97 --- /dev/null +++ b/KernelLand/Modules/IPStack/ipv4.c @@ -0,0 +1,367 @@ +/* + * Acess2 IP Stack + * - IPv4 Protcol Handling + */ +#define DEBUG 1 +#include "ipstack.h" +#include "link.h" +#include "ipv4.h" +#include "firewall.h" + +#define DEFAULT_TTL 32 + +// === IMPORTS === +extern tInterface *gIP_Interfaces; +extern void ICMP_Initialise(); +extern int ICMP_Ping(tInterface *Interface, tIPv4 Addr); +extern tMacAddr ARP_Resolve4(tInterface *Interface, tIPv4 Address); + +// === PROTOTYPES === + int IPv4_Initialise(); + int IPv4_RegisterCallback(int ID, tIPCallback Callback); +void IPv4_int_GetPacket(tAdapter *Interface, tMacAddr From, int Length, void *Buffer); +tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast); +Uint32 IPv4_Netmask(int FixedBits); +Uint16 IPv4_Checksum(const void *Buf, size_t Length); + int IPv4_Ping(tInterface *Iface, tIPv4 Addr); + +// === GLOBALS === +tIPCallback gaIPv4_Callbacks[256]; + +// === CODE === +/** + * \brief Initialise the IPv4 Code + */ +int IPv4_Initialise() +{ + ICMP_Initialise(); + Link_RegisterType(IPV4_ETHERNET_ID, IPv4_int_GetPacket); + return 1; +} + +/** + * \brief Registers a callback + * \param ID 8-bit packet type ID + * \param Callback Callback function + */ +int IPv4_RegisterCallback(int ID, tIPCallback Callback) +{ + if( ID < 0 || ID > 255 ) return 0; + if( gaIPv4_Callbacks[ID] ) return 0; + gaIPv4_Callbacks[ID] = Callback; + return 1; +} + +/** + * \brief Creates and sends an IPv4 Packet + * \param Iface Interface + * \param Address Destination IP + * \param Protocol Protocol ID + * \param ID Some random ID number + * \param Length Data Length + * \param Data Packet Data + * \return Boolean Success + */ +int IPv4_SendPacket(tInterface *Iface, tIPv4 Address, int Protocol, int ID, int Length, const void *Data) +{ + tMacAddr to; + int bufSize = sizeof(tIPv4Header) + Length; + char buf[bufSize]; + tIPv4Header *hdr = (void*)buf; + int ret; + + to = ARP_Resolve4(Iface, Address); + if( MAC_EQU(to, cMAC_ZERO) ) { + // No route to host + Log_Notice("IPv4", "No route to host %i.%i.%i.%i", + Address.B[0], Address.B[1], Address.B[2], Address.B[3]); + return 0; + } + + // OUTPUT Firewall rule go here + ret = IPTables_TestChain("OUTPUT", + 4, (tIPv4*)Iface->Address, &Address, + Protocol, 0, + Length, Data); + if(ret > 0) { + // Just drop it (with an error) + Log_Notice("IPv4", "Firewall dropped packet"); + return 0; + } + + memcpy(&hdr->Options[0], Data, Length); + hdr->Version = 4; + hdr->HeaderLength = sizeof(tIPv4Header)/4; + hdr->DiffServices = 0; // TODO: Check + + hdr->Reserved = 0; + hdr->DontFragment = 0; + hdr->MoreFragments = 0; + hdr->FragOffLow = 0; + hdr->FragOffHi = 0; + + hdr->TotalLength = htons( bufSize ); + hdr->Identifcation = htons( ID ); // TODO: Check + hdr->TTL = DEFAULT_TTL; + hdr->Protocol = Protocol; + hdr->HeaderChecksum = 0; // Will be set later + hdr->Source = *(tIPv4*)Iface->Address; + hdr->Destination = Address; + hdr->HeaderChecksum = htons( IPv4_Checksum(hdr, sizeof(tIPv4Header)) ); + + Log_Log("IPv4", "Sending packet to %i.%i.%i.%i", + Address.B[0], Address.B[1], Address.B[2], Address.B[3]); + Link_SendPacket(Iface->Adapter, IPV4_ETHERNET_ID, to, bufSize, buf); + return 1; +} + +/** + * \fn void IPv4_int_GetPacket(tInterface *Adapter, tMacAddr From, int Length, void *Buffer) + * \brief Process an IPv4 Packet + */ +void IPv4_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer) +{ + tIPv4Header *hdr = Buffer; + tInterface *iface; + Uint8 *data; + int dataLength; + int ret; + + if(Length < sizeof(tIPv4Header)) return; + + #if 0 + //Log_Log("IPv4", "Version = %i", hdr->Version); + //Log_Log("IPv4", "HeaderLength = %i", hdr->HeaderLength); + //Log_Log("IPv4", "DiffServices = %i", hdr->DiffServices); + Log_Debug("IPv4", "TotalLength = %i", ntohs(hdr->TotalLength) ); + //Log_Log("IPv4", "Identifcation = %i", ntohs(hdr->Identifcation) ); + //Log_Log("IPv4", "TTL = %i", hdr->TTL ); + Log_Debug("IPv4", "Protocol = %i", hdr->Protocol ); + //Log_Log("IPv4", "HeaderChecksum = 0x%x", ntohs(hdr->HeaderChecksum) ); + Log_Debug("IPv4", "Source = %i.%i.%i.%i", + hdr->Source.B[0], hdr->Source.B[1], hdr->Source.B[2], hdr->Source.B[3] ); + Log_Debug("IPv4", "Destination = %i.%i.%i.%i", + hdr->Destination.B[0], hdr->Destination.B[1], + hdr->Destination.B[2], hdr->Destination.B[3] ); + #endif + + // Check that the version IS IPv4 + if(hdr->Version != 4) { + Log_Log("IPv4", "hdr->Version(%i) != 4", hdr->Version); + return; + } + + // Check Header checksum + { + Uint16 hdrVal, compVal; + hdrVal = ntohs(hdr->HeaderChecksum); + hdr->HeaderChecksum = 0; + compVal = IPv4_Checksum(hdr, hdr->HeaderLength * 4); + if(hdrVal != compVal) { + Log_Log("IPv4", "Header checksum fails (%04x != %04x)", hdrVal, compVal); + return ; + } + hdr->HeaderChecksum = hdrVal; + } + + // Check Packet length + if( ntohs(hdr->TotalLength) > Length) { + Log_Log("IPv4", "hdr->TotalLength(%i) > Length(%i)", ntohs(hdr->TotalLength), Length); + return; + } + + // TODO: Handle packet fragmentation + + + Log_Debug("IPv4", " From %i.%i.%i.%i to %i.%i.%i.%i", + hdr->Source.B[0], hdr->Source.B[1], hdr->Source.B[2], hdr->Source.B[3], + hdr->Destination.B[0], hdr->Destination.B[1], hdr->Destination.B[2], hdr->Destination.B[3] + ); + + // Get Data and Data Length + dataLength = ntohs(hdr->TotalLength) - sizeof(tIPv4Header); + data = &hdr->Options[0]; + + // Get Interface (allowing broadcasts) + iface = IPv4_GetInterface(Adapter, hdr->Destination, 1); + + // Firewall rules + if( iface ) { + // Incoming Packets + ret = IPTables_TestChain("INPUT", + 4, &hdr->Source, &hdr->Destination, + hdr->Protocol, 0, + dataLength, data + ); + } + else { + // Routed packets + ret = IPTables_TestChain("FORWARD", + 4, &hdr->Source, &hdr->Destination, + hdr->Protocol, 0, + dataLength, data + ); + } + switch(ret) + { + // 0 - Allow + case 0: break; + // 1 - Silent Drop + case 1: + Log_Debug("IPv4", "Silently dropping packet"); + return ; + case -1: + // Bad rule + break ; + // Unknown, silent drop + default: + Log_Warning("IPv4", "Unknown firewall rule"); + return ; + } + + // Routing + if(!iface) + { + tMacAddr to; + tRoute *rt; + + Log_Debug("IPv4", "Route the packet"); + // Drop the packet if the TTL is zero + if( hdr->TTL == 0 ) { + Log_Warning("IPv4", "TODO: Send ICMP-Timeout when TTL exceeded"); + return ; + } + + hdr->TTL --; + + rt = IPStack_FindRoute(4, NULL, &hdr->Destination); // Get the route (gets the interface) + if( !rt || !rt->Interface ) + return ; + to = ARP_Resolve4(rt->Interface, hdr->Destination); // Resolve address + if( MAC_EQU(to, cMAC_ZERO) ) + return ; + + // Send packet + Log_Log("IPv4", "Forwarding packet to %i.%i.%i.%i (via %i.%i.%i.%i)", + hdr->Destination.B[0], hdr->Destination.B[1], + hdr->Destination.B[2], hdr->Destination.B[3], + ((tIPv4*)rt->NextHop)->B[0], ((tIPv4*)rt->NextHop)->B[1], + ((tIPv4*)rt->NextHop)->B[2], ((tIPv4*)rt->NextHop)->B[3]); + Link_SendPacket(rt->Interface->Adapter, IPV4_ETHERNET_ID, to, Length, Buffer); + + + return ; + } + + // Send it on + if( !gaIPv4_Callbacks[hdr->Protocol] ) { + Log_Log("IPv4", "Unknown Protocol %i", hdr->Protocol); + return ; + } + + gaIPv4_Callbacks[hdr->Protocol]( iface, &hdr->Source, dataLength, data ); +} + +/** + * \fn tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address) + * \brief Searches an adapter for a matching address + * \param Adapter Incoming Adapter + * \param Address Destination Address + * \param Broadcast Allow broadcast packets + */ +tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast) +{ + tInterface *iface = NULL; + Uint32 netmask; + Uint32 addr, this; + + ENTER("pAdapter xAddress bBroadcast", Adapter, Address, Broadcast); + + addr = ntohl( Address.L ); + LOG("addr = 0x%x", addr); + + for( iface = gIP_Interfaces; iface; iface = iface->Next) + { + if( iface->Adapter != Adapter ) continue; + if( iface->Type != 4 ) continue; + if( IP4_EQU(Address, *(tIPv4*)iface->Address) ) { + LOG("Exact match"); + LEAVE('p', iface); + return iface; + } + + if( !Broadcast ) continue; + + // Check for broadcast + this = ntohl( ((tIPv4*)iface->Address)->L ); + netmask = IPv4_Netmask(iface->SubnetBits); + LOG("iface addr = 0x%x, netmask = 0x%x (bits = %i)", this, netmask, iface->SubnetBits); + + if( (addr & netmask) == (this & netmask) && (addr & ~netmask) == (0xFFFFFFFF & ~netmask) ) + { + LOG("Broadcast match"); + LEAVE('p', iface); + return iface; + } + } + LEAVE('n'); + return NULL; +} + +/** + * \brief Convert a network prefix to a netmask + * \param FixedBits Netmask size (/n) + * + * For example /24 will become 255.255.255.0 (0xFFFFFF00) + */ +Uint32 IPv4_Netmask(int FixedBits) +{ + Uint32 ret = 0xFFFFFFFF; + if( FixedBits == 0 ) + return 0; + if( FixedBits < 32 ) + { + ret >>= (32-FixedBits); + ret <<= (32-FixedBits); + } + // Returns a native endian netmask + return ret; +} + +/** + * \brief Calculate the IPv4 Checksum + * \param Buf Input buffer + * \param Size Size of input + * + * One's complement sum of all 16-bit words (bitwise inverted) + */ +Uint16 IPv4_Checksum(const void *Buf, size_t Length) +{ + const Uint16 *words = Buf; + Uint32 sum = 0; + int i; + + // Sum all whole words + for(i = 0; i < Length/2; i++ ) + { + sum += ntohs(words[i]); + } + if( Length & 1 ) + sum += ntohs( words[i] & 0xFF ); + + // Apply one's complement + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return ~sum; +} + +/** + * \brief Sends an ICMP Echo and waits for a reply + * \param IFace Interface + * \param Addr Destination address + */ +int IPv4_Ping(tInterface *IFace, tIPv4 Addr) +{ + return ICMP_Ping(IFace, Addr); +} diff --git a/KernelLand/Modules/IPStack/ipv4.h b/KernelLand/Modules/IPStack/ipv4.h new file mode 100644 index 00000000..2c3e1cae --- /dev/null +++ b/KernelLand/Modules/IPStack/ipv4.h @@ -0,0 +1,52 @@ +/* + * Acess2 IP Stack + * - IPv4 Definitions + */ +#ifndef _IPV4_H_ +#define _IPV4_H_ + +#include "ipstack.h" + +typedef struct sIPv4Header tIPv4Header; + +struct sIPv4Header +{ + struct { + // Spec says Version is first, but stupid bit ordering + unsigned HeaderLength: 4; // in 4-byte chunks + unsigned Version: 4; // = 4 + } __attribute__((packed)); + Uint8 DiffServices; // Differentiated Services + Uint16 TotalLength; + Uint16 Identifcation; + + struct { + unsigned Reserved: 1; + unsigned DontFragment: 1; + unsigned MoreFragments: 1; + unsigned FragOffLow: 5; + } __attribute__((packed)); + Uint8 FragOffHi; // Number of 8-byte blocks from the original start + + Uint8 TTL; // Max number of hops, effectively + Uint8 Protocol; + Uint16 HeaderChecksum; // One's Complement Sum of the entire header must equal zero + + tIPv4 Source; + tIPv4 Destination; + + Uint8 Options[]; +} __attribute__((packed)); + +#define IP4PROT_ICMP 1 +#define IP4PROT_TCP 6 +#define IP4PROT_UDP 17 + +#define IPV4_ETHERNET_ID 0x0800 + +// === FUNCTIONS === +extern int IPv4_RegisterCallback(int ID, tIPCallback Callback); +extern Uint16 IPv4_Checksum(const void *Buf, size_t Length); +extern int IPv4_SendPacket(tInterface *Iface, tIPv4 Address, int Protocol, int ID, int Length, const void *Data); + +#endif diff --git a/KernelLand/Modules/IPStack/ipv6.c b/KernelLand/Modules/IPStack/ipv6.c new file mode 100644 index 00000000..7cb8e0a8 --- /dev/null +++ b/KernelLand/Modules/IPStack/ipv6.c @@ -0,0 +1,261 @@ +/* + * Acess2 IP Stack + * - IPv6 Protcol Handling + */ +#include "ipstack.h" +#include "link.h" +#include "ipv6.h" +#include "firewall.h" + +// === IMPORTS === +extern tInterface *gIP_Interfaces; +extern Uint32 IPv4_Netmask(int FixedBits); + +// === PROTOTYPES === + int IPv6_Initialise(); + int IPv6_RegisterCallback(int ID, tIPCallback Callback); +void IPv6_int_GetPacket(tAdapter *Interface, tMacAddr From, int Length, void *Buffer); +tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast); + +// === GLOBALS === +tIPCallback gaIPv6_Callbacks[256]; + +// === CODE === +/** + * \brief Initialise the IPv6 handling code + */ +int IPv6_Initialise() +{ + Link_RegisterType(IPV6_ETHERNET_ID, IPv6_int_GetPacket); + return 1; +} + +/** + * \brief Registers a callback + * \param ID 8-bit packet type ID + * \param Callback Callback function + */ +int IPv6_RegisterCallback(int ID, tIPCallback Callback) +{ + if( ID < 0 || ID > 255 ) return 0; + if( gaIPv6_Callbacks[ID] ) return 0; + gaIPv6_Callbacks[ID] = Callback; + return 1; +} + +/** + * \brief Creates and sends an IPv6 Packet + * \param Iface Interface + * \param Destination Destination IP + * \param Protocol Protocol ID + * \param Length Data Length + * \param Data Packet Data + * \return Boolean Success + */ +int IPv6_SendPacket(tInterface *Iface, tIPv6 Destination, int Protocol, size_t Length, const void *Data) +{ + return 0; +} + +/** + * \fn void IPv6_int_GetPacket(tInterface *Interface, tMacAddr From, int Length, void *Buffer) + * \brief Process an IPv6 Packet + * \param Interface Input interface + * \param From Source MAC address + * \param Length Packet length + * \param Buffer Packet data + */ +void IPv6_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer) +{ + tInterface *iface; + tIPv6Header *hdr = Buffer; + int ret, dataLength; + char *dataPtr; + Uint8 nextHeader; + + if(Length < sizeof(tIPv6Header)) return; + + hdr->Head = ntohl(hdr->Head); + + //if( ((hdr->Head >> (20+8)) & 0xF) != 6 ) + if( hdr->Version != 6 ) + return; + + #if 1 + Log_Debug("IPv6", "hdr = {"); + Log_Debug("IPv6", " .Version = %i", hdr->Version ); + Log_Debug("IPv6", " .TrafficClass = %i", hdr->TrafficClass ); + Log_Debug("IPv6", " .FlowLabel = %i", hdr->FlowLabel ); + Log_Debug("IPv6", " .PayloadLength = 0x%04x", ntohs(hdr->PayloadLength) ); + Log_Debug("IPv6", " .NextHeader = 0x%02x", hdr->NextHeader ); + Log_Debug("IPv6", " .HopLimit = 0x%02x", hdr->HopLimit ); + Log_Debug("IPv6", " .Source = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", hdr->Source ); + Log_Debug("IPv6", " .Destination = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", hdr->Destination ); + Log_Debug("IPv6", "}"); + #endif + + // No checksum in IPv6 + + // Check Packet length + if( ntohs(hdr->PayloadLength)+sizeof(tIPv6Header) > Length) { + Log_Log("IPv6", "hdr->PayloadLength(%i) > Length(%i)", ntohs(hdr->PayloadLength), Length); + return; + } + + // Process Options + nextHeader = hdr->NextHeader; + dataPtr = hdr->Data; + dataLength = hdr->PayloadLength; + for( ;; ) + { + struct { + Uint8 NextHeader; + Uint8 Length; // In 8-byte chunks, with 0 being 8 bytes long + Uint8 Data[]; + } *optionHdr; + optionHdr = (void*)dataPtr; + // Hop-by-hop options + if(nextHeader == 0) + { + // TODO: Parse the options (actually, RFC2460 doesn't specify any) + } + // Routing Options + else if(nextHeader == 43) + { + // TODO: Routing Header options + } + else + { + break; // Unknown, pass on + } + nextHeader = optionHdr->NextHeader; + dataPtr += (optionHdr->Length + 1) * 8; // 8-octet length (0 = 8 bytes long) + } + + // Get Interface (allowing broadcasts) + iface = IPv6_GetInterface(Adapter, hdr->Destination, 1); + + // Firewall rules + if( iface ) { + // Incoming Packets + ret = IPTables_TestChain("INPUT", + 6, &hdr->Source, &hdr->Destination, + hdr->NextHeader, 0, + hdr->PayloadLength, hdr->Data + ); + } + else { + // Routed packets + ret = IPTables_TestChain("FORWARD", + 6, &hdr->Source, &hdr->Destination, + hdr->NextHeader, 0, + hdr->PayloadLength, hdr->Data + ); + } + + switch(ret) + { + // 0 - Allow + case 0: break; + // 1 - Silent Drop + case 1: + Log_Debug("IPv6", "Silently dropping packet"); + return ; + // Unknown, silent drop + default: + return ; + } + + // Routing + if(!iface) + { + #if 0 + tMacAddr to; + tRoute *rt; + + Log_Debug("IPv6", "Route the packet"); + // Drop the packet if the TTL is zero + if( hdr->HopLimit == 0 ) { + Log_Warning("IPv6", "TODO: Sent ICMP-Timeout when TTL exceeded"); + return ; + } + + hdr->HopLimit --; + + rt = IPStack_FindRoute(6, NULL, &hdr->Destination); // Get the route (gets the interface) + to = ICMP6_ResolveHWAddr(rt->Interface, hdr->Destination); // Resolve address + + // Send packet + Log_Log("IPv6", "Forwarding packet"); + Link_SendPacket(rt->Interface->Adapter, IPV6_ETHERNET_ID, to, Length, Buffer); + #endif + + return ; + } + + // Send it on + if( !gaIPv6_Callbacks[hdr->NextHeader] ) { + Log_Log("IPv6", "Unknown Protocol %i", hdr->NextHeader); + return ; + } + + gaIPv6_Callbacks[hdr->NextHeader]( iface, &hdr->Source, hdr->PayloadLength, hdr->Data ); +} + +/** + * \fn tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address) + * \brief Searches an adapter for a matching address + * \param Adapter Source adapter + * \param Address Destination Address + * \param Broadcast Allow broadcast? + */ +tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast) +{ + int i, j; + tInterface *iface = NULL; + Uint32 netmask; + + for( iface = gIP_Interfaces; iface; iface = iface->Next) + { + tIPv6 *thisAddr; + + // Check for this adapter + if( iface->Adapter != Adapter ) continue; + + // Skip non-IPv6 Interfaces + if( iface->Type != 6 ) continue; + + thisAddr = (tIPv6*)iface->Address; + // If the address is a perfect match, return this interface + if( IP6_EQU(Address, *thisAddr) ) return iface; + + // Check if we want to match broadcast addresses + if( !Broadcast ) continue; + + // Check for broadcast + // - Check first DWORDs + if( iface->SubnetBits > 32 && Address.L[0] != thisAddr->L[0] ) + continue; + if( iface->SubnetBits > 64 && Address.L[1] != thisAddr->L[1] ) + continue; + if( iface->SubnetBits > 96 && Address.L[2] != thisAddr->L[2] ) + continue; + + // Check final DWORD + j = iface->SubnetBits / 32; + i = iface->SubnetBits % 32; + netmask = IPv4_Netmask( iface->SubnetBits % 32 ); + + // Check the last bit of the netmask + if( (Address.L[j] >> i) != (thisAddr->L[j] >> i) ) continue; + + // Check that the host portion is one + if( (Address.L[j] & ~netmask) != (0xFFFFFFFF & ~netmask) ) continue; + if( j >= 2 && Address.L[3] != 0xFFFFFFFF) continue; + if( j >= 1 && Address.L[2] != 0xFFFFFFFF) continue; + if( j >= 0 && Address.L[1] != 0xFFFFFFFF) continue; + + return iface; + } + return NULL; +} diff --git a/KernelLand/Modules/IPStack/ipv6.h b/KernelLand/Modules/IPStack/ipv6.h new file mode 100644 index 00000000..d2e4f28d --- /dev/null +++ b/KernelLand/Modules/IPStack/ipv6.h @@ -0,0 +1,42 @@ +/* + * Acess2 IP Stack + * - IPv6 Definitions + */ +#ifndef _IPV6_H_ +#define _IPV6_H_ + +#include "ipstack.h" + +typedef struct sIPv6Header tIPv6Header; + +struct sIPv6Header +{ + #if 0 + // High 4: Version + // Next 8: Traffic Class + // Low 20: Flow Label + Uint32 Head; + #else + union { + Uint32 Head; // Allow a ntohl to happen + struct { + unsigned Version: 4; + unsigned TrafficClass: 8; + unsigned FlowLabel: 20; + } PACKED; + } PACKED; + #endif + Uint16 PayloadLength; + Uint8 NextHeader; // Type of payload data + Uint8 HopLimit; + tIPv6 Source; + tIPv6 Destination; + char Data[]; +}; + +#define IPV6_ETHERNET_ID 0x86DD + +extern int IPv6_RegisterCallback(int ID, tIPCallback Callback); +extern int IPv6_SendPacket(tInterface *Iface, tIPv6 Destination, int Protocol, size_t Length, const void *Data); + +#endif diff --git a/KernelLand/Modules/IPStack/link.c b/KernelLand/Modules/IPStack/link.c new file mode 100644 index 00000000..8bca51fc --- /dev/null +++ b/KernelLand/Modules/IPStack/link.c @@ -0,0 +1,225 @@ +/* + * Acess2 IP Stack + * - Link/Media Layer Interface + */ +#include "ipstack.h" +#include "link.h" + +// === CONSTANTS === +#define MAX_PACKET_SIZE 2048 + +// === PROTOTYPES === +void Link_RegisterType(Uint16 Type, tPacketCallback Callback); +void Link_InitCRC(); +Uint32 Link_CalculateCRC(void *Data, int Length); +void Link_SendPacket(tAdapter *Adapter, Uint16 Type, tMacAddr To, int Length, void *Buffer); +void Link_WatchDevice(tAdapter *Adapter); + +// === GLOBALS === + int giRegisteredTypes = 0; + int giRegisteredTypeSpace = 0; +struct { + Uint16 Type; + tPacketCallback Callback; +} *gaRegisteredTypes; + int gbLink_CRCTableGenerated = 0; +Uint32 gaiLink_CRCTable[256]; + +// === CODE === +/** + * \fn void Link_RegisterType(Uint16 Type, tPacketCallback Callback) + * \brief Registers a callback for a specific packet type + * + * \todo Make thread safe (place a mutex on the list) + */ +void Link_RegisterType(Uint16 Type, tPacketCallback Callback) +{ + int i; + void *tmp; + + for( i = giRegisteredTypes; i -- ; ) + { + if(gaRegisteredTypes[i].Type == Type) { + Log_Warning("Net Link", "Attempt to register 0x%x twice", Type); + return ; + } + // Ooh! Free slot! + if(gaRegisteredTypes[i].Callback == NULL) break; + } + + if(i == -1) + { + giRegisteredTypeSpace += 5; + tmp = realloc(gaRegisteredTypes, giRegisteredTypeSpace*sizeof(*gaRegisteredTypes)); + if(!tmp) { + Log_Warning("Net Link", + "Out of heap space! (Attempted to allocate %i)", + giRegisteredTypeSpace*sizeof(*gaRegisteredTypes) + ); + return ; + } + gaRegisteredTypes = tmp; + i = giRegisteredTypes; + giRegisteredTypes ++; + } + + gaRegisteredTypes[i].Callback = Callback; + gaRegisteredTypes[i].Type = Type; +} + +/** + * \fn void Link_SendPacket(tAdapter *Adapter, Uint16 Type, tMacAddr To, int Length, void *Buffer) + * \brief Formats and sends a packet on the specified interface + */ +void Link_SendPacket(tAdapter *Adapter, Uint16 Type, tMacAddr To, int Length, void *Buffer) +{ + int bufSize = sizeof(tEthernetHeader) + ((Length+3)&~3) + 4; + Uint8 buf[bufSize]; // dynamic stack arrays ftw! + tEthernetHeader *hdr = (void*)buf; + + Log_Log("Net Link", "Sending %i bytes to %02x:%02x:%02x:%02x:%02x:%02x (Type 0x%x)", + Length, To.B[0], To.B[1], To.B[2], To.B[3], To.B[4], To.B[5], Type); + + hdr->Dest = To; + hdr->Src = Adapter->MacAddr; + hdr->Type = htons(Type); + + memcpy(hdr->Data, Buffer, Length); + + *(Uint32*) &hdr->Data[bufSize-4] = 0; + *(Uint32*) &hdr->Data[bufSize-4] = htonl( Link_CalculateCRC(buf, bufSize) ); + + VFS_Write(Adapter->DeviceFD, bufSize, buf); +} + +void Link_WorkerThread(void *Ptr) +{ + tAdapter *Adapter = Ptr; + + Threads_SetName(Adapter->Device); + Log_Log("Net Link", "Thread %i watching '%s'", Threads_GetTID(), Adapter->Device); + + // Child Thread + while(Adapter->DeviceFD != -1) + { + Uint8 buf[MAX_PACKET_SIZE]; + tEthernetHeader *hdr = (void*)buf; + int ret, i; + Uint32 checksum; + + // Wait for a packet (Read on a network device is blocking) + //Log_Debug("NET", "Waiting on adapter FD#0x%x", Adapter->DeviceFD); + ret = VFS_Read(Adapter->DeviceFD, MAX_PACKET_SIZE, buf); + if(ret == -1) break; + + if(ret < sizeof(tEthernetHeader)) { + Log_Log("Net Link", "Recieved an undersized packet (%i < %i)", + ret, sizeof(tEthernetHeader)); + continue; + } + + Log_Log("Net Link", + "Packet from %02x:%02x:%02x:%02x:%02x:%02x" + " to %02x:%02x:%02x:%02x:%02x:%02x (Type=%04x)", + hdr->Src.B[0], hdr->Src.B[1], hdr->Src.B[2], + hdr->Src.B[3], hdr->Src.B[4], hdr->Src.B[5], + hdr->Dest.B[0], hdr->Dest.B[1], hdr->Dest.B[2], + hdr->Dest.B[3], hdr->Dest.B[4], hdr->Dest.B[5], + ntohs(hdr->Type) + ); + checksum = *(Uint32*)&hdr->Data[ret-sizeof(tEthernetHeader)-4]; + //Log_Log("NET", "Checksum 0x%08x", checksum); + // TODO: Check checksum + + // Check if there is a registered callback for this packet type + for( i = giRegisteredTypes; i--; ) + { + if(gaRegisteredTypes[i].Type == ntohs(hdr->Type)) break; + } + // No? Ignore it + if( i == -1 ) { + Log_Log("Net Link", "Unregistered type 0x%x", ntohs(hdr->Type)); + continue; + } + + // Call the callback + gaRegisteredTypes[i].Callback( + Adapter, + hdr->Src, + ret - sizeof(tEthernetHeader), + hdr->Data + ); + } + + Log_Log("Net Link", "Watcher terminated (file closed)"); + + Threads_Exit(0, 0); +} + +/** + * \fn void Link_WatchDevice(tAdapter *Adapter) + * \brief Spawns a worker thread to watch the specified adapter + */ +void Link_WatchDevice(tAdapter *Adapter) +{ + int tid; + + if( !gbLink_CRCTableGenerated ) + Link_InitCRC(); + + tid = Proc_SpawnWorker(Link_WorkerThread, Adapter); // Create a new worker thread + + if(tid < 0) { + Log_Warning("Net Link", "Unable to create watcher thread for '%s'", Adapter->Device); + return ; + } + + Log_Log("Net Link", "Watching '%s' using tid %i", Adapter->Device, tid); +} + +// From http://www.cl.cam.ac.uk/research/srg/bluebook/21/crc/node6.html +#define QUOTIENT 0x04c11db7 +void Link_InitCRC(void) +{ + int i, j; + Uint32 crc; + + for (i = 0; i < 256; i++) + { + crc = i << 24; + for (j = 0; j < 8; j++) + { + if (crc & 0x80000000) + crc = (crc << 1) ^ QUOTIENT; + else + crc = crc << 1; + } + gaiLink_CRCTable[i] = crc; + } + + gbLink_CRCTableGenerated = 1; +} + +Uint32 Link_CalculateCRC(void *Data, int Length) +{ + // x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1 + Uint32 result; + int i; + Uint32 *data = Data; + + if(Length < 4) return 0; + + result = *data++ << 24; + result |= *data++ << 16; + result |= *data++ << 8; + result |= *data++; + result = ~ result; + Length -= 4; + + for( i = 0; i < Length; i++ ) + { + result = (result << 8 | *data++) ^ gaiLink_CRCTable[result >> 24]; + } + + return ~result; +} diff --git a/KernelLand/Modules/IPStack/link.h b/KernelLand/Modules/IPStack/link.h new file mode 100644 index 00000000..d96008d4 --- /dev/null +++ b/KernelLand/Modules/IPStack/link.h @@ -0,0 +1,29 @@ +/* + * Acess2 IP Stack + * - Link/Media Layer Header + */ +#ifndef _LINK_H_ +#define _LINK_H_ + +// === EXTERNAL === +typedef void (*tPacketCallback)(tAdapter *Interface, tMacAddr From, int Length, void *Buffer); + +extern void Link_RegisterType(Uint16 Type, tPacketCallback Callback); +extern void Link_SendPacket(tAdapter *Interface, Uint16 Type, tMacAddr To, int Length, void *Buffer); +extern void Link_WatchDevice(tAdapter *Adapter); + +// === INTERNAL === +typedef struct sEthernetHeader tEthernetHeader; +typedef struct sEthernetFooter tEthernetFooter; +struct sEthernetHeader { + tMacAddr Dest; + tMacAddr Src; + Uint16 Type; + Uint8 Data[]; +}; + +struct sEthernetFooter { + //Uint32 CRC; +}; + +#endif diff --git a/KernelLand/Modules/IPStack/main.c b/KernelLand/Modules/IPStack/main.c new file mode 100644 index 00000000..d5d1ebc2 --- /dev/null +++ b/KernelLand/Modules/IPStack/main.c @@ -0,0 +1,278 @@ +/* + * Acess2 IP Stack + * - Stack Initialisation + */ +#define DEBUG 0 +#define VERSION VER2(0,10) +#include "ipstack.h" +#include "link.h" +#include +#include + +// === IMPORTS === +extern int ARP_Initialise(); +extern void UDP_Initialise(); +extern void TCP_Initialise(); +extern int IPv4_Initialise(); +extern int IPv6_Initialise(); + +extern tAdapter *IPStack_GetAdapter(const char *Path); +extern char *IPStack_Root_ReadDir(tVFS_Node *Node, int Pos); +extern tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Name); +extern int IPStack_Root_IOCtl(tVFS_Node *Node, int ID, void *Data); +extern tInterface gIP_LoopInterface; +extern tInterface *IPStack_AddInterface(const char *Device, const char *Name); +extern tRoute *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric); + +// === PROTOTYPES === + int IPStack_Install(char **Arguments); + int IPStack_CompareAddress(int AddressType, const void *Address1, const void *Address2, int CheckBits); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, IPStack, IPStack_Install, NULL, NULL); +tVFS_NodeType gIP_RootNodeType = { + .ReadDir = IPStack_Root_ReadDir, + .FindDir = IPStack_Root_FindDir, + .IOCtl = IPStack_Root_IOCtl +}; +tDevFS_Driver gIP_DriverInfo = { + NULL, "ip", + { + .Size = -1, // Number of interfaces + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Flags = VFS_FFLAG_DIRECTORY, + .Type = &gIP_RootNodeType + } +}; + +// === CODE === +/** + * \fn int IPStack_Install(char **Arguments) + * \brief Intialise the relevant parts of the stack and register with DevFS + */ +int IPStack_Install(char **Arguments) +{ + int i = 0; + + // Layer 3 - Network Layer Protocols + ARP_Initialise(); + IPv4_Initialise(); + IPv6_Initialise(); + // Layer 4 - Transport Layer Protocols + TCP_Initialise(); + UDP_Initialise(); + + if(Arguments) + { + // Parse module arguments + for( i = 0; Arguments[i]; i++ ) + { + // TODO: + // Define interfaces by ::: + // Where: + // - is the device path (E.g. /Devices/ne2k/0) + // - is a number (e.g. 4) or symbol (e.g. AF_INET4) + // - is a condensed hexadecimal stream (in big endian) + // (E.g. 0A000201 for 10.0.2.1 IPv4) + // - is the number of subnet bits (E.g. 24 for an IPv4 Class C) + // Example: /Devices/ne2k/0:4:0A00020A:24 + // would define an interface with the address 10.0.2.10/24 + if( Arguments[i][0] == '/' ) { + // Define Interface + char *dev, *type, *addr, *bits; + + // Read definition + dev = Arguments[i]; + type = strchr(dev, ':'); + if( !type ) { + Log_Warning("IPStack", ":::"); + continue; + } + *type = '\0'; type ++; + + addr = strchr(type, ':'); + if( !addr ) { + Log_Warning("IPStack", ":::"); + continue; + } + *addr = '\0'; addr ++; + + bits = strchr(addr, ':'); + if( !bits ) { + Log_Warning("IPStack", ":::"); + continue; + } + *bits = '\0'; bits ++; + + // Define interface + { + int iType = atoi(type); + int size = IPStack_GetAddressSize(iType); + Uint8 addrData[size]; + int iBits = atoi(bits); + + UnHex(addrData, size, addr); + + tInterface *iface = IPStack_AddInterface(dev, ""); + if( !iface ) { + Log_Warning("IPStack", "Unable to add interface on '%s'", dev); + continue ; + } + iface->Type = iType; + memcpy(iface->Address, addrData, size); + iface->SubnetBits = iBits; + + // Route for addrData/iBits, no next hop, default metric + IPStack_AddRoute(iface->Name, iface->Address, iBits, NULL, 0); + + Log_Notice("IPStack", "Boot interface %s/%i on %s", + IPStack_PrintAddress(iType, addrData), iBits, + dev); + } + + continue; + } + + // I could also define routes using ::[:] + // Example: 1:00000000:0:0A000201 + if( '0' <= Arguments[i][0] && Arguments[i][0] <= '9' ) + { + // Define Interface + char *ifaceName, *network, *bits, *gateway; + + // Read definition + ifaceName = Arguments[i]; + + network = strchr(ifaceName, ':'); + if( !network ) { + Log_Warning("IPStack", ":::"); + continue; + } + *network = '\0'; network ++; + + bits = strchr(network, ':'); + if( !bits ) { + Log_Warning("IPStack", ":::"); + continue; + } + *bits = '\0'; bits ++; + + gateway = strchr(bits, ':'); + if( gateway ) { + *gateway = '\0'; gateway ++; + } + + // Define route + { + tVFS_Node *node = IPStack_Root_FindDir(NULL, ifaceName); + if( !node ) { + Log_Warning("IPStack", "Unknown interface '%s' in arg %i", ifaceName, i); + continue ; + } + tInterface *iface = node->ImplPtr; + + int size = IPStack_GetAddressSize(iface->Type); + Uint8 netData[size]; + Uint8 gwData[size]; + int iBits = atoi(bits); + + UnHex(netData, size, network); + if( gateway ) + UnHex(gwData, size, gateway); + else + memset(gwData, 0, size); + + IPStack_AddRoute(ifaceName, netData, iBits, gwData, 30); + } + + continue; + } + } + } + + // Initialise loopback interface + gIP_LoopInterface.Adapter = IPStack_GetAdapter("LOOPBACK"); + + DevFS_AddDevice( &gIP_DriverInfo ); + + return MODULE_ERR_OK; +} + +/** + * \brief Gets the size (in bytes) of a specified form of address + */ +int IPStack_GetAddressSize(int AddressType) +{ + switch(AddressType) + { + case -1: // -1 = maximum + return sizeof(tIPv6); + + case AF_NULL: + return 0; + + case AF_INET4: + return sizeof(tIPv4); + case AF_INET6: + return sizeof(tIPv6); + + default: + return 0; + } +} + +/** + * \brief Compare two IP Addresses masked by CheckBits + */ +int IPStack_CompareAddress(int AddressType, const void *Address1, const void *Address2, int CheckBits) +{ + int size = IPStack_GetAddressSize(AddressType); + Uint8 mask; + const Uint8 *addr1 = Address1, *addr2 = Address2; + + // Sanity check size + if( CheckBits < 0 ) CheckBits = size*8; + if( CheckBits > size*8 ) CheckBits = size*8; + + if( CheckBits == 0 ) return 1; // /0 matches anything + + // Check first bits/8 bytes + if( memcmp(Address1, Address2, CheckBits/8) != 0 ) return 0; + + // Check if the mask is a multiple of 8 + if( CheckBits % 8 == 0 ) return 1; + + // Check last bits + mask = 0xFF << (8 - (CheckBits % 8)); + if( (addr1[CheckBits/8] & mask) == (addr2[CheckBits/8] & mask) ) + return 1; + + return 0; +} + +const char *IPStack_PrintAddress(int AddressType, const void *Address) +{ + switch( AddressType ) + { + case 4: { + static char ret[4*3+3+1]; + const Uint8 *addr = Address; + sprintf(ret, "%i.%i.%i.%i", addr[0], addr[1], addr[2], addr[3]); + return ret; + } + + case 6: { // TODO: address compression + static char ret[8*4+7+1]; + const Uint16 *addr = Address; + sprintf(ret, "%x:%x:%x:%x:%x:%x:%x:%x", + ntohs(addr[0]), ntohs(addr[1]), ntohs(addr[2]), ntohs(addr[3]), + ntohs(addr[4]), ntohs(addr[5]), ntohs(addr[6]), ntohs(addr[7]) + ); + return ret; + } + + default: + return ""; + } +} diff --git a/KernelLand/Modules/IPStack/routing.c b/KernelLand/Modules/IPStack/routing.c new file mode 100644 index 00000000..7173d1f3 --- /dev/null +++ b/KernelLand/Modules/IPStack/routing.c @@ -0,0 +1,623 @@ +/* + * Acess2 IP Stack + * - Routing Tables + */ +#define DEBUG 0 +#define VERSION VER2(0,10) +#include +#include +#include "ipstack.h" +#include "link.h" + +#define DEFAUTL_METRIC 30 + +// === IMPORTS === +extern tInterface *gIP_Interfaces; +extern tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Filename); + +// === PROTOTYPES === +// - Routes directory +char *IPStack_RouteDir_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *IPStack_RouteDir_FindDir(tVFS_Node *Node, const char *Name); + int IPStack_RouteDir_MkNod(tVFS_Node *Node, const char *Name, Uint Flags); + int IPStack_RouteDir_Relink(tVFS_Node *Node, const char *OldName, const char *NewName); +tRoute *_Route_FindExactRoute(int Type, void *Network, int Subnet, int Metric); + int _Route_ParseRouteName(const char *Name, void *Addr, int *SubnetBits, int *Metric); + int IPStack_RouteDir_IOCtl(tVFS_Node *Node, int ID, void *Data); +// - Route Management +tRoute *IPStack_Route_Create(int AddrType, void *Network, int SubnetBits, int Metric); +tRoute *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric); +tRoute *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address); +// - Individual Routes + int IPStack_Route_IOCtl(tVFS_Node *Node, int ID, void *Data); + +// === GLOBALS === + int giIP_NextRouteId = 1; +tRoute *gIP_Routes; +tRoute *gIP_RoutesEnd; +tVFS_NodeType gIP_RouteNodeType = { + .IOCtl = IPStack_Route_IOCtl +}; +tVFS_NodeType gIP_RouteDirNodeType = { + .ReadDir = IPStack_RouteDir_ReadDir, + .FindDir = IPStack_RouteDir_FindDir, + .MkNod = IPStack_RouteDir_MkNod, + .Relink = IPStack_RouteDir_Relink, + .IOCtl = IPStack_RouteDir_IOCtl +}; +tVFS_Node gIP_RouteNode = { + .Flags = VFS_FFLAG_DIRECTORY, + .Size = -1, + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Type = &gIP_RouteDirNodeType +}; + +// === CODE === +/** + * \brief ReadDir for the /Devices/ip/routes/ directory + */ +char *IPStack_RouteDir_ReadDir(tVFS_Node *Node, int Pos) +{ + tRoute *rt; + + for(rt = gIP_Routes; rt && Pos --; rt = rt->Next); + if( !rt ) return NULL; + + { + int addrlen = IPStack_GetAddressSize(rt->AddressType); + int len = sprintf(NULL, "%i::%i:%i", rt->AddressType, rt->SubnetBits, rt->Metric) + addrlen*2; + char buf[len+1]; + int ofs; + ofs = sprintf(buf, "%i:", rt->AddressType); + ofs += Hex(buf+ofs, addrlen, rt->Network); + sprintf(buf+ofs, ":%i:%i", rt->SubnetBits, rt->Metric); + return strdup(buf); + } +} + +/** + * \brief FindDir for the /Devices/ip/routes/ directory + */ +tVFS_Node *IPStack_RouteDir_FindDir(tVFS_Node *Node, const char *Name) +{ + // Interpret the name as :, returning the interface for + // needed to access that address. + // E.g. '@4:0A02000A' - 10.2.0.10 + // Hm... It could do with a way to have a better address type representation + if( Name[0] == '@' ) + { + tRoute *rt; + int ofs = 1; + int type; + + ofs += ParseInt(Name+ofs, &type); + int addrSize = IPStack_GetAddressSize(type); + Uint8 addrData[addrSize]; + + // Separator + if( Name[ofs] != ':' ) return NULL; + ofs ++; + + // Error if the size is invalid + if( strlen(Name+ofs) != addrSize*2 ) + return NULL; + + // Parse the address + // - Error if the address data is not fully hex + if( UnHex(addrData, addrSize, Name + ofs) != addrSize ) + return NULL; + + // Find the route + rt = IPStack_FindRoute(type, NULL, addrData); + if(!rt) return NULL; + + if( rt->Interface ) + { + // Return the interface node + // - Sure it's hijacking it from inteface.c's area, but it's + // simpler this way + return &rt->Interface->Node; + } + else + { + return NULL; + } + } + else if( Name[0] == '#' ) + { + int num, ofs = 1; + + ofs = ParseInt(Name+ofs, &num); + if( ofs == 1 ) return NULL; + if( Name[ofs] != '\0' ) return NULL; + if( num < 0) return NULL; + + for( tRoute *rt = gIP_Routes; rt; rt = rt->Next ) + { + if( rt->Node.Inode > num ) return NULL; + if( rt->Node.Inode == num ) return &rt->Node; + } + return NULL; + } + else + { + int type = _Route_ParseRouteName(Name, NULL, NULL, NULL); + if( type <= 0 ) return NULL; + + int addrSize = IPStack_GetAddressSize(type); + Uint8 addrData[addrSize]; + int subnet_bits, metric; + + _Route_ParseRouteName(Name, addrData, &subnet_bits, &metric); + + tRoute *rt = _Route_FindExactRoute(type, addrData, subnet_bits, metric); + if(rt) return &rt->Node; + return NULL; + } +} + +/** + * \brief Create a new route node + */ +int IPStack_RouteDir_MkNod(tVFS_Node *Node, const char *Name, Uint Flags) +{ + if( Flags ) return -EINVAL; + if( Threads_GetUID() != 0 ) return -EACCES; + + int type = _Route_ParseRouteName(Name, NULL, NULL, NULL); + if( type <= 0 ) return -EINVAL; + + int size = IPStack_GetAddressSize(type); + Uint8 addrdata[size]; + int subnet, metric; + + _Route_ParseRouteName(Name, addrdata, &subnet, &metric); + + // Check for duplicates + if( _Route_FindExactRoute(type, addrdata, subnet, metric) ) + return -EEXIST; + + IPStack_Route_Create(type, addrdata, subnet, metric); + + return 0; +} + +/** + * \brief Rename / Delete a route + */ +int IPStack_RouteDir_Relink(tVFS_Node *Node, const char *OldName, const char *NewName) +{ + tRoute *rt; + + if( Threads_GetUID() != 0 ) return -EACCES; + + // Get the original route entry + { + int type = _Route_ParseRouteName(OldName, NULL, NULL, NULL); + if(type <= 0) return -EINVAL; + Uint8 addr[IPStack_GetAddressSize(type)]; + int subnet, metric; + _Route_ParseRouteName(OldName, addr, &subnet, &metric); + + rt = _Route_FindExactRoute(type, addr, subnet, metric); + } + + if( NewName == NULL ) + { + // Delete the route + tRoute *prev = NULL; + for(tRoute *r = gIP_Routes; r && r != rt; prev = r, r = r->Next); + + if(prev) + prev->Next = rt->Next; + else + gIP_Routes = rt->Next; + free(rt); + } + else + { + // Change the route + int type = _Route_ParseRouteName(NewName, NULL, NULL, NULL); + if(type <= 0) return -EINVAL; + Uint8 addr[IPStack_GetAddressSize(type)]; + int subnet, metric; + _Route_ParseRouteName(NewName, addr, &subnet, &metric); + + return -ENOTIMPL; + } + return 0; +} + +tRoute *_Route_FindExactRoute(int Type, void *Network, int Subnet, int Metric) +{ + for(tRoute *rt = gIP_Routes; rt; rt = rt->Next) + { + if( rt->AddressType != Type ) continue; + if( rt->Metric != Metric ) continue; + if( rt->SubnetBits != Subnet ) continue; + if( IPStack_CompareAddress(Type, rt->Network, Network, -1) == 0 ) + continue ; + return rt; + } + return NULL; +} + +int _Route_ParseRouteName(const char *Name, void *Addr, int *SubnetBits, int *Metric) +{ + int type, addrlen; + int ofs = 0, ilen; + + ENTER("sName pAddr pSubnetBits pMetric", Name, Addr, SubnetBits, Metric); + + ilen = ParseInt(Name, &type); + if(ilen == 0) { + LOG("Type failed to parse"); + LEAVE_RET('i', -1); + } + ofs += ilen; + + if(Name[ofs] != ':') LEAVE_RET('i', -1); + ofs ++; + + addrlen = IPStack_GetAddressSize(type); + if( Addr ) + { + if( UnHex(Addr, addrlen, Name + ofs) != addrlen ) + return -1; + } + ofs += addrlen*2; + + if(Name[ofs] != ':') LEAVE_RET('i', -1); + ofs ++; + + ilen = ParseInt(Name+ofs, SubnetBits); + if(ilen == 0) { + LOG("Subnet failed to parse"); + LEAVE_RET('i', -1); + } + ofs += ilen; + + if(Name[ofs] != ':') LEAVE_RET('i', -1); + ofs ++; + + ilen = ParseInt(Name+ofs, Metric); + if(ilen == 0) { + LOG("Metric failed to parse"); + LEAVE_RET('i', -1); + } + ofs += ilen; + + if(Name[ofs] != '\0') LEAVE_RET('i', -1); + + LEAVE('i', type); + return type; +} + +/** + * \brief Names for the route list IOCtl Calls + */ +static const char *casIOCtls_RouteDir[] = { + DRV_IOCTLNAMES, + "locate_route", // Find the best route for an address - struct {int Type, char Address[]} * + NULL + }; + +/** + * \brief IOCtl for /Devices/ip/routes/ + */ +int IPStack_RouteDir_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + tRoute *rt; + ENTER("pNode iID pData", Node, ID, Data); + switch(ID) + { + // --- Standard IOCtls (0-3) --- + BASE_IOCTLS(DRV_TYPE_MISC, STR(IDENT), VERSION, casIOCtls_RouteDir) + + case 4: // Locate Route + { + struct { + int Type; + Uint8 Addr[]; + } *data = Data; + + if( !CheckMem(Data, sizeof(int)) ) + LEAVE_RET('i', -1); + if( !CheckMem(Data, sizeof(int) + IPStack_GetAddressSize(data->Type)) ) + LEAVE_RET('i', -1); + + Log_Debug("IPStack", "Route_RouteDir_IOCtl - FindRoute %i, %s", + data->Type, IPStack_PrintAddress(data->Type, data->Addr) ); + rt = IPStack_FindRoute(data->Type, NULL, data->Addr); + + if( !rt ) + LEAVE_RET('i', 0); + + LEAVE('i', rt->Node.Inode); + return rt->Node.Inode; + } + break; + } + LEAVE('i', 0); + return 0; +} + +/** + * \brief Create a new route entry + * \param InterfaceName Name of the interface using this route + */ +tRoute *IPStack_Route_Create(int AddrType, void *Network, int SubnetBits, int Metric) +{ + tRoute *rt; + int size; + + // Get the size of the specified address type + size = IPStack_GetAddressSize(AddrType); + if( size == 0 ) { + return NULL; + } + + // Allocate space + rt = calloc(1, sizeof(tRoute) + size*2 ); + + // Set up node + rt->Node.ImplPtr = rt; + rt->Node.Inode = giIP_NextRouteId ++; + rt->Node.Size = 0; + rt->Node.NumACLs = 1, + rt->Node.ACLs = &gVFS_ACL_EveryoneRO; + rt->Node.Type = &gIP_RouteNodeType; + + // Set up state + rt->AddressType = AddrType; + rt->Network = (void *)( (tVAddr)rt + sizeof(tRoute) ); + rt->SubnetBits = SubnetBits; + rt->NextHop = (void *)( (tVAddr)rt + sizeof(tRoute) + size ); + rt->Interface = NULL; + rt->Metric = Metric; + memcpy(rt->Network, Network, size); + memset(rt->NextHop, 0, size); + + // Clear non-fixed bits + { + Uint8 *data = rt->Network; + int i; + i = SubnetBits / 8; + if( SubnetBits % 8 ) { + data[i] &= ~((1 << (8 - SubnetBits % 8)) - 1); + i ++; + } + memset(data + i, 0, size - i); + } + + + // Add to list + if( gIP_RoutesEnd ) { + gIP_RoutesEnd->Next = rt; + gIP_RoutesEnd = rt; + } + else { + gIP_Routes = gIP_RoutesEnd = rt; + } + +// Log_Log("IPStack", "Route entry for '%s' created", InterfaceName); + + return rt; +} + +/** + * \brief Add and fill a route + */ +tRoute *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric) +{ + tInterface *iface; + tRoute *rt; + int addrSize; + + { + tVFS_Node *tmp; + tmp = IPStack_Root_FindDir(NULL, Interface); + if(!tmp) return NULL; + iface = tmp->ImplPtr; + if(tmp->Type->Close) tmp->Type->Close(tmp); + } + + rt = IPStack_Route_Create(iface->Type, Network, SubnetBits, Metric); + if( !rt ) return NULL; + + addrSize = IPStack_GetAddressSize(iface->Type); + rt->Interface = iface; + + if( NextHop ) + memcpy(rt->NextHop, NextHop, addrSize); + + return rt; +} + +/** + */ +tRoute *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address) +{ + tRoute *rt; + tRoute *best = NULL; + tInterface *iface; + int addrSize; + + ENTER("iAddressType pInterface sAddress", + AddressType, Interface, IPStack_PrintAddress(AddressType, Address)); + + if( Interface && AddressType != Interface->Type ) { + LOG("Interface->Type (%i) != AddressType", Interface->Type); + LEAVE('n'); + return NULL; + } + + // Get address size + addrSize = IPStack_GetAddressSize(AddressType); + + // Check against explicit routes + for( rt = gIP_Routes; rt; rt = rt->Next ) + { + // Check interface + if( Interface && rt->Interface != Interface ) continue; + // Check address type + if( rt->AddressType != AddressType ) continue; + + LOG("Checking network %s/%i", IPStack_PrintAddress(AddressType, rt->Network), rt->SubnetBits); + + // Check if the address matches + if( !IPStack_CompareAddress(AddressType, rt->Network, Address, rt->SubnetBits) ) + continue; + + if( best ) { + // More direct routes are preferred + if( best->SubnetBits > rt->SubnetBits ) { + LOG("Skipped - less direct (%i < %i)", rt->SubnetBits, best->SubnetBits); + continue; + } + // If equally direct, choose the best metric + if( best->SubnetBits == rt->SubnetBits && best->Metric < rt->Metric ) { + LOG("Skipped - higher metric (%i > %i)", rt->Metric, best->Metric); + continue; + } + } + + best = rt; + } + + // Check against implicit routes + if( !best && !Interface ) + { + for( iface = gIP_Interfaces; iface; iface = iface->Next ) + { + if( Interface && iface != Interface ) continue; + if( iface->Type != AddressType ) continue; + + + // Check if the address matches + if( !IPStack_CompareAddress(AddressType, iface->Address, Address, iface->SubnetBits) ) + continue; + + if( best ) { + // More direct routes are preferred + if( best->SubnetBits > rt->SubnetBits ) { + LOG("Skipped - less direct (%i < %i)", rt->SubnetBits, best->SubnetBits); + continue; + } + // If equally direct, choose the best metric + if( best->SubnetBits == rt->SubnetBits && best->Metric < rt->Metric ) { + LOG("Skipped - higher metric (%i > %i)", rt->Metric, best->Metric); + continue; + } + } + + rt = &iface->Route; + memcpy(rt->Network, iface->Address, addrSize); + memset(rt->NextHop, 0, addrSize); + rt->Metric = DEFAUTL_METRIC; + rt->SubnetBits = iface->SubnetBits; + + best = rt; + } + } + if( !best && Interface ) + { + rt = &Interface->Route; + // Make sure route is up to date + memcpy(rt->Network, Interface->Address, addrSize); + memset(rt->NextHop, 0, addrSize); + rt->Metric = DEFAUTL_METRIC; + rt->SubnetBits = Interface->SubnetBits; + + if( IPStack_CompareAddress(AddressType, rt->Network, Address, rt->SubnetBits) ) + { + best = rt; + } + } + + LEAVE('p', best); + return best; +} + +/** + * \brief Names for route IOCtl Calls + */ +static const char *casIOCtls_Route[] = { + DRV_IOCTLNAMES, + "get_nexthop", // Get next hop - (void *Data), returns boolean success + "set_nexthop", // Set next hop - (void *Data), returns boolean success + "get_interface", // Get interface name - (char *Name), returns name length, NULL OK + "set_interface", // Set interface - (const char *Name) + NULL + }; + +/** + * \brief IOCtl for /Devices/ip/routes/# + */ +int IPStack_Route_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + tRoute *rt = Node->ImplPtr; + int addrSize = IPStack_GetAddressSize(rt->AddressType); + + switch(ID) + { + // --- Standard IOCtls (0-3) --- + BASE_IOCTLS(DRV_TYPE_MISC, STR(IDENT), VERSION, casIOCtls_Route) + + // Get Next Hop + case 4: + if( !CheckMem(Data, addrSize) ) return -1; + memcpy(Data, rt->NextHop, addrSize); + return 1; + // Set Next Hop + case 5: + if( Threads_GetUID() != 0 ) return -1; + if( !CheckMem(Data, addrSize) ) return -1; + memcpy(rt->NextHop, Data, addrSize); + return 1; + + // Get interface name + case 6: + if( !rt->Interface ) { + if(Data && !CheckMem(Data, 1) ) + return -1; + if(Data) + *(char*)Data = 0; + return 0; + } + if( Data ) { + if( !CheckMem(Data, strlen(rt->Interface->Name) + 1) ) + return -1; + strcpy(Data, rt->Interface->Name); + } + return strlen(rt->Interface->Name); + // Set interface name + case 7: + if( Threads_GetUID() != 0 ) + return -1; + if( !CheckString(Data) ) + return -1; + else + { + tInterface *iface; + tVFS_Node *tmp; + tmp = IPStack_Root_FindDir(NULL, Data); + if(!tmp) + return -1; + iface = tmp->ImplPtr; + if(tmp->Type->Close) tmp->Type->Close(tmp); + + if( iface->Type != rt->AddressType ) + return -1; + + // TODO: Other checks? + + rt->Interface = iface; + } + return 0; + + default: + return -1; + } +} diff --git a/KernelLand/Modules/IPStack/sctp.c b/KernelLand/Modules/IPStack/sctp.c new file mode 100644 index 00000000..d130933f --- /dev/null +++ b/KernelLand/Modules/IPStack/sctp.c @@ -0,0 +1,598 @@ +/* + * Acess2 IP Stack + * - SCTP (Stream Control Transmission Protocol) Handling + */ +#include "ipstack.h" +#include +#include "sctp.h" + +#define SCTP_ALLOC_BASE 0xC000 + +// === PROTOTYPES === +void SCTP_Initialise(); +void SCTP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer); +void SCTP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer); +void SCTP_SendPacket(tSCTPChannel *Channel, void *Data, size_t Length); +// --- Listening Server +tVFS_Node *SCTP_Server_Init(tInterface *Interface); +char *SCTP_Server_ReadDir(tVFS_Node *Node, int ID); +tVFS_Node *SCTP_Server_FindDir(tVFS_Node *Node, const char *Name); + int SCTP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data); +void SCTP_Server_Close(tVFS_Node *Node); +// --- Client Channels +tVFS_Node *SCTP_Channel_Init(tInterface *Interface); +Uint64 SCTP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 SCTP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + int SCTP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data); +void SCTP_Channel_Close(tVFS_Node *Node); +// --- Helpers +Uint16 SCTP_int_AllocatePort(); + int SCTP_int_MarkPortAsUsed(Uint16 Port); +void SCTP_int_FreePort(Uint16 Port); + +// === GLOBALS === +tMutex glSCTP_Servers; +tSCTPServer *gpSCTP_Servers; + +tMutex glSCTP_Channels; +tSCTPChannel *gpSCTP_Channels; + +tMutex glSCTP_Ports; +Uint32 gSCTP_Ports[0x10000/32]; + +tSocketFile gSCTP_ServerFile = {NULL, "sctps", SCTP_Server_Init}; +tSocketFile gSCTP_ClientFile = {NULL, "sctpc", SCTP_Channel_Init}; + +// === CODE === +/** + * \fn void TCP_Initialise() + * \brief Initialise the TCP Layer + */ +void SCTP_Initialise() +{ + IPStack_AddFile(&gSCTP_ServerFile); + IPStack_AddFile(&gSCTP_ClientFile); + //IPv4_RegisterCallback(IP4PROT_SCTP, SCTP_GetPacket, SCTP_Unreachable); + IPv4_RegisterCallback(IP4PROT_SCTP, SCTP_GetPacket); +} + +/** + * \brief Scan a list of tSCTPChannels and find process the first match + * \return 0 if no match was found, -1 on error and 1 if a match was found + */ +int SCTP_int_ScanList(tSCTPChannel *List, tInterface *Interface, void *Address, int Length, void *Buffer) +{ + tSCTPHeader *hdr = Buffer; + tSCTPChannel *chan; + tSCTPPacket *pack; + int len; + + for(chan = List; + chan; + chan = chan->Next) + { + if(chan->Interface != Interface) continue; + if(chan->LocalPort != ntohs(hdr->DestPort)) continue; + if(chan->RemotePort != ntohs(hdr->SourcePort)) continue; + + if(Interface->Type == 4) { + if(!IP4_EQU(chan->RemoteAddr.v4, *(tIPv4*)Address)) continue; + } + else if(Interface->Type == 6) { + if(!IP6_EQU(chan->RemoteAddr.v6, *(tIPv6*)Address)) continue; + } + else { + Log_Warning("SCTP", "Address type %i unknown", Interface->Type); + Mutex_Release(&glSCTP_Channels); + return -1; + } + + Log_Log("SCTP", "Recieved packet for %p", chan); + // Create the cached packet + len = ntohs(hdr->Length); + pack = malloc(sizeof(tSCTPPacket) + len); + pack->Next = NULL; + pack->Length = len; + memcpy(pack->Data, hdr->Data, len); + + // Add the packet to the channel's queue + SHORTLOCK(&chan->lQueue); + if(chan->Queue) + chan->QueueEnd->Next = pack; + else + chan->QueueEnd = chan->Queue = pack; + SHORTREL(&chan->lQueue); + Mutex_Release(&glSCTP_Channels); + return 1; + } + return 0; +} + +/** + * \fn void SCTP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) + * \brief Handles a packet from the IP Layer + */ +void SCTP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) +{ + tSCTPHeader *hdr = Buffer; + tSCTPServer *srv; + int ret; + + Log_Log("SCTP", "hdr->SourcePort = %i", ntohs(hdr->SourcePort)); + Log_Log("SCTP", "hdr->DestPort = %i", ntohs(hdr->DestPort)); + Log_Log("SCTP", "hdr->VerifcationTag = %i", ntohs(hdr->VerifcationTag)); + Log_Log("SCTP", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum)); + + // Check registered connections + Mutex_Acquire(&glSCTP_Channels); + ret = SCTP_int_ScanList(gpSCTP_Channels, Interface, Address, Length, Buffer); + Mutex_Release(&glSCTP_Channels); + if(ret != 0) return ; + + + // TODO: Server/Listener + Mutex_Acquire(&glSCTP_Servers); + for(srv = gpSCTP_Servers; + srv; + srv = srv->Next) + { + if(srv->Interface != Interface) continue; + if(srv->ListenPort != ntohs(hdr->DestPort)) continue; + ret = SCTP_int_ScanList(srv->Channels, Interface, Address, Length, Buffer); + if(ret != 0) break; + + // Add connection + Log_Warning("SCTP", "TODO - Add channel on connection"); + //TODO + } + Mutex_Release(&glSCTP_Servers); + +} + +/** + * \brief Handle an ICMP Unrechable Error + */ +void SCTP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer) +{ + +} + +/** + * \brief Send a packet + * \param Channel Channel to send the packet from + * \param Data Packet data + * \param Length Length in bytes of packet data + */ +void SCTP_SendPacket(tSCTPChannel *Channel, void *Data, size_t Length) +{ + tSCTPHeader *hdr; + + switch(Channel->Interface->Type) + { + case 4: + // Create the packet + hdr = malloc(sizeof(tSCTPHeader)+Length); + hdr->SourcePort = htons( Channel->LocalPort ); + hdr->DestPort = htons( Channel->RemotePort ); + hdr->Length = htons( sizeof(tSCTPHeader) + Length ); + hdr->Checksum = 0; // Checksum can be zero on IPv4 + memcpy(hdr->Data, Data, Length); + // Pass on the the IPv4 Layer + IPv4_SendPacket(Channel->Interface, Channel->RemoteAddr.v4, IP4PROT_SCTP, 0, sizeof(tSCTPHeader)+Length, hdr); + // Free allocated packet + free(hdr); + break; + } +} + +// --- Listening Server +tVFS_Node *SCTP_Server_Init(tInterface *Interface) +{ + tSCTPServer *new; + new = calloc( sizeof(tSCTPServer), 1 ); + if(!new) return NULL; + + new->Node.ImplPtr = new; + new->Node.Flags = VFS_FFLAG_DIRECTORY; + new->Node.NumACLs = 1; + new->Node.ACLs = &gVFS_ACL_EveryoneRX; + new->Node.ReadDir = SCTP_Server_ReadDir; + new->Node.FindDir = SCTP_Server_FindDir; + new->Node.IOCtl = SCTP_Server_IOCtl; + new->Node.Close = SCTP_Server_Close; + + Mutex_Acquire(&glSCTP_Servers); + new->Next = gpSCTP_Servers; + gpSCTP_Servers = new; + Mutex_Release(&glSCTP_Servers); + + return &new->Node; +} + +/** + * \brief Wait for a connection and return its ID in a string + */ +char *SCTP_Server_ReadDir(tVFS_Node *Node, int ID) +{ + tSCTPServer *srv = Node->ImplPtr; + tSCTPChannel *chan; + char *ret; + + if( srv->ListenPort == 0 ) return NULL; + + // Lock (so another thread can't collide with us here) and wait for a connection + Mutex_Acquire( &srv->Lock ); + while( srv->NewChannels == NULL ) Threads_Yield(); + // Pop the connection off the new list + chan = srv->NewChannels; + srv->NewChannels = chan->Next; + // Release the lock + Mutex_Release( &srv->Lock ); + + // Create the ID string and return it + ret = malloc(11+1); + sprintf(ret, "%i", chan->Node.ImplInt); + + return ret; +} + +/** + * \brief Take a string and find the channel + */ +tVFS_Node *SCTP_Server_FindDir(tVFS_Node *Node, const char *Name) +{ + tSCTPServer *srv = Node->ImplPtr; + tSCTPChannel *chan; + int id = atoi(Name); + + for(chan = srv->Channels; + chan; + chan = chan->Next) + { + if( chan->Node.ImplInt < id ) continue; + if( chan->Node.ImplInt > id ) break; // Go sorted lists! + + return &chan->Node; + } + + return NULL; +} + +/** + * \brief Names for server IOCtl Calls + */ +static const char *casIOCtls_Server[] = { + DRV_IOCTLNAMES, + "getset_listenport", + NULL + }; +/** + * \brief Channel IOCtls + */ +int SCTP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + tSCTPServer *srv = Node->ImplPtr; + + ENTER("pNode iID pData", Node, ID, Data); + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_MISC, "SCTP Server", 0x100, casIOCtls_Server); + + case 4: // getset_localport (returns bool success) + if(!Data) LEAVE_RET('i', srv->ListenPort); + if(!CheckMem( Data, sizeof(Uint16) ) ) { + LOG("Invalid pointer %p", Data); + LEAVE_RET('i', -1); + } + // Set port + srv->ListenPort = *(Uint16*)Data; + // Permissions check (Ports lower than 1024 are root-only) + if(srv->ListenPort != 0 && srv->ListenPort < 1024) { + if( Threads_GetUID() != 0 ) { + LOG("Attempt by non-superuser to listen on port %i", srv->ListenPort); + srv->ListenPort = 0; + LEAVE_RET('i', -1); + } + } + // Allocate a random port if requested + if( srv->ListenPort == 0 ) + srv->ListenPort = SCTP_int_AllocatePort(); + else + { + // Else, mark the requested port as used + if( SCTP_int_MarkPortAsUsed(srv->ListenPort) == 0 ) { + LOG("Port %i us currently in use", srv->ListenPort); + srv->ListenPort = 0; + LEAVE_RET('i', -1); + } + LEAVE_RET('i', 1); + } + LEAVE_RET('i', 1); + + default: + LEAVE_RET('i', -1); + } + LEAVE_RET('i', 0); +} + +void SCTP_Server_Close(tVFS_Node *Node) +{ + tSCTPServer *srv = Node->ImplPtr; + tSCTPServer *prev; + tSCTPChannel *chan; + tSCTPPacket *tmp; + + + // Remove from the main list first + Mutex_Acquire(&glSCTP_Servers); + if(gpSCTP_Servers == srv) + gpSCTP_Servers = gpSCTP_Servers->Next; + else + { + for(prev = gpSCTP_Servers; + prev->Next && prev->Next != srv; + prev = prev->Next); + if(!prev->Next) + Log_Warning("SCTP", "Bookeeping Fail, server %p is not in main list", srv); + else + prev->Next = prev->Next->Next; + } + Mutex_Release(&glSCTP_Servers); + + + Mutex_Acquire(&srv->Lock); + for(chan = srv->Channels; + chan; + chan = chan->Next) + { + // Clear Queue + SHORTLOCK(&chan->lQueue); + while(chan->Queue) + { + tmp = chan->Queue; + chan->Queue = tmp->Next; + free(tmp); + } + SHORTREL(&chan->lQueue); + + // Free channel structure + free(chan); + } + Mutex_Release(&srv->Lock); + + free(srv); +} + +// --- Client Channels +tVFS_Node *SCTP_Channel_Init(tInterface *Interface) +{ + tSCTPChannel *new; + new = calloc( sizeof(tSCTPChannel), 1 ); + new->Interface = Interface; + new->Node.ImplPtr = new; + new->Node.NumACLs = 1; + new->Node.ACLs = &gVFS_ACL_EveryoneRW; + new->Node.Read = SCTP_Channel_Read; + new->Node.Write = SCTP_Channel_Write; + new->Node.IOCtl = SCTP_Channel_IOCtl; + new->Node.Close = SCTP_Channel_Close; + + Mutex_Acquire(&glSCTP_Channels); + new->Next = gpSCTP_Channels; + gpSCTP_Channels = new; + Mutex_Release(&glSCTP_Channels); + + return &new->Node; +} + +/** + * \brief Read from the channel file (wait for a packet) + */ +Uint64 SCTP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tSCTPChannel *chan = Node->ImplPtr; + tSCTPPacket *pack; + + if(chan->LocalPort == 0) return 0; + if(chan->RemotePort == 0) return 0; + + while(chan->Queue == NULL) Threads_Yield(); + + for(;;) + { + SHORTLOCK(&chan->lQueue); + if(chan->Queue == NULL) { + SHORTREL(&chan->lQueue); + continue; + } + pack = chan->Queue; + chan->Queue = pack->Next; + if(!chan->Queue) chan->QueueEnd = NULL; + SHORTREL(&chan->lQueue); + break; + } + + // Clip length to packet length + if(Length > pack->Length) Length = pack->Length; + // Copy packet data from cache + memcpy(Buffer, pack->Data, Length); + // Free cached packet + free(pack); + + return Length; +} + +/** + * \brief Write to the channel file (send a packet) + */ +Uint64 SCTP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tSCTPChannel *chan = Node->ImplPtr; + if(chan->RemotePort == 0) return 0; + + SCTP_SendPacket(chan, Buffer, (size_t)Length); + + return 0; +} + +/** + * \brief Names for channel IOCtl Calls + */ +static const char *casIOCtls_Channel[] = { + DRV_IOCTLNAMES, + "getset_localport", + "getset_remoteport", + "set_remoteaddr", + NULL + }; +/** + * \brief Channel IOCtls + */ +int SCTP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + tSCTPChannel *chan = Node->ImplPtr; + ENTER("pNode iID pData", Node, ID, Data); + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_MISC, "SCTP Channel", 0x100, casIOCtls_Channel); + + case 4: // getset_localport (returns bool success) + if(!Data) LEAVE_RET('i', chan->LocalPort); + if(!CheckMem( Data, sizeof(Uint16) ) ) { + LOG("Invalid pointer %p", Data); + LEAVE_RET('i', -1); + } + // Set port + chan->LocalPort = *(Uint16*)Data; + // Permissions check (Ports lower than 1024 are root-only) + if(chan->LocalPort != 0 && chan->LocalPort < 1024) { + if( Threads_GetUID() != 0 ) { + LOG("Attempt by non-superuser to listen on port %i", chan->LocalPort); + chan->LocalPort = 0; + LEAVE_RET('i', -1); + } + } + // Allocate a random port if requested + if( chan->LocalPort == 0 ) + chan->LocalPort = SCTP_int_AllocatePort(); + else + { + // Else, mark the requested port as used + if( SCTP_int_MarkPortAsUsed(chan->LocalPort) == 0 ) { + LOG("Port %i us currently in use", chan->LocalPort); + chan->LocalPort = 0; + LEAVE_RET('i', 0); + } + LEAVE_RET('i', 1); + } + LEAVE_RET('i', 1); + + case 5: // getset_remoteport (returns bool success) + if(!Data) LEAVE_RET('i', chan->RemotePort); + if(!CheckMem( Data, sizeof(Uint16) ) ) { + LOG("Invalid pointer %p", Data); + LEAVE_RET('i', -1); + } + chan->RemotePort = *(Uint16*)Data; + return 1; + + case 6: // set_remoteaddr (returns bool success) + switch(chan->Interface->Type) + { + case 4: + if(!CheckMem(Data, sizeof(tIPv4))) { + LOG("Invalid pointer %p", Data); + LEAVE_RET('i', -1); + } + chan->RemoteAddr.v4 = *(tIPv4*)Data; + break; + } + break; + } + LEAVE_RET('i', 0); +} + +/** + * \brief Close and destroy an open channel + */ +void SCTP_Channel_Close(tVFS_Node *Node) +{ + tSCTPChannel *chan = Node->ImplPtr; + tSCTPChannel *prev; + + // Remove from the main list first + Mutex_Acquire(&glSCTP_Channels); + if(gpSCTP_Channels == chan) + gpSCTP_Channels = gpSCTP_Channels->Next; + else + { + for(prev = gpSCTP_Channels; + prev->Next && prev->Next != chan; + prev = prev->Next); + if(!prev->Next) + Log_Warning("SCTP", "Bookeeping Fail, channel %p is not in main list", chan); + else + prev->Next = prev->Next->Next; + } + Mutex_Release(&glSCTP_Channels); + + // Clear Queue + SHORTLOCK(&chan->lQueue); + while(chan->Queue) + { + tSCTPPacket *tmp; + tmp = chan->Queue; + chan->Queue = tmp->Next; + free(tmp); + } + SHORTREL(&chan->lQueue); + + // Free channel structure + free(chan); +} + +/** + * \return Port Number on success, or zero on failure + */ +Uint16 SCTP_int_AllocatePort() +{ + int i; + Mutex_Acquire(&glSCTP_Ports); + // Fast Search + for( i = SCTP_ALLOC_BASE; i < 0x10000; i += 32 ) + if( gSCTP_Ports[i/32] != 0xFFFFFFFF ) + break; + if(i == 0x10000) return 0; + for( ;; i++ ) + { + if( !(gSCTP_Ports[i/32] & (1 << (i%32))) ) + return i; + } + Mutex_Release(&glSCTP_Ports); +} + +/** + * \brief Allocate a specific port + * \return Boolean Success + */ +int SCTP_int_MarkPortAsUsed(Uint16 Port) +{ + Mutex_Acquire(&glSCTP_Ports); + if( gSCTP_Ports[Port/32] & (1 << (Port%32)) ) { + return 0; + Mutex_Release(&glSCTP_Ports); + } + gSCTP_Ports[Port/32] |= 1 << (Port%32); + Mutex_Release(&glSCTP_Ports); + return 1; +} + +/** + * \brief Free an allocated port + */ +void SCTP_int_FreePort(Uint16 Port) +{ + Mutex_Acquire(&glSCTP_Ports); + gSCTP_Ports[Port/32] &= ~(1 << (Port%32)); + Mutex_Release(&glSCTP_Ports); +} diff --git a/KernelLand/Modules/IPStack/tcp.c b/KernelLand/Modules/IPStack/tcp.c new file mode 100644 index 00000000..e9c3de98 --- /dev/null +++ b/KernelLand/Modules/IPStack/tcp.c @@ -0,0 +1,1373 @@ +/* + * Acess2 IP Stack + * - TCP Handling + */ +#define DEBUG 1 +#include "ipstack.h" +#include "ipv4.h" +#include "ipv6.h" +#include "tcp.h" + +#define USE_SELECT 1 +#define HEXDUMP_INCOMING 0 +#define HEXDUMP_OUTGOING 0 +#define CACHE_FUTURE_PACKETS_IN_BYTES 1 // Use a ring buffer to cache out of order packets + +#define TCP_MIN_DYNPORT 0xC000 +#define TCP_MAX_HALFOPEN 1024 // Should be enough + +#define TCP_MAX_PACKET_SIZE 1024 +#define TCP_WINDOW_SIZE 0x2000 +#define TCP_RECIEVE_BUFFER_SIZE 0x4000 + +// === PROTOTYPES === +void TCP_Initialise(void); +void TCP_StartConnection(tTCPConnection *Conn); +void TCP_SendPacket(tTCPConnection *Conn, size_t Length, tTCPHeader *Data); +void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer); +void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length); +int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t Length); +void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection); +Uint16 TCP_GetUnusedPort(); + int TCP_AllocatePort(Uint16 Port); + int TCP_DeallocatePort(Uint16 Port); +// --- Server +tVFS_Node *TCP_Server_Init(tInterface *Interface); +char *TCP_Server_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, const char *Name); + int TCP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data); +void TCP_Server_Close(tVFS_Node *Node); +// --- Client +tVFS_Node *TCP_Client_Init(tInterface *Interface); +Uint64 TCP_Client_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 TCP_Client_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); + int TCP_Client_IOCtl(tVFS_Node *Node, int ID, void *Data); +void TCP_Client_Close(tVFS_Node *Node); +// --- Helpers + int WrapBetween(Uint32 Lower, Uint32 Value, Uint32 Higher, Uint32 MaxValue); + +// === TEMPLATES === +tSocketFile gTCP_ServerFile = {NULL, "tcps", TCP_Server_Init}; +tSocketFile gTCP_ClientFile = {NULL, "tcpc", TCP_Client_Init}; +tVFS_NodeType gTCP_ServerNodeType = { + .TypeName = "TCP Server", + .ReadDir = TCP_Server_ReadDir, + .FindDir = TCP_Server_FindDir, + .IOCtl = TCP_Server_IOCtl, + .Close = TCP_Server_Close + }; +tVFS_NodeType gTCP_ClientNodeType = { + .TypeName = "TCP Client/Connection", + .Read = TCP_Client_Read, + .Write = TCP_Client_Write, + .IOCtl = TCP_Client_IOCtl, + .Close = TCP_Client_Close + }; + +// === GLOBALS === + int giTCP_NumHalfopen = 0; +tShortSpinlock glTCP_Listeners; +tTCPListener *gTCP_Listeners; +tShortSpinlock glTCP_OutbountCons; +tTCPConnection *gTCP_OutbountCons; +Uint32 gaTCP_PortBitmap[0x800]; + int giTCP_NextOutPort = TCP_MIN_DYNPORT; + +// === CODE === +/** + * \brief Initialise the TCP Layer + * + * Registers the client and server files and the GetPacket callback + */ +void TCP_Initialise(void) +{ + giTCP_NextOutPort += rand()%32; + IPStack_AddFile(&gTCP_ServerFile); + IPStack_AddFile(&gTCP_ClientFile); + IPv4_RegisterCallback(IP4PROT_TCP, TCP_GetPacket); + IPv6_RegisterCallback(IP4PROT_TCP, TCP_GetPacket); +} + +/** + * \brief Sends a packet from the specified connection, calculating the checksums + * \param Conn Connection + * \param Length Length of data + * \param Data Packet data (cast as a TCP Header) + */ +void TCP_SendPacket( tTCPConnection *Conn, size_t Length, tTCPHeader *Data ) +{ + Uint16 checksum[2]; + + Data->Checksum = 0; + checksum[1] = htons( ~IPv4_Checksum( (void*)Data, Length ) ); // Partial checksum + if(Length & 1) + ((Uint8*)Data)[Length] = 0; + + // TODO: Fragment packet + + switch( Conn->Interface->Type ) + { + case 4: + // Append IPv4 Pseudo Header + { + Uint32 buf[3]; + buf[0] = ((tIPv4*)Conn->Interface->Address)->L; + buf[1] = Conn->RemoteIP.v4.L; + buf[2] = (htons(Length)<<16) | (6<<8) | 0; + checksum[0] = htons( ~IPv4_Checksum(buf, sizeof(buf)) ); // Partial checksum + } + Data->Checksum = htons( IPv4_Checksum(checksum, 2*2) ); // Combine the two + IPv4_SendPacket(Conn->Interface, Conn->RemoteIP.v4, IP4PROT_TCP, 0, Length, Data); + break; + + case 6: + // Append IPv6 Pseudo Header + { + Uint32 buf[4+4+1+1]; + memcpy(buf, Conn->Interface->Address, 16); + memcpy(&buf[4], &Conn->RemoteIP, 16); + buf[8] = htonl(Length); + buf[9] = htonl(6); + checksum[0] = htons( ~IPv4_Checksum(buf, sizeof(buf)) ); // Partial checksum + } + Data->Checksum = htons( IPv4_Checksum(checksum, 2*2) ); // Combine the two + IPv6_SendPacket(Conn->Interface, Conn->RemoteIP.v6, IP4PROT_TCP, Length, Data); + break; + } +} + +/** + * \brief Handles a packet from the IP Layer + * \param Interface Interface the packet arrived from + * \param Address Pointer to the addres structure + * \param Length Size of packet in bytes + * \param Buffer Packet data + */ +void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) +{ + tTCPHeader *hdr = Buffer; + tTCPListener *srv; + tTCPConnection *conn; + + Log_Log("TCP", "TCP_GetPacket: :%i from [%s]:%i, Flags= %s%s%s%s%s%s%s%s", + ntohs(hdr->DestPort), + IPStack_PrintAddress(Interface->Type, Address), + ntohs(hdr->SourcePort), + (hdr->Flags & TCP_FLAG_CWR) ? "CWR " : "", + (hdr->Flags & TCP_FLAG_ECE) ? "ECE " : "", + (hdr->Flags & TCP_FLAG_URG) ? "URG " : "", + (hdr->Flags & TCP_FLAG_ACK) ? "ACK " : "", + (hdr->Flags & TCP_FLAG_PSH) ? "PSH " : "", + (hdr->Flags & TCP_FLAG_RST) ? "RST " : "", + (hdr->Flags & TCP_FLAG_SYN) ? "SYN " : "", + (hdr->Flags & TCP_FLAG_FIN) ? "FIN " : "" + ); + + if( Length > (hdr->DataOffset >> 4)*4 ) + { + Log_Log("TCP", "TCP_GetPacket: SequenceNumber = 0x%x", ntohl(hdr->SequenceNumber)); +#if HEXDUMP_INCOMING + Debug_HexDump( + "TCP_GetPacket: Packet Data = ", + (Uint8*)hdr + (hdr->DataOffset >> 4)*4, + Length - (hdr->DataOffset >> 4)*4 + ); +#endif + } + + // Check Servers + { + for( srv = gTCP_Listeners; srv; srv = srv->Next ) + { + // Check if the server is active + if(srv->Port == 0) continue; + // Check the interface + if(srv->Interface && srv->Interface != Interface) continue; + // Check the destination port + if(srv->Port != htons(hdr->DestPort)) continue; + + Log_Log("TCP", "TCP_GetPacket: Matches server %p", srv); + // Is this in an established connection? + for( conn = srv->Connections; conn; conn = conn->Next ) + { + // Check that it is coming in on the same interface + if(conn->Interface != Interface) continue; + + // Check Source Port + Log_Log("TCP", "TCP_GetPacket: conn->RemotePort(%i) == hdr->SourcePort(%i)", + conn->RemotePort, ntohs(hdr->SourcePort)); + if(conn->RemotePort != ntohs(hdr->SourcePort)) continue; + + // Check Source IP + Log_Debug("TCP", "TCP_GetPacket: conn->RemoteIP(%s)", + IPStack_PrintAddress(conn->Interface->Type, &conn->RemoteIP)); + Log_Debug("TCP", " == Address(%s)", + IPStack_PrintAddress(conn->Interface->Type, Address)); + if( IPStack_CompareAddress(conn->Interface->Type, &conn->RemoteIP, Address, -1) == 0 ) + continue ; + + Log_Log("TCP", "TCP_GetPacket: Matches connection %p", conn); + // We have a response! + TCP_INT_HandleConnectionPacket(conn, hdr, Length); + + return; + } + + Log_Log("TCP", "TCP_GetPacket: Opening Connection"); + // Open a new connection (well, check that it's a SYN) + if(hdr->Flags != TCP_FLAG_SYN) { + Log_Log("TCP", "TCP_GetPacket: Packet is not a SYN"); + return ; + } + + // TODO: Check for halfopen max + + conn = calloc(1, sizeof(tTCPConnection)); + conn->State = TCP_ST_SYN_RCVD; + conn->LocalPort = srv->Port; + conn->RemotePort = ntohs(hdr->SourcePort); + conn->Interface = Interface; + + switch(Interface->Type) + { + case 4: conn->RemoteIP.v4 = *(tIPv4*)Address; break; + case 6: conn->RemoteIP.v6 = *(tIPv6*)Address; break; + } + + conn->RecievedBuffer = RingBuffer_Create( TCP_RECIEVE_BUFFER_SIZE ); + + conn->NextSequenceRcv = ntohl( hdr->SequenceNumber ) + 1; + conn->NextSequenceSend = rand(); + + // Create node + conn->Node.NumACLs = 1; + conn->Node.ACLs = &gVFS_ACL_EveryoneRW; + conn->Node.ImplPtr = conn; + conn->Node.ImplInt = srv->NextID ++; + conn->Node.Type = &gTCP_ClientNodeType; // TODO: Special type for the server end? + + // Hmm... Theoretically, this lock will never have to wait, + // as the interface is locked to the watching thread, and this + // runs in the watching thread. But, it's a good idea to have + // it, just in case + // Oh, wait, there is a case where a wildcard can be used + // (srv->Interface == NULL) so having the lock is a good idea + SHORTLOCK(&srv->lConnections); + if( !srv->Connections ) + srv->Connections = conn; + else + srv->ConnectionsTail->Next = conn; + srv->ConnectionsTail = conn; + if(!srv->NewConnections) + srv->NewConnections = conn; + VFS_MarkAvaliable( &srv->Node, 1 ); + SHORTREL(&srv->lConnections); + + // Send the SYN ACK + hdr->Flags |= TCP_FLAG_ACK; + hdr->AcknowlegementNumber = htonl(conn->NextSequenceRcv); + hdr->SequenceNumber = htonl(conn->NextSequenceSend); + hdr->DestPort = hdr->SourcePort; + hdr->SourcePort = htons(srv->Port); + hdr->DataOffset = (sizeof(tTCPHeader)/4) << 4; + TCP_SendPacket( conn, sizeof(tTCPHeader), hdr ); + conn->NextSequenceSend ++; + return ; + } + } + + + // Check Open Connections + { + for( conn = gTCP_OutbountCons; conn; conn = conn->Next ) + { + // Check that it is coming in on the same interface + if(conn->Interface != Interface) continue; + + // Check Source Port + if(conn->RemotePort != ntohs(hdr->SourcePort)) continue; + + // Check Source IP + if(conn->Interface->Type == 6 && !IP6_EQU(conn->RemoteIP.v6, *(tIPv6*)Address)) + continue; + if(conn->Interface->Type == 4 && !IP4_EQU(conn->RemoteIP.v4, *(tIPv4*)Address)) + continue; + + TCP_INT_HandleConnectionPacket(conn, hdr, Length); + return ; + } + } + + Log_Log("TCP", "TCP_GetPacket: No Match"); +} + +/** + * \brief Handles a packet sent to a specific connection + * \param Connection TCP Connection pointer + * \param Header TCP Packet pointer + * \param Length Length of the packet + */ +void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length) +{ + int dataLen; + Uint32 sequence_num; + + // Silently drop once finished + // TODO: Check if this needs to be here + if( Connection->State == TCP_ST_FINISHED ) { + Log_Log("TCP", "Packet ignored - connection finnished"); + return ; + } + + // Syncronise sequence values + if(Header->Flags & TCP_FLAG_SYN) { + // TODO: What if the packet also has data? + Connection->NextSequenceRcv = ntohl(Header->SequenceNumber); + } + + // Ackowledge a sent packet + if(Header->Flags & TCP_FLAG_ACK) { + // TODO: Process an ACKed Packet + Log_Log("TCP", "Conn %p, Sent packet 0x%x ACKed", Connection, Header->AcknowlegementNumber); + } + + // Get length of data + dataLen = Length - (Header->DataOffset>>4)*4; + Log_Log("TCP", "HandleConnectionPacket - dataLen = %i", dataLen); + + // + // State Machine + // + switch( Connection->State ) + { + // Pre-init connection? + case TCP_ST_CLOSED: + Log_Log("TCP", "Packets to a closed connection?!"); + break; + + // --- Init States --- + // SYN sent, expecting SYN-ACK Connection Opening + case TCP_ST_SYN_SENT: + if( Header->Flags & TCP_FLAG_SYN ) + { + Connection->NextSequenceRcv ++; + Header->DestPort = Header->SourcePort; + Header->SourcePort = htons(Connection->LocalPort); + Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv); + Header->SequenceNumber = htonl(Connection->NextSequenceSend); + Header->WindowSize = htons(TCP_WINDOW_SIZE); + Header->Flags = TCP_FLAG_ACK; + Header->DataOffset = (sizeof(tTCPHeader)/4) << 4; + TCP_SendPacket( Connection, sizeof(tTCPHeader), Header ); + + if( Header->Flags & TCP_FLAG_ACK ) + { + Log_Log("TCP", "ACKing SYN-ACK"); + Connection->State = TCP_ST_OPEN; + } + else + { + Log_Log("TCP", "ACKing SYN"); + Connection->State = TCP_ST_SYN_RCVD; + } + } + break; + + // SYN-ACK sent, expecting ACK + case TCP_ST_SYN_RCVD: + if( Header->Flags & TCP_FLAG_ACK ) + { + // TODO: Handle max half-open limit + Connection->State = TCP_ST_OPEN; + Log_Log("TCP", "Connection fully opened"); + } + break; + + // --- Established State --- + case TCP_ST_OPEN: + // - Handle State changes + // + if( Header->Flags & TCP_FLAG_FIN ) { + Log_Log("TCP", "Conn %p closed, recieved FIN", Connection); + VFS_MarkError(&Connection->Node, 1); + Connection->State = TCP_ST_CLOSE_WAIT; +// Header->Flags &= ~TCP_FLAG_FIN; + // CLOSE WAIT requires the client to close (or does it?) + #if 0 + + #endif + } + + // Check for an empty packet + if(dataLen == 0) { + if( Header->Flags == TCP_FLAG_ACK ) + { + Log_Log("TCP", "ACK only packet"); + return ; + } + Connection->NextSequenceRcv ++; // TODO: Is this right? (empty packet counts as one byte) + Log_Log("TCP", "Empty Packet, inc and ACK the current sequence number"); + Header->DestPort = Header->SourcePort; + Header->SourcePort = htons(Connection->LocalPort); + Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv); + Header->SequenceNumber = htonl(Connection->NextSequenceSend); + Header->Flags |= TCP_FLAG_ACK; + TCP_SendPacket( Connection, sizeof(tTCPHeader), Header ); + return ; + } + + // NOTES: + // Flags + // PSH - Has Data? + // /NOTES + + sequence_num = ntohl(Header->SequenceNumber); + + Log_Log("TCP", "0x%08x <= 0x%08x < 0x%08x", + Connection->NextSequenceRcv, + ntohl(Header->SequenceNumber), + Connection->NextSequenceRcv + TCP_WINDOW_SIZE + ); + + // Is this packet the next expected packet? + if( sequence_num == Connection->NextSequenceRcv ) + { + int rv; + // Ooh, Goodie! Add it to the recieved list + rv = TCP_INT_AppendRecieved(Connection, + (Uint8*)Header + (Header->DataOffset>>4)*4, + dataLen + ); + if(rv != 0) { + break; + } + Log_Log("TCP", "0x%08x += %i", Connection->NextSequenceRcv, dataLen); + Connection->NextSequenceRcv += dataLen; + + // TODO: This should be moved out of the watcher thread, + // so that a single lost packet on one connection doesn't cause + // all connections on the interface to lag. + // - Meh, no real issue, as the cache shouldn't be that large + TCP_INT_UpdateRecievedFromFuture(Connection); + + // ACK Packet + Header->DestPort = Header->SourcePort; + Header->SourcePort = htons(Connection->LocalPort); + Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv); + Header->SequenceNumber = htonl(Connection->NextSequenceSend); + Header->WindowSize = htons(TCP_WINDOW_SIZE); + Header->Flags &= TCP_FLAG_SYN; // Eliminate all flags save for SYN + Header->Flags |= TCP_FLAG_ACK; // Add ACK + Log_Log("TCP", "Sending ACK for 0x%08x", Connection->NextSequenceRcv); + TCP_SendPacket( Connection, sizeof(tTCPHeader), Header ); + //Connection->NextSequenceSend ++; + } + // Check if the packet is in window + else if( WrapBetween(Connection->NextSequenceRcv, sequence_num, + Connection->NextSequenceRcv+TCP_WINDOW_SIZE, 0xFFFFFFFF) ) + { + Uint8 *dataptr = (Uint8*)Header + (Header->DataOffset>>4)*4; + #if CACHE_FUTURE_PACKETS_IN_BYTES + Uint32 index; + int i; + + index = sequence_num % TCP_WINDOW_SIZE; + for( i = 0; i < dataLen; i ++ ) + { + Connection->FuturePacketValidBytes[index/8] |= 1 << (index%8); + Connection->FuturePacketData[index] = dataptr[i]; + // Do a wrap increment + index ++; + if(index == TCP_WINDOW_SIZE) index = 0; + } + #else + tTCPStoredPacket *pkt, *tmp, *prev = NULL; + + // Allocate and fill cached packet + pkt = malloc( sizeof(tTCPStoredPacket) + dataLen ); + pkt->Next = NULL; + pkt->Sequence = ntohl(Header->SequenceNumber); + pkt->Length = dataLen; + memcpy(pkt->Data, dataptr, dataLen); + + Log_Log("TCP", "We missed a packet, caching", + pkt->Sequence, Connection->NextSequenceRcv); + + // No? Well, let's cache it and look at it later + SHORTLOCK( &Connection->lFuturePackets ); + for(tmp = Connection->FuturePackets; + tmp; + prev = tmp, tmp = tmp->Next) + { + if(tmp->Sequence >= pkt->Sequence) break; + } + + // Add if before first, or sequences don't match + if( !tmp || tmp->Sequence != pkt->Sequence ) + { + if(prev) + prev->Next = pkt; + else + Connection->FuturePackets = pkt; + pkt->Next = tmp; + } + // Replace if larger + else if(pkt->Length > tmp->Length) + { + if(prev) + prev->Next = pkt; + pkt->Next = tmp->Next; + free(tmp); + } + else + { + free(pkt); // TODO: Find some way to remove this + } + SHORTREL( &Connection->lFuturePackets ); + #endif + } + // Badly out of sequence packet + else + { + Log_Log("TCP", "Fully out of sequence packet (0x%08x not between 0x%08x and 0x%08x), dropped", + sequence_num, Connection->NextSequenceRcv, Connection->NextSequenceRcv+TCP_WINDOW_SIZE); + // TODO: Spec says we should send an empty ACK with the current state + } + break; + + // --- Remote close states + case TCP_ST_CLOSE_WAIT: + + // Ignore everything, CLOSE_WAIT is terminated by the client + Log_Debug("TCP", "CLOSE WAIT - Ignoring packets"); + + break; + + // LAST-ACK - Waiting for the ACK of FIN (from CLOSE WAIT) + case TCP_ST_LAST_ACK: + if( Header->Flags & TCP_FLAG_ACK ) + { + Connection->State = TCP_ST_FINISHED; // Connection completed + Log_Log("TCP", "LAST-ACK to CLOSED - Connection remote closed"); + // TODO: Destrory the TCB + } + break; + + // --- Local close States + case TCP_ST_FIN_WAIT1: + if( Header->Flags & TCP_FLAG_FIN ) + { + Connection->State = TCP_ST_CLOSING; + Log_Debug("TCP", "Conn %p closed, sent FIN and recieved FIN", Connection); + VFS_MarkError(&Connection->Node, 1); + + // ACK Packet + Header->DestPort = Header->SourcePort; + Header->SourcePort = htons(Connection->LocalPort); + Header->AcknowlegementNumber = Header->SequenceNumber; + Header->SequenceNumber = htonl(Connection->NextSequenceSend); + Header->WindowSize = htons(TCP_WINDOW_SIZE); + Header->Flags = TCP_FLAG_ACK; + TCP_SendPacket( Connection, sizeof(tTCPHeader), Header ); + break ; + } + + // TODO: Make sure that the packet is actually ACKing the FIN + if( Header->Flags & TCP_FLAG_ACK ) + { + Connection->State = TCP_ST_FIN_WAIT2; + Log_Debug("TCP", "Conn %p closed, sent FIN ACKed", Connection); + VFS_MarkError(&Connection->Node, 1); + return ; + } + break; + + case TCP_ST_FIN_WAIT2: + if( Header->Flags & TCP_FLAG_FIN ) + { + Connection->State = TCP_ST_TIME_WAIT; + Log_Debug("TCP", "FIN sent and recieved, ACKing and going into TIME WAIT %p FINWAIT-2 -> TIME WAIT", Connection); + // Send ACK + Header->DestPort = Header->SourcePort; + Header->SourcePort = htons(Connection->LocalPort); + Header->AcknowlegementNumber = Header->SequenceNumber; + Header->SequenceNumber = htonl(Connection->NextSequenceSend); + Header->WindowSize = htons(TCP_WINDOW_SIZE); + Header->Flags = TCP_FLAG_ACK; + TCP_SendPacket( Connection, sizeof(tTCPHeader), Header ); + } + break; + + case TCP_ST_CLOSING: + // TODO: Make sure that the packet is actually ACKing the FIN + if( Header->Flags & TCP_FLAG_ACK ) + { + Connection->State = TCP_ST_TIME_WAIT; + Log_Debug("TCP", "Conn %p CLOSING -> TIME WAIT", Connection); + VFS_MarkError(&Connection->Node, 1); + return ; + } + break; + + // --- Closed (or near closed) states) --- + case TCP_ST_TIME_WAIT: + Log_Log("TCP", "Packets on Time-Wait, ignored"); + break; + + case TCP_ST_FINISHED: + Log_Log("TCP", "Packets when CLOSED, ignoring"); + break; + + //default: + // Log_Warning("TCP", "Unhandled TCP state %i", Connection->State); + // break; + } + +} + +/** + * \brief Appends a packet to the recieved list + * \param Connection Connection structure + * \param Data Packet contents + * \param Length Length of \a Data + */ +int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t Length) +{ + Mutex_Acquire( &Connection->lRecievedPackets ); + + if(Connection->RecievedBuffer->Length + Length > Connection->RecievedBuffer->Space ) + { + VFS_MarkAvaliable(&Connection->Node, 1); + Log_Error("TCP", "Buffer filled, packet dropped (:%i) - %i + %i > %i", + Connection->LocalPort, Connection->RecievedBuffer->Length, Length, + Connection->RecievedBuffer->Space + ); + Mutex_Release( &Connection->lRecievedPackets ); + return 1; + } + + RingBuffer_Write( Connection->RecievedBuffer, Data, Length ); + + VFS_MarkAvaliable(&Connection->Node, 1); + + Mutex_Release( &Connection->lRecievedPackets ); + return 0; +} + +/** + * \brief Updates the connections recieved list from the future list + * \param Connection Connection structure + * + * Updates the recieved packets list with packets from the future (out + * of order) packets list that are now able to be added in direct + * sequence. + */ +void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection) +{ + #if CACHE_FUTURE_PACKETS_IN_BYTES + int i, length = 0; + Uint32 index; + + // Calculate length of contiguous bytes + length = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv; + index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE; + for( i = 0; i < length; i ++ ) + { + if( Connection->FuturePacketValidBytes[i / 8] == 0xFF ) { + i += 7; index += 7; + continue; + } + else if( !(Connection->FuturePacketValidBytes[i / 8] & (1 << (i%8))) ) + break; + + index ++; + if(index > TCP_WINDOW_SIZE) + index -= TCP_WINDOW_SIZE; + } + length = i; + + index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE; + + // Write data to to the ring buffer + if( TCP_WINDOW_SIZE - index > length ) + { + // Simple case + RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, length ); + } + else + { + int endLen = TCP_WINDOW_SIZE - index; + // 2-part case + RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, endLen ); + RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData, endLen - length ); + } + + // Mark (now saved) bytes as invalid + // - Align index + while(index % 8 && length) + { + Connection->FuturePacketData[index] = 0; + Connection->FuturePacketData[index/8] &= ~(1 << (index%8)); + index ++; + if(index > TCP_WINDOW_SIZE) + index -= TCP_WINDOW_SIZE; + length --; + } + while( length > 7 ) + { + Connection->FuturePacketData[index] = 0; + Connection->FuturePacketValidBytes[index/8] = 0; + length -= 8; + index += 8; + if(index > TCP_WINDOW_SIZE) + index -= TCP_WINDOW_SIZE; + } + while(length) + { + Connection->FuturePacketData[index] = 0; + Connection->FuturePacketData[index/8] &= ~(1 << (index%8)); + index ++; + if(index > TCP_WINDOW_SIZE) + index -= TCP_WINDOW_SIZE; + length --; + } + + #else + tTCPStoredPacket *pkt; + for(;;) + { + SHORTLOCK( &Connection->lFuturePackets ); + + // Clear out duplicates from cache + // - If a packet has just been recieved, and it is expected, then + // (since NextSequenceRcv = rcvd->Sequence + rcvd->Length) all + // packets in cache that are smaller than the next expected + // are now defunct. + pkt = Connection->FuturePackets; + while(pkt && pkt->Sequence < Connection->NextSequenceRcv) + { + tTCPStoredPacket *next = pkt->Next; + free(pkt); + pkt = next; + } + + // If there's no packets left in cache, stop looking + if(!pkt || pkt->Sequence > Connection->NextSequenceRcv) { + SHORTREL( &Connection->lFuturePackets ); + return; + } + + // Delete packet from future list + Connection->FuturePackets = pkt->Next; + + // Release list + SHORTREL( &Connection->lFuturePackets ); + + // Looks like we found one + TCP_INT_AppendRecieved(Connection, pkt); + Connection->NextSequenceRcv += pkt->Length; + free(pkt); + } + #endif +} + +/** + * \fn Uint16 TCP_GetUnusedPort() + * \brief Gets an unused port and allocates it + */ +Uint16 TCP_GetUnusedPort() +{ + Uint16 ret; + + // Get Next outbound port + ret = giTCP_NextOutPort++; + while( gaTCP_PortBitmap[ret/32] & (1UL << (ret%32)) ) + { + ret ++; + giTCP_NextOutPort++; + if(giTCP_NextOutPort == 0x10000) { + ret = giTCP_NextOutPort = TCP_MIN_DYNPORT; + } + } + + // Mark the new port as used + gaTCP_PortBitmap[ret/32] |= 1 << (ret%32); + + return ret; +} + +/** + * \fn int TCP_AllocatePort(Uint16 Port) + * \brief Marks a port as used + */ +int TCP_AllocatePort(Uint16 Port) +{ + // Check if the port has already been allocated + if( gaTCP_PortBitmap[Port/32] & (1 << (Port%32)) ) + return 0; + + // Allocate + gaTCP_PortBitmap[Port/32] |= 1 << (Port%32); + + return 1; +} + +/** + * \fn int TCP_DeallocatePort(Uint16 Port) + * \brief Marks a port as unused + */ +int TCP_DeallocatePort(Uint16 Port) +{ + // Check if the port has already been allocated + if( !(gaTCP_PortBitmap[Port/32] & (1 << (Port%32))) ) + return 0; + + // Allocate + gaTCP_PortBitmap[Port/32] &= ~(1 << (Port%32)); + + return 1; +} + +// --- Server +tVFS_Node *TCP_Server_Init(tInterface *Interface) +{ + tTCPListener *srv; + + srv = calloc( 1, sizeof(tTCPListener) ); + + if( srv == NULL ) { + Log_Warning("TCP", "malloc failed for listener (%i) bytes", sizeof(tTCPListener)); + return NULL; + } + + srv->Interface = Interface; + srv->Port = 0; + srv->NextID = 0; + srv->Connections = NULL; + srv->ConnectionsTail = NULL; + srv->NewConnections = NULL; + srv->Next = NULL; + srv->Node.Flags = VFS_FFLAG_DIRECTORY; + srv->Node.Size = -1; + srv->Node.ImplPtr = srv; + srv->Node.NumACLs = 1; + srv->Node.ACLs = &gVFS_ACL_EveryoneRW; + srv->Node.Type = &gTCP_ServerNodeType; + + SHORTLOCK(&glTCP_Listeners); + srv->Next = gTCP_Listeners; + gTCP_Listeners = srv; + SHORTREL(&glTCP_Listeners); + + return &srv->Node; +} + +/** + * \brief Wait for a new connection and return the connection ID + * \note Blocks until a new connection is made + * \param Node Server node + * \param Pos Position (ignored) + */ +char *TCP_Server_ReadDir(tVFS_Node *Node, int Pos) +{ + tTCPListener *srv = Node->ImplPtr; + tTCPConnection *conn; + char *ret; + + ENTER("pNode iPos", Node, Pos); + + Log_Log("TCP", "Thread %i waiting for a connection", Threads_GetTID()); + for(;;) + { + SHORTLOCK( &srv->lConnections ); + if( srv->NewConnections != NULL ) break; + SHORTREL( &srv->lConnections ); + Threads_Yield(); // TODO: Sleep until poked + } + + + // Increment the new list (the current connection is still on the + // normal list) + conn = srv->NewConnections; + srv->NewConnections = conn->Next; + + if( srv->NewConnections == NULL ) + VFS_MarkAvaliable( Node, 0 ); + + SHORTREL( &srv->lConnections ); + + LOG("conn = %p", conn); + LOG("srv->Connections = %p", srv->Connections); + LOG("srv->NewConnections = %p", srv->NewConnections); + LOG("srv->ConnectionsTail = %p", srv->ConnectionsTail); + + ret = malloc(9); + itoa(ret, conn->Node.ImplInt, 16, 8, '0'); + Log_Log("TCP", "Thread %i got '%s'", Threads_GetTID(), ret); + LEAVE('s', ret); + return ret; +} + +/** + * \brief Gets a client connection node + * \param Node Server node + * \param Name Hexadecimal ID of the node + */ +tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, const char *Name) +{ + tTCPConnection *conn; + tTCPListener *srv = Node->ImplPtr; + char tmp[9]; + int id = atoi(Name); + + ENTER("pNode sName", Node, Name); + + // Check for a non-empty name + if( Name[0] ) + { + // Sanity Check + itoa(tmp, id, 16, 8, '0'); + if(strcmp(tmp, Name) != 0) { + LOG("'%s' != '%s' (%08x)", Name, tmp, id); + LEAVE('n'); + return NULL; + } + + Log_Debug("TCP", "srv->Connections = %p", srv->Connections); + Log_Debug("TCP", "srv->NewConnections = %p", srv->NewConnections); + Log_Debug("TCP", "srv->ConnectionsTail = %p", srv->ConnectionsTail); + + // Search + SHORTLOCK( &srv->lConnections ); + for(conn = srv->Connections; + conn; + conn = conn->Next) + { + LOG("conn->Node.ImplInt = %i", conn->Node.ImplInt); + if(conn->Node.ImplInt == id) break; + } + SHORTREL( &srv->lConnections ); + + // If not found, ret NULL + if(!conn) { + LOG("Connection %i not found", id); + LEAVE('n'); + return NULL; + } + } + // Empty Name - Check for a new connection and if it's there, open it + else + { + SHORTLOCK( &srv->lConnections ); + conn = srv->NewConnections; + if( conn != NULL ) + srv->NewConnections = conn->Next; + VFS_MarkAvaliable( Node, srv->NewConnections != NULL ); + SHORTREL( &srv->lConnections ); + if( !conn ) { + LOG("No new connections"); + LEAVE('n'); + return NULL; + } + } + + // Return node + LEAVE('p', &conn->Node); + return &conn->Node; +} + +/** + * \brief Handle IOCtl calls + */ +int TCP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + tTCPListener *srv = Node->ImplPtr; + + switch(ID) + { + case 4: // Get/Set Port + if(!Data) // Get Port + return srv->Port; + + if(srv->Port) // Wait, you can't CHANGE the port + return -1; + + if(!CheckMem(Data, sizeof(Uint16))) // Sanity check + return -1; + + // Permissions check + if(Threads_GetUID() != 0 + && *(Uint16*)Data != 0 + && *(Uint16*)Data < 1024) + return -1; + + // TODO: Check if a port is in use + + // Set Port + srv->Port = *(Uint16*)Data; + if(srv->Port == 0) // Allocate a random port + srv->Port = TCP_GetUnusedPort(); + else // Else, mark this as used + TCP_AllocatePort(srv->Port); + + Log_Log("TCP", "Server %p listening on port %i", srv, srv->Port); + + return srv->Port; + } + return 0; +} + +void TCP_Server_Close(tVFS_Node *Node) +{ + free(Node->ImplPtr); +} + +// --- Client +/** + * \brief Create a client node + */ +tVFS_Node *TCP_Client_Init(tInterface *Interface) +{ + tTCPConnection *conn = calloc( sizeof(tTCPConnection) + TCP_WINDOW_SIZE + TCP_WINDOW_SIZE/8, 1 ); + + conn->State = TCP_ST_CLOSED; + conn->Interface = Interface; + conn->LocalPort = -1; + conn->RemotePort = -1; + + conn->Node.ImplPtr = conn; + conn->Node.NumACLs = 1; + conn->Node.ACLs = &gVFS_ACL_EveryoneRW; + conn->Node.Type = &gTCP_ClientNodeType; + + conn->RecievedBuffer = RingBuffer_Create( TCP_RECIEVE_BUFFER_SIZE ); + #if 0 + conn->SentBuffer = RingBuffer_Create( TCP_SEND_BUFFER_SIZE ); + Semaphore_Init(conn->SentBufferSpace, 0, TCP_SEND_BUFFER_SIZE, "TCP SentBuffer", conn->Name); + #endif + + #if CACHE_FUTURE_PACKETS_IN_BYTES + // Future recieved data (ahead of the expected sequence number) + conn->FuturePacketData = (Uint8*)conn + sizeof(tTCPConnection); + conn->FuturePacketValidBytes = conn->FuturePacketData + TCP_WINDOW_SIZE; + #endif + + SHORTLOCK(&glTCP_OutbountCons); + conn->Next = gTCP_OutbountCons; + gTCP_OutbountCons = conn; + SHORTREL(&glTCP_OutbountCons); + + return &conn->Node; +} + +/** + * \brief Wait for a packet and return it + * \note If \a Length is smaller than the size of the packet, the rest + * of the packet's data will be discarded. + */ +Uint64 TCP_Client_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tTCPConnection *conn = Node->ImplPtr; + size_t len; + + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + LOG("conn = %p {State:%i}", conn, conn->State); + + // Check if connection is estabilishing + // - TODO: Sleep instead (maybe using VFS_SelectNode to wait for the + // data to be availiable + while( conn->State == TCP_ST_SYN_RCVD || conn->State == TCP_ST_SYN_SENT ) + Threads_Yield(); + + // If the conneciton is not open, then clean out the recieved buffer + if( conn->State != TCP_ST_OPEN ) + { + Mutex_Acquire( &conn->lRecievedPackets ); + len = RingBuffer_Read( Buffer, conn->RecievedBuffer, Length ); + Mutex_Release( &conn->lRecievedPackets ); + + if( len == 0 ) { + VFS_MarkAvaliable(Node, 0); + LEAVE('i', -1); + return -1; + } + + LEAVE('i', len); + return len; + } + + // Wait + VFS_SelectNode(Node, VFS_SELECT_READ|VFS_SELECT_ERROR, NULL, "TCP_Client_Read"); + + // Lock list and read as much as possible (up to `Length`) + Mutex_Acquire( &conn->lRecievedPackets ); + len = RingBuffer_Read( Buffer, conn->RecievedBuffer, Length ); + + if( len == 0 || conn->RecievedBuffer->Length == 0 ) { + LOG("Marking as none avaliable (len = %i)", len); + VFS_MarkAvaliable(Node, 0); + } + + // Release the lock (we don't need it any more) + Mutex_Release( &conn->lRecievedPackets ); + + LEAVE('i', len); + return len; +} + +/** + * \brief Send a data packet on a connection + */ +void TCP_INT_SendDataPacket(tTCPConnection *Connection, size_t Length, const void *Data) +{ + char buf[sizeof(tTCPHeader)+Length]; + tTCPHeader *packet = (void*)buf; + + packet->SourcePort = htons(Connection->LocalPort); + packet->DestPort = htons(Connection->RemotePort); + packet->DataOffset = (sizeof(tTCPHeader)/4)*16; + packet->WindowSize = htons(TCP_WINDOW_SIZE); + + packet->AcknowlegementNumber = htonl(Connection->NextSequenceRcv); + packet->SequenceNumber = htonl(Connection->NextSequenceSend); + packet->Flags = TCP_FLAG_PSH|TCP_FLAG_ACK; // Hey, ACK if you can! + + memcpy(packet->Options, Data, Length); + + Log_Debug("TCP", "Send sequence 0x%08x", Connection->NextSequenceSend); +#if HEXDUMP_OUTGOING + Debug_HexDump("TCP_INT_SendDataPacket: Data = ", Data, Length); +#endif + + TCP_SendPacket( Connection, sizeof(tTCPHeader)+Length, packet ); + + Connection->NextSequenceSend += Length; +} + +/** + * \brief Send some bytes on a connection + */ +Uint64 TCP_Client_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + tTCPConnection *conn = Node->ImplPtr; + size_t rem = Length; + + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + +// #if DEBUG +// Debug_HexDump("TCP_Client_Write: Buffer = ", +// Buffer, Length); +// #endif + + // Check if connection is open + while( conn->State == TCP_ST_SYN_RCVD || conn->State == TCP_ST_SYN_SENT ) + Threads_Yield(); + + if( conn->State != TCP_ST_OPEN ) { + VFS_MarkError(Node, 1); + LEAVE('i', -1); + return -1; + } + + do + { + int len = (rem < TCP_MAX_PACKET_SIZE) ? rem : TCP_MAX_PACKET_SIZE; + + #if 0 + // Wait for space in the buffer + Semaphore_Signal( &Connection->SentBufferSpace, len ); + + // Save data to buffer (and update the length read by the ammount written) + len = RingBuffer_Write( &Connection->SentBuffer, Buffer, len); + #endif + + // Send packet + TCP_INT_SendDataPacket(conn, len, Buffer); + + Buffer += len; + rem -= len; + } while( rem > 0 ); + + LEAVE('i', Length); + return Length; +} + +/** + * \brief Open a connection to another host using TCP + * \param Conn Connection structure + */ +void TCP_StartConnection(tTCPConnection *Conn) +{ + tTCPHeader hdr = {0}; + + Conn->State = TCP_ST_SYN_SENT; + + hdr.SourcePort = htons(Conn->LocalPort); + hdr.DestPort = htons(Conn->RemotePort); + Conn->NextSequenceSend = rand(); + hdr.SequenceNumber = htonl(Conn->NextSequenceSend); + hdr.DataOffset = (sizeof(tTCPHeader)/4) << 4; + hdr.Flags = TCP_FLAG_SYN; + hdr.WindowSize = htons(TCP_WINDOW_SIZE); // Max + hdr.Checksum = 0; // TODO + + TCP_SendPacket( Conn, sizeof(tTCPHeader), &hdr ); + + Conn->NextSequenceSend ++; + Conn->State = TCP_ST_SYN_SENT; + + return ; +} + +/** + * \brief Control a client socket + */ +int TCP_Client_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + tTCPConnection *conn = Node->ImplPtr; + + ENTER("pNode iID pData", Node, ID, Data); + + switch(ID) + { + case 4: // Get/Set local port + if(!Data) + LEAVE_RET('i', conn->LocalPort); + if(conn->State != TCP_ST_CLOSED) + LEAVE_RET('i', -1); + if(!CheckMem(Data, sizeof(Uint16))) + LEAVE_RET('i', -1); + + if(Threads_GetUID() != 0 && *(Uint16*)Data < 1024) + LEAVE_RET('i', -1); + + conn->LocalPort = *(Uint16*)Data; + LEAVE_RET('i', conn->LocalPort); + + case 5: // Get/Set remote port + if(!Data) LEAVE_RET('i', conn->RemotePort); + if(conn->State != TCP_ST_CLOSED) LEAVE_RET('i', -1); + if(!CheckMem(Data, sizeof(Uint16))) LEAVE_RET('i', -1); + conn->RemotePort = *(Uint16*)Data; + LEAVE_RET('i', conn->RemotePort); + + case 6: // Set Remote IP + if( conn->State != TCP_ST_CLOSED ) + LEAVE_RET('i', -1); + if( conn->Interface->Type == 4 ) + { + if(!CheckMem(Data, sizeof(tIPv4))) LEAVE_RET('i', -1); + conn->RemoteIP.v4 = *(tIPv4*)Data; + } + else if( conn->Interface->Type == 6 ) + { + if(!CheckMem(Data, sizeof(tIPv6))) LEAVE_RET('i', -1); + conn->RemoteIP.v6 = *(tIPv6*)Data; + } + LEAVE_RET('i', 0); + + case 7: // Connect + if(conn->LocalPort == 0xFFFF) + conn->LocalPort = TCP_GetUnusedPort(); + if(conn->RemotePort == -1) + LEAVE_RET('i', 0); + + { + tTime timeout_end = now() + conn->Interface->TimeoutDelay; + + TCP_StartConnection(conn); + // TODO: Wait for connection to open + while( conn->State == TCP_ST_SYN_SENT && timeout_end > now() ) { + Threads_Yield(); + } + if( conn->State == TCP_ST_SYN_SENT ) + LEAVE_RET('i', 0); + } + + LEAVE_RET('i', 1); + + // Get recieve buffer length + case 8: + LEAVE_RET('i', conn->RecievedBuffer->Length); + } + + return 0; +} + +void TCP_Client_Close(tVFS_Node *Node) +{ + tTCPConnection *conn = Node->ImplPtr; + tTCPHeader packet; + + ENTER("pNode", Node); + + if( conn->State == TCP_ST_CLOSE_WAIT || conn->State == TCP_ST_OPEN ) + { + packet.SourcePort = htons(conn->LocalPort); + packet.DestPort = htons(conn->RemotePort); + packet.DataOffset = (sizeof(tTCPHeader)/4)*16; + packet.WindowSize = TCP_WINDOW_SIZE; + + packet.AcknowlegementNumber = 0; + packet.SequenceNumber = htonl(conn->NextSequenceSend); + packet.Flags = TCP_FLAG_FIN; + + TCP_SendPacket( conn, sizeof(tTCPHeader), &packet ); + } + + switch( conn->State ) + { + case TCP_ST_CLOSE_WAIT: + conn->State = TCP_ST_LAST_ACK; + break; + case TCP_ST_OPEN: + conn->State = TCP_ST_FIN_WAIT1; + while( conn->State == TCP_ST_FIN_WAIT1 ) Threads_Yield(); + break; + default: + Log_Warning("TCP", "Unhandled connection state in TCP_Client_Close"); + break; + } + + free(conn); + + LEAVE('-'); +} + +/** + * \brief Checks if a value is between two others (after taking into account wrapping) + */ +int WrapBetween(Uint32 Lower, Uint32 Value, Uint32 Higher, Uint32 MaxValue) +{ + if( MaxValue < 0xFFFFFFFF ) + { + Lower %= MaxValue + 1; + Value %= MaxValue + 1; + Higher %= MaxValue + 1; + } + + // Simple Case, no wrap ? + // Lower Value Higher + // | ... + ... + ... + ... | + + if( Lower < Higher ) { + return Lower < Value && Value < Higher; + } + // Higher has wrapped below lower + + // Value > Lower ? + // Higher Lower Value + // | ... + ... + ... + ... | + if( Value > Lower ) { + return 1; + } + + // Value < Higher ? + // Value Higher Lower + // | ... + ... + ... + ... | + if( Value < Higher ) { + return 1; + } + + return 0; +} diff --git a/KernelLand/Modules/IPStack/tcp.h b/KernelLand/Modules/IPStack/tcp.h new file mode 100644 index 00000000..6aa404f6 --- /dev/null +++ b/KernelLand/Modules/IPStack/tcp.h @@ -0,0 +1,166 @@ +/* + * Acess2 IP Stack + * - TCP Definitions + */ +#ifndef _TCP_H_ +#define _TCP_H_ + +#include "ipstack.h" +#include // tRingBuffer + +typedef struct sTCPHeader tTCPHeader; +typedef struct sTCPListener tTCPListener; +typedef struct sTCPStoredPacket tTCPStoredPacket; +typedef struct sTCPConnection tTCPConnection; + +struct sTCPHeader +{ + Uint16 SourcePort; + Uint16 DestPort; + Uint32 SequenceNumber; + Uint32 AcknowlegementNumber; + #if 0 + struct { // Lowest to highest + unsigned Reserved: 4; + unsigned DataOffset: 4; // Size of the header in 32-bit words + } __attribute__ ((packed)); + #else + Uint8 DataOffset; + #endif + #if 0 + struct { // Lowest to Highest + unsigned FIN: 1; // Last packet + unsigned SYN: 1; // Synchronise Sequence Numbers + unsigned RST: 1; // Reset Connection + unsigned PSH: 1; // Push Function + unsigned ACK: 1; // Acknowlegement field is significant + unsigned URG: 1; // Urgent pointer is significant + unsigned ECE: 1; // ECN-Echo + unsigned CWR: 1; // Congestion Window Reduced + } __attribute__ ((packed)) Flags; + #else + Uint8 Flags; + #endif + Uint16 WindowSize; + + Uint16 Checksum; + Uint16 UrgentPointer; + + Uint8 Options[]; +} __attribute__ ((packed)); + +enum eTCPFlags +{ + TCP_FLAG_FIN = 0x01, + TCP_FLAG_SYN = 0x02, + TCP_FLAG_RST = 0x04, + TCP_FLAG_PSH = 0x08, + TCP_FLAG_ACK = 0x10, + TCP_FLAG_URG = 0x20, + TCP_FLAG_ECE = 0x40, + TCP_FLAG_CWR = 0x80 +}; + +struct sTCPListener +{ + struct sTCPListener *Next; //!< Next server in the list + Uint16 Port; //!< Listening port (0 disables the server) + tInterface *Interface; //!< Listening Interface + tVFS_Node Node; //!< Server Directory node + int NextID; //!< Name of the next connection + tShortSpinlock lConnections; //!< Spinlock for connections + tTCPConnection *Connections; //!< Connections (linked list) + tTCPConnection *volatile NewConnections; + tTCPConnection *ConnectionsTail; +}; + +struct sTCPStoredPacket +{ + struct sTCPStoredPacket *Next; + size_t Length; + Uint32 Sequence; + Uint8 Data[]; +}; + +enum eTCPConnectionState +{ + TCP_ST_CLOSED, // 0 - Connection invalid + + TCP_ST_SYN_SENT, // 1 - SYN sent by local, waiting for SYN-ACK + TCP_ST_SYN_RCVD, // 2 - SYN recieved, SYN-ACK sent + + TCP_ST_OPEN, // 3 - Connection open + + // Local Close + TCP_ST_FIN_WAIT1, // 4 - FIN sent, waiting for reply (ACK or FIN) + TCP_ST_FIN_WAIT2, // 5 - sent FIN acked, waiting for FIN from peer + TCP_ST_CLOSING, // 6 - Waiting for ACK of FIN (FIN sent and recieved) + TCP_ST_TIME_WAIT, // 7 - Waiting for timeout after local close + // Remote close + TCP_ST_CLOSE_WAIT, // 8 - FIN recieved, waiting for user to close (error set, wait for node close) + TCP_ST_LAST_ACK, // 9 - FIN sent and recieved, waiting for ACK + TCP_ST_FINISHED // 10 - Essentially closed, all packets are invalid +}; + +struct sTCPConnection +{ + struct sTCPConnection *Next; + enum eTCPConnectionState State; //!< Connection state (see ::eTCPConnectionState) + Uint16 LocalPort; //!< Local port + Uint16 RemotePort; //!< Remote port + tInterface *Interface; //!< Listening Interface + tVFS_Node Node; //!< Node + + Uint32 NextSequenceSend; //!< Next sequence value for outbound packets + Uint32 NextSequenceRcv; //!< Next expected sequence value for inbound + + #if 0 + /** + * \brief Non-ACKed packets + * \note Ring buffer + * \{ + */ + tMutex lNonACKedPackets; + tTCPStoredPacket *SentPackets; //!< Non-acknowleged packets + /** + * \} + */ + #endif + + /** + * \brief Unread Packets + * \note Ring buffer + * \{ + */ + tMutex lRecievedPackets; + tRingBuffer *RecievedBuffer; + /** + * \} + */ + + /** + * \brief Out of sequence packets + * \note Sorted list to improve times + * \todo Convert this to a ring buffer and a bitmap of valid bytes + * \{ + */ + #if CACHE_FUTURE_PACKETS_OR_BYTES == bytes + Uint32 HighestSequenceRcvd; //!< Highest sequence number (within window) recieved + Uint8 *FuturePacketData; //!< Future packet data (indexed by sequence number) + Uint8 *FuturePacketValidBytes; //!< Valid byte bitmap (WINDOW_SIZE/8 bytes) + #else + tShortSpinlock lFuturePackets; //!< Future packets spinlock + tTCPStoredPacket *FuturePackets; //!< Out of sequence packets + #endif + /** + * \} + */ + + union { + tIPv4 v4; + tIPv6 v6; + } RemoteIP; //!< Remote IP Address + // Type is determined by LocalInterface->Type +}; + +#endif diff --git a/KernelLand/Modules/IPStack/udp.c b/KernelLand/Modules/IPStack/udp.c new file mode 100644 index 00000000..cbdcd56b --- /dev/null +++ b/KernelLand/Modules/IPStack/udp.c @@ -0,0 +1,442 @@ +/* + * Acess2 IP Stack + * - UDP Handling + */ +#include "ipstack.h" +#include +#include "udp.h" + +#define UDP_ALLOC_BASE 0xC000 + +// === PROTOTYPES === +void UDP_Initialise(); +void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer); +void UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer); +void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, const void *Address, Uint16 Port, const void *Data, size_t Length); +// --- Client Channels +tVFS_Node *UDP_Channel_Init(tInterface *Interface); +Uint64 UDP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 UDP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); + int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data); +void UDP_Channel_Close(tVFS_Node *Node); +// --- Helpers +Uint16 UDP_int_AllocatePort(); + int UDP_int_MarkPortAsUsed(Uint16 Port); +void UDP_int_FreePort(Uint16 Port); + +// === GLOBALS === +tVFS_NodeType gUDP_NodeType = { + .Read = UDP_Channel_Read, + .Write = UDP_Channel_Write, + .IOCtl = UDP_Channel_IOCtl, + .Close = UDP_Channel_Close +}; +tMutex glUDP_Channels; +tUDPChannel *gpUDP_Channels; + +tMutex glUDP_Ports; +Uint32 gUDP_Ports[0x10000/32]; + +tSocketFile gUDP_SocketFile = {NULL, "udp", UDP_Channel_Init}; + +// === CODE === +/** + * \fn void TCP_Initialise() + * \brief Initialise the TCP Layer + */ +void UDP_Initialise() +{ + IPStack_AddFile(&gUDP_SocketFile); + //IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket, UDP_Unreachable); + IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket); +} + +/** + * \brief Scan a list of tUDPChannels and find process the first match + * \return 0 if no match was found, -1 on error and 1 if a match was found + */ +int UDP_int_ScanList(tUDPChannel *List, tInterface *Interface, void *Address, int Length, void *Buffer) +{ + tUDPHeader *hdr = Buffer; + tUDPChannel *chan; + tUDPPacket *pack; + int len; + + for(chan = List; + chan; + chan = chan->Next) + { + // Match local endpoint + if(chan->Interface && chan->Interface != Interface) continue; + if(chan->LocalPort != ntohs(hdr->DestPort)) continue; + + // Check for remote port restriction + if(chan->Remote.Port && chan->Remote.Port != ntohs(hdr->SourcePort)) + continue; + // Check for remote address restriction + if(chan->RemoteMask) + { + if(chan->Remote.AddrType != Interface->Type) + continue; + if(!IPStack_CompareAddress(Interface->Type, Address, + &chan->Remote.Addr, chan->RemoteMask) + ) + continue; + } + + Log_Log("UDP", "Recieved packet for %p", chan); + // Create the cached packet + len = ntohs(hdr->Length); + pack = malloc(sizeof(tUDPPacket) + len); + pack->Next = NULL; + memcpy(&pack->Remote.Addr, Address, IPStack_GetAddressSize(Interface->Type)); + pack->Remote.Port = ntohs(hdr->SourcePort); + pack->Remote.AddrType = Interface->Type; + pack->Length = len; + memcpy(pack->Data, hdr->Data, len); + + // Add the packet to the channel's queue + SHORTLOCK(&chan->lQueue); + if(chan->Queue) + chan->QueueEnd->Next = pack; + else + chan->QueueEnd = chan->Queue = pack; + SHORTREL(&chan->lQueue); + VFS_MarkAvaliable(&chan->Node, 1); + Mutex_Release(&glUDP_Channels); + return 1; + } + return 0; +} + +/** + * \fn void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) + * \brief Handles a packet from the IP Layer + */ +void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) +{ + tUDPHeader *hdr = Buffer; + + Log_Debug("UDP", "hdr->SourcePort = %i", ntohs(hdr->SourcePort)); + Log_Debug("UDP", "hdr->DestPort = %i", ntohs(hdr->DestPort)); + Log_Debug("UDP", "hdr->Length = %i", ntohs(hdr->Length)); + Log_Debug("UDP", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum)); + + // Check registered connections + Mutex_Acquire(&glUDP_Channels); + UDP_int_ScanList(gpUDP_Channels, Interface, Address, Length, Buffer); + Mutex_Release(&glUDP_Channels); +} + +/** + * \brief Handle an ICMP Unrechable Error + */ +void UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer) +{ + +} + +/** + * \brief Send a packet + * \param Channel Channel to send the packet from + * \param Data Packet data + * \param Length Length in bytes of packet data + */ +void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, const void *Address, Uint16 Port, const void *Data, size_t Length) +{ + tUDPHeader *hdr; + + if(Channel->Interface && Channel->Interface->Type != AddrType) return ; + + switch(AddrType) + { + case 4: + // Create the packet + hdr = malloc(sizeof(tUDPHeader)+Length); + hdr->SourcePort = htons( Channel->LocalPort ); + hdr->DestPort = htons( Port ); + hdr->Length = htons( sizeof(tUDPHeader) + Length ); + hdr->Checksum = 0; // Checksum can be zero on IPv4 + memcpy(hdr->Data, Data, Length); + // Pass on the the IPv4 Layer + // TODO: What if Channel->Interface is NULL here? + IPv4_SendPacket(Channel->Interface, *(tIPv4*)Address, IP4PROT_UDP, 0, sizeof(tUDPHeader)+Length, hdr); + // Free allocated packet + free(hdr); + break; + } +} + +// --- Client Channels +tVFS_Node *UDP_Channel_Init(tInterface *Interface) +{ + tUDPChannel *new; + new = calloc( sizeof(tUDPChannel), 1 ); + new->Interface = Interface; + new->Node.ImplPtr = new; + new->Node.NumACLs = 1; + new->Node.ACLs = &gVFS_ACL_EveryoneRW; + new->Node.Type = &gUDP_NodeType; + + Mutex_Acquire(&glUDP_Channels); + new->Next = gpUDP_Channels; + gpUDP_Channels = new; + Mutex_Release(&glUDP_Channels); + + return &new->Node; +} + +/** + * \brief Read from the channel file (wait for a packet) + */ +Uint64 UDP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tUDPChannel *chan = Node->ImplPtr; + tUDPPacket *pack; + tUDPEndpoint *ep; + int ofs, addrlen; + + if(chan->LocalPort == 0) { + Log_Notice("UDP", "Channel %p sent with no local port", chan); + return 0; + } + + while(chan->Queue == NULL) Threads_Yield(); + + for(;;) + { + VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "UDP_Channel_Read"); + SHORTLOCK(&chan->lQueue); + if(chan->Queue == NULL) { + SHORTREL(&chan->lQueue); + continue; + } + pack = chan->Queue; + chan->Queue = pack->Next; + if(!chan->Queue) { + chan->QueueEnd = NULL; + VFS_MarkAvaliable(Node, 0); // Nothing left + } + SHORTREL(&chan->lQueue); + break; + } + + // Check that the header fits + addrlen = IPStack_GetAddressSize(pack->Remote.AddrType); + ep = Buffer; + ofs = 4 + addrlen; + if(Length < ofs) { + free(pack); + Log_Notice("UDP", "Insuficient space for header in buffer (%i < %i)", (int)Length, ofs); + return 0; + } + + // Fill header + ep->Port = pack->Remote.Port; + ep->AddrType = pack->Remote.AddrType; + memcpy(&ep->Addr, &pack->Remote.Addr, addrlen); + + // Copy packet data + if(Length > ofs + pack->Length) Length = ofs + pack->Length; + memcpy((char*)Buffer + ofs, pack->Data, Length - ofs); + + // Free cached packet + free(pack); + + return Length; +} + +/** + * \brief Write to the channel file (send a packet) + */ +Uint64 UDP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + tUDPChannel *chan = Node->ImplPtr; + const tUDPEndpoint *ep; + const void *data; + int ofs; + if(chan->LocalPort == 0) return 0; + + ep = Buffer; + ofs = 2 + 2 + IPStack_GetAddressSize( ep->AddrType ); + + data = (const char *)Buffer + ofs; + + UDP_SendPacketTo(chan, ep->AddrType, &ep->Addr, ep->Port, data, (size_t)Length - ofs); + + return 0; +} + +/** + * \brief Names for channel IOCtl Calls + */ +static const char *casIOCtls_Channel[] = { + DRV_IOCTLNAMES, + "getset_localport", + "getset_remoteport", + "getset_remotemask", + "set_remoteaddr", + NULL + }; +/** + * \brief Channel IOCtls + */ +int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + tUDPChannel *chan = Node->ImplPtr; + ENTER("pNode iID pData", Node, ID, Data); + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_MISC, "UDP Channel", 0x100, casIOCtls_Channel); + + case 4: // getset_localport (returns bool success) + if(!Data) LEAVE_RET('i', chan->LocalPort); + if(!CheckMem( Data, sizeof(Uint16) ) ) { + LOG("Invalid pointer %p", Data); + LEAVE_RET('i', -1); + } + // Set port + chan->LocalPort = *(Uint16*)Data; + // Permissions check (Ports lower than 1024 are root-only) + if(chan->LocalPort != 0 && chan->LocalPort < 1024) { + if( Threads_GetUID() != 0 ) { + LOG("Attempt by non-superuser to listen on port %i", chan->LocalPort); + chan->LocalPort = 0; + LEAVE_RET('i', -1); + } + } + // Allocate a random port if requested + if( chan->LocalPort == 0 ) + chan->LocalPort = UDP_int_AllocatePort(); + else + { + // Else, mark the requested port as used + if( UDP_int_MarkPortAsUsed(chan->LocalPort) == 0 ) { + LOG("Port %i us currently in use", chan->LocalPort); + chan->LocalPort = 0; + LEAVE_RET('i', 0); + } + LEAVE_RET('i', 1); + } + LEAVE_RET('i', 1); + + case 5: // getset_remoteport (returns bool success) + if(!Data) LEAVE_RET('i', chan->Remote.Port); + if(!CheckMem( Data, sizeof(Uint16) ) ) { + LOG("Invalid pointer %p", Data); + LEAVE_RET('i', -1); + } + chan->Remote.Port = *(Uint16*)Data; + return 1; + + case 6: // getset_remotemask (returns bool success) + if(!Data) LEAVE_RET('i', chan->RemoteMask); + if(!CheckMem(Data, sizeof(int))) LEAVE_RET('i', -1); + if( !chan->Interface ) { + LOG("Can't set remote mask on NULL interface"); + LEAVE_RET('i', -1); + } + if( *(int*)Data > IPStack_GetAddressSize(chan->Interface->Type) ) + LEAVE_RET('i', -1); + chan->RemoteMask = *(int*)Data; + return 1; + + case 7: // set_remoteaddr (returns bool success) + if( !chan->Interface ) { + LOG("Can't set remote address on NULL interface"); + LEAVE_RET('i', -1); + } + if(!CheckMem(Data, IPStack_GetAddressSize(chan->Interface->Type))) { + LOG("Invalid pointer"); + LEAVE_RET('i', -1); + } + memcpy(&chan->Remote.Addr, Data, IPStack_GetAddressSize(chan->Interface->Type)); + return 0; + } + LEAVE_RET('i', 0); +} + +/** + * \brief Close and destroy an open channel + */ +void UDP_Channel_Close(tVFS_Node *Node) +{ + tUDPChannel *chan = Node->ImplPtr; + tUDPChannel *prev; + + // Remove from the main list first + Mutex_Acquire(&glUDP_Channels); + if(gpUDP_Channels == chan) + gpUDP_Channels = gpUDP_Channels->Next; + else + { + for(prev = gpUDP_Channels; + prev->Next && prev->Next != chan; + prev = prev->Next); + if(!prev->Next) + Log_Warning("UDP", "Bookeeping Fail, channel %p is not in main list", chan); + else + prev->Next = prev->Next->Next; + } + Mutex_Release(&glUDP_Channels); + + // Clear Queue + SHORTLOCK(&chan->lQueue); + while(chan->Queue) + { + tUDPPacket *tmp; + tmp = chan->Queue; + chan->Queue = tmp->Next; + free(tmp); + } + SHORTREL(&chan->lQueue); + + // Free channel structure + free(chan); +} + +/** + * \return Port Number on success, or zero on failure + */ +Uint16 UDP_int_AllocatePort() +{ + int i; + Mutex_Acquire(&glUDP_Ports); + // Fast Search + for( i = UDP_ALLOC_BASE; i < 0x10000; i += 32 ) + if( gUDP_Ports[i/32] != 0xFFFFFFFF ) + break; + if(i == 0x10000) return 0; + for( ;; i++ ) + { + if( !(gUDP_Ports[i/32] & (1 << (i%32))) ) + return i; + } + Mutex_Release(&glUDP_Ports); +} + +/** + * \brief Allocate a specific port + * \return Boolean Success + */ +int UDP_int_MarkPortAsUsed(Uint16 Port) +{ + Mutex_Acquire(&glUDP_Ports); + if( gUDP_Ports[Port/32] & (1 << (Port%32)) ) { + return 0; + Mutex_Release(&glUDP_Ports); + } + gUDP_Ports[Port/32] |= 1 << (Port%32); + Mutex_Release(&glUDP_Ports); + return 1; +} + +/** + * \brief Free an allocated port + */ +void UDP_int_FreePort(Uint16 Port) +{ + Mutex_Acquire(&glUDP_Ports); + gUDP_Ports[Port/32] &= ~(1 << (Port%32)); + Mutex_Release(&glUDP_Ports); +} diff --git a/KernelLand/Modules/IPStack/udp.h b/KernelLand/Modules/IPStack/udp.h new file mode 100644 index 00000000..39147894 --- /dev/null +++ b/KernelLand/Modules/IPStack/udp.h @@ -0,0 +1,59 @@ +/* + * Acess2 IP Stack + * - UDP Definitions + */ +#ifndef _UDP_H_ +#define _UDP_H_ + +#include "ipstack.h" +#include "ipv4.h" + +typedef struct sUDPHeader tUDPHeader; +typedef struct sUDPEndpoint tUDPEndpoint; +typedef struct sUDPPacket tUDPPacket; +typedef struct sUDPChannel tUDPChannel; + +struct sUDPHeader +{ + Uint16 SourcePort; + Uint16 DestPort; + Uint16 Length; + Uint16 Checksum; + Uint8 Data[]; +}; + +struct sUDPEndpoint +{ + Uint16 Port; + Uint16 AddrType; + union { + tIPv4 v4; + tIPv6 v6; + } Addr; +}; + +struct sUDPPacket +{ + struct sUDPPacket *Next; + tUDPEndpoint Remote; + size_t Length; + Uint8 Data[]; +}; + +struct sUDPChannel +{ + struct sUDPChannel *Next; + tInterface *Interface; + Uint16 LocalPort; + + tUDPEndpoint Remote; // Only accept packets form this address/port pair + int RemoteMask; // Mask on the address + + tVFS_Node Node; + tShortSpinlock lQueue; + tUDPPacket * volatile Queue; + tUDPPacket *QueueEnd; +}; + +#endif + diff --git a/KernelLand/Modules/Input/Makefile.tpl b/KernelLand/Modules/Input/Makefile.tpl new file mode 100644 index 00000000..c36c928b --- /dev/null +++ b/KernelLand/Modules/Input/Makefile.tpl @@ -0,0 +1,3 @@ +CATEGORY = Input + +-include ../../Makefile.tpl diff --git a/KernelLand/Modules/Input/PS2KbMouse/8042.c b/KernelLand/Modules/Input/PS2KbMouse/8042.c new file mode 100644 index 00000000..e11bc74f --- /dev/null +++ b/KernelLand/Modules/Input/PS2KbMouse/8042.c @@ -0,0 +1,104 @@ +/* + * Acess2 + * - By thePowersGang (John Hodge) + * + * 8042 (or comaptible) Driver + */ +#include +#include "common.h" + +// === PROTOTYPES === +void KBC8042_Init(void); +void KBC8042_KeyboardHandler(int IRQ, void *Ptr); +void KBC8042_MouseHandler(int IRQ, void *Ptr); +void KBC8042_EnableMouse(void); +static inline void KBC8042_SendDataAlt(Uint8 data); +static inline void KBC8042_SendData(Uint8 data); +static inline Uint8 KBC8042_ReadData(void); +static void KBC8042_SendMouseCommand(Uint8 cmd); + +// === CODE === +void KBC8042_Init(void) +{ + IRQ_AddHandler(1, KBC8042_KeyboardHandler, NULL); + IRQ_AddHandler(12, KBC8042_MouseHandler, NULL); // Set IRQ + + { + Uint8 temp; + // Attempt to get around a strange bug in Bochs/Qemu by toggling + // the controller on and off + temp = inb(0x61); + outb(0x61, temp | 0x80); + outb(0x61, temp & 0x7F); + inb(0x60); // Clear keyboard buffer + } +} + +void KBC8042_KeyboardHandler(int IRQ, void *Ptr) +{ + Uint8 scancode; + +// Log("KBC8042_KeyboardHandler: (IRQ=%i, Ptr=%p)", IRQ, Ptr); + + scancode = inb(0x60); + KB_HandleScancode( scancode ); +} + +void KBC8042_MouseHandler(int IRQ, void *Ptr) +{ + PS2Mouse_HandleInterrupt( inb(0x60) ); +} + +void KBC8042_SetLEDs(Uint8 leds) +{ + while( inb(0x64) & 2 ); // Wait for bit 2 to unset + outb(0x60, 0xED); // Send update command + + while( inb(0x64) & 2 ); // Wait for bit 2 to unset + outb(0x60, leds); +} + +void KBC8042_EnableMouse(void) +{ + Uint8 status; + Log_Log("8042", "Enabling Mouse..."); + + // Enable AUX PS/2 + KBC8042_SendDataAlt(0xA8); + + // Enable AUX PS/2 (Compaq Status Byte) + KBC8042_SendDataAlt(0x20); // Send Command + status = KBC8042_ReadData(); // Get Status + status &= ~0x20; // Clear "Disable Mouse Clock" + status |= 0x02; // Set IRQ12 Enable + KBC8042_SendDataAlt(0x60); // Send Command + KBC8042_SendData(status); // Set Status + + //mouseSendCommand(0xF6); // Set Default Settings + KBC8042_SendMouseCommand(0xF4); // Enable Packets +} + +static inline void KBC8042_SendDataAlt(Uint8 data) +{ + int timeout=100000; + while( timeout-- && inb(0x64) & 2 ); // Wait for Flag to clear + outb(0x64, data); // Send Command +} +static inline void KBC8042_SendData(Uint8 data) +{ + int timeout=100000; + while( timeout-- && inb(0x64) & 2 ); // Wait for Flag to clear + outb(0x60, data); // Send Command +} +static inline Uint8 KBC8042_ReadData(void) +{ + int timeout=100000; + while( timeout-- && (inb(0x64) & 1) == 0); // Wait for Flag to set + return inb(0x60); +} +static inline void KBC8042_SendMouseCommand(Uint8 cmd) +{ + KBC8042_SendDataAlt(0xD4); + KBC8042_SendData(cmd); +} + diff --git a/KernelLand/Modules/Input/PS2KbMouse/Makefile b/KernelLand/Modules/Input/PS2KbMouse/Makefile new file mode 100644 index 00000000..306e2b41 --- /dev/null +++ b/KernelLand/Modules/Input/PS2KbMouse/Makefile @@ -0,0 +1,8 @@ +# +# + +OBJ = main.o kb.o ps2mouse.o +OBJ += 8042.o pl050.o +NAME = PS2KbMouse + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Input/PS2KbMouse/common.h b/KernelLand/Modules/Input/PS2KbMouse/common.h new file mode 100644 index 00000000..7cbffe9c --- /dev/null +++ b/KernelLand/Modules/Input/PS2KbMouse/common.h @@ -0,0 +1,26 @@ +/* + * Acess2 + * + * PS2 Keyboard/Mouse Driver + * + * common.h + * - Shared definitions + */ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +extern int KB_Install(char **Arguments); +extern int PS2Mouse_Install(char **Arguments); + +extern void KBC8042_Init(void); +extern void KBC8042_EnableMouse(void); + +extern void PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ); +extern void PL050_EnableMouse(void); + +extern void KB_HandleScancode(Uint8 scancode); +extern void PS2Mouse_HandleInterrupt(Uint8 InputByte); + +extern void (*gpMouse_EnableFcn)(void); + +#endif diff --git a/KernelLand/Modules/Input/PS2KbMouse/kb.c b/KernelLand/Modules/Input/PS2KbMouse/kb.c new file mode 100644 index 00000000..a121b611 --- /dev/null +++ b/KernelLand/Modules/Input/PS2KbMouse/kb.c @@ -0,0 +1,251 @@ +/* + * Acess2 + * PS2 Keyboard Driver + */ +#include +#include +#include +#include +#include +#include "kb_kbdus.h" + +// === CONSTANTS === +#define KB_BUFFER_SIZE 1024 +#define USE_KERNEL_MAGIC 1 + +// === IMPORTS === +extern void Threads_ToggleTrace(int TID); +extern void Threads_Dump(void); +extern void Heap_Stats(void); + +// === PROTOTYPES === + int KB_Install(char **Arguments); +void KB_HandleScancode(Uint8 scancode); +void KB_UpdateLEDs(void); + int KB_IOCtl(tVFS_Node *Node, int Id, void *Data); + +// === GLOBALS === +tVFS_NodeType gKB_NodeType = { + .IOCtl = KB_IOCtl +}; +tDevFS_Driver gKB_DevInfo = { + NULL, "PS2Keyboard", + { .Type = &gKB_NodeType } +}; +tKeybardCallback gKB_Callback = NULL; +Uint32 **gpKB_Map = gpKBDUS; +Uint8 gbaKB_States[3][256]; + int gbKB_ShiftState = 0; + int gbKB_CapsState = 0; + int gbKB_KeyUp = 0; + int giKB_KeyLayer = 0; +#if USE_KERNEL_MAGIC + int gbKB_MagicState = 0; + int giKB_MagicAddress = 0; + int giKB_MagicAddressPos = 0; +#endif + +// === CODE === +/** + * \brief Install the keyboard driver + */ +int KB_Install(char **Arguments) +{ + DevFS_AddDevice( &gKB_DevInfo ); + //Log("KB_Install: Installed"); + return MODULE_ERR_OK; +} + +/** + * \brief Called on a keyboard IRQ + * \param IRQNum IRQ number (unused) + */ +void KB_HandleScancode(Uint8 scancode) +{ + Uint32 ch; + int bCaseSwitch = (gbKB_ShiftState != 0) != (gbKB_CapsState != 0); + + //Log_Debug("Keyboard", "scancode = %02x", scancode); + + // Ignore ACKs + if(scancode == 0xFA) { + // Oh man! This is anarchic (I'm leaving it here to represent + // the mess that Acess once was) + //kb_lastChar = KB_ACK; + return; + } + + // Layer +1 + if(scancode == 0xE0) { + giKB_KeyLayer = 1; + return; + } + // Layer +2 + if(scancode == 0xE1) { + giKB_KeyLayer = 2; + return; + } + + #if KB_ALT_SCANCODES + if(scancode == 0xF0) + { + gbKB_KeyUp = 1; + return; + } + #else + if(scancode & 0x80) + { + scancode &= 0x7F; + gbKB_KeyUp = 1; + } + #endif + + if( gKB_Callback ) + gKB_Callback( (giKB_KeyLayer << 8) | scancode | KEY_ACTION_RAWSYM ); + + // Translate + ch = gpKB_Map[giKB_KeyLayer*2+bCaseSwitch][scancode]; + // - Unknown characters in the shift layer fall through to lower + if(bCaseSwitch && ch == 0) + ch = gpKB_Map[giKB_KeyLayer*2][scancode]; + // Check for unknown key + if(!ch) + { + if(!gbKB_KeyUp) + Log_Warning("Keyboard", "UNK %i %x", giKB_KeyLayer, scancode); +// return ; + // Can pass through to ensure each raw message has a up/down with it + } + + // Key Up? + if (gbKB_KeyUp) + { + gbKB_KeyUp = 0; + gbaKB_States[giKB_KeyLayer][scancode] = 0; // Unset key state flag + + #if USE_KERNEL_MAGIC + if(ch == KEY_LCTRL) gbKB_MagicState &= ~1; + if(ch == KEY_LALT) gbKB_MagicState &= ~2; + #endif + + if(ch == KEY_LSHIFT) gbKB_ShiftState &= ~1; + if(ch == KEY_RSHIFT) gbKB_ShiftState &= ~2; + + // Call callback + if(gKB_Callback) gKB_Callback( ch | KEY_ACTION_RELEASE ); + + // Reset Layer + giKB_KeyLayer = 0; + return; + } + + // Refire? + if( gbaKB_States[giKB_KeyLayer][scancode] == 1 ) + { + if(gKB_Callback) gKB_Callback(ch | KEY_ACTION_REFIRE); + giKB_KeyLayer = 0; + return ; + } + + // Set the bit relating to the key + gbaKB_States[giKB_KeyLayer][scancode] = 1; + // Set shift key bits + if(ch == KEY_LSHIFT) gbKB_ShiftState |= 1; + if(ch == KEY_RSHIFT) gbKB_ShiftState |= 2; + + // Check for Caps Lock + if(ch == KEY_CAPSLOCK) { + gbKB_CapsState = !gbKB_CapsState; + KB_UpdateLEDs(); + } + + // --- Check for Kernel Magic Combos + #if USE_KERNEL_MAGIC + if(ch == KEY_LCTRL) { + gbKB_MagicState |= 1; + //Log_Log("Keyboard", "Kernel Magic LCTRL Down\n"); + } + if(ch == KEY_LALT) { + gbKB_MagicState |= 2; + //Log_Log("Keyboard", "Kernel Magic LALT Down\n"); + } + if(gbKB_MagicState == 3) + { + switch(ch) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': case 'a': case 'b': + case 'c': case 'd': case 'e': case 'f': + { + char str[4] = {'0', 'x', ch, 0}; + if(giKB_MagicAddressPos == BITS/4) return; + giKB_MagicAddress |= atoi(str) << giKB_MagicAddressPos; + giKB_MagicAddressPos ++; + } + return; + + // Instruction Tracing + case 't': + Log("Toggle instruction tracing on %i\n", giKB_MagicAddress); + Threads_ToggleTrace( giKB_MagicAddress ); + giKB_MagicAddress = 0; giKB_MagicAddressPos = 0; + return; + + // Thread List Dump + case 'p': Threads_Dump(); return; + // Heap Statistics + case 'h': Heap_Stats(); return; + // Dump Structure + case 's': return; + } + } + #endif + + if(gKB_Callback) + gKB_Callback(ch | KEY_ACTION_PRESS); + + // Reset Layer + giKB_KeyLayer = 0; +} + +/** + * \fn void KB_UpdateLEDs(void) + * \brief Updates the status of the keyboard LEDs + */ +void KB_UpdateLEDs(void) +{ + Uint8 leds; + + leds = (gbKB_CapsState ? 4 : 0); + + // TODO: Update LEDS + Log_Warning("Keyboard", "TODO: Update LEDs"); +} + +static const char *csaIOCTL_NAMES[] = {DRV_IOCTLNAMES, DRV_KEYBAORD_IOCTLNAMES, NULL}; + +/** + * \fn int KB_IOCtl(tVFS_Node *Node, int Id, void *Data) + * \brief Calls an IOCtl Command + */ +int KB_IOCtl(tVFS_Node *Node, int Id, void *Data) +{ + switch(Id) + { + BASE_IOCTLS(DRV_TYPE_KEYBOARD, "KB", 0x100, csaIOCTL_NAMES); + + // Sets the Keyboard Callback + case KB_IOCTL_SETCALLBACK: + // Sanity Check + if((Uint)Data < KERNEL_BASE) return 0; + // Can only be set once + if(gKB_Callback != NULL) return 0; + // Set Callback + gKB_Callback = Data; + return 1; + + default: + return 0; + } +} diff --git a/KernelLand/Modules/Input/PS2KbMouse/kb_kbdus.h b/KernelLand/Modules/Input/PS2KbMouse/kb_kbdus.h new file mode 100644 index 00000000..5df32573 --- /dev/null +++ b/KernelLand/Modules/Input/PS2KbMouse/kb_kbdus.h @@ -0,0 +1,61 @@ + +#ifndef _KBDUS_H +#define _KBDUS_H + +// - Base (NO PREFIX) +Uint32 gpKBDUS1[256] = { + 0, + KEY_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', // 0x01 - 0x0e + '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', // 0x0f - 0x1c + KEY_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', // 0x1d - 0x28 + '`', KEY_LSHIFT,'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KEY_RSHIFT, // 0x29 - 0x3e + KEY_KPSTAR, + KEY_LALT, ' ', KEY_CAPSLOCK, + KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, + KEY_NUMLOCK, KEY_SCROLLLOCK, + KEY_KPHOME, KEY_KPUP, KEY_KPPGUP, KEY_KPMINUS, + KEY_KPLEFT, KEY_KP5, KEY_KPRIGHT, KEY_KPPLUS, + KEY_KPEND, KEY_KPDOWN, KEY_KPPGDN, + KEY_KPINS, KEY_KPDEL, + 0, 0, 0, KEY_F11, KEY_F12, 0, 0, 0, 0, 0, 0, 0, +/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; +// Shift Key pressed +Uint32 gpKBDUS1s[256] = { + 0, + KEY_ESC, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', // 0x01 - 0x0e + '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', // 0x0f - 0x1c + KEY_LCTRL, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':','"', // 0x1d - 0x28 + '~', KEY_LSHIFT,'|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', KEY_RSHIFT, // 0x29 - 0x3e + 0 + }; +// - 0xE0 Prefixed +Uint32 gpKBDUS2[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F +/*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-F +/*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_KPENTER, KEY_RCTRL, 0, 0, +/*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*30*/ 0, 0, 0, 0, 0, KEY_KPSLASH, 0, 0, KEY_RALT, 0, 0, 0, 0, 0, 0, 0, +/*40*/ 0, 0, 0, 0, 0, 0, 0, KEY_HOME, KEY_UP, KEY_PGUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END, +/*50*/ KEY_DOWN, KEY_PGDOWN, KEY_INS, KEY_DEL, 0, 0, 0, 0, 0, 0, 0, KEY_LWIN, KEY_RWIN, KEY_MENU, 0, 0, +/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; +// - 0xE1 Prefixed +Uint32 gpKBDUS3[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F +/*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-F +/*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PAUSE, 0, 0, +/*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*30*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*50*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + + +Uint32 *gpKBDUS[6] = { gpKBDUS1, gpKBDUS1s, gpKBDUS2, gpKBDUS2, gpKBDUS3, gpKBDUS3 }; + +#endif diff --git a/KernelLand/Modules/Input/PS2KbMouse/main.c b/KernelLand/Modules/Input/PS2KbMouse/main.c new file mode 100644 index 00000000..c5ad4e8b --- /dev/null +++ b/KernelLand/Modules/Input/PS2KbMouse/main.c @@ -0,0 +1,48 @@ +/* + * Acess2 + * + * PS/2 Keboard / Mouse Driver + */ +#include +#include +#include "common.h" + +// === IMPORTS === +// TODO: Allow runtime/compile-time switching +// Maybe PCI will have it? +// Integrator-CP +#if 0 +#define KEYBOARD_IRQ 3 +#define KEYBOARD_BASE 0x18000000 +#define MOUSE_IRQ 4 +#define MOUSE_BASE 0x19000000 +#endif +// Realview +#if 1 +#define KEYBOARD_IRQ 20 +#define KEYBOARD_BASE 0x10006000 +#define MOUSE_IRQ 21 +#define MOUSE_BASE 0x10007000 +#endif + +// === PROTOTYPES === + int PS2_Install(char **Arguments); + +// === GLOBALS === +MODULE_DEFINE(0, 0x0100, Input_PS2KbMouse, PS2_Install, NULL, NULL); // Shuts the makefile up +MODULE_DEFINE(0, 0x0100, PS2Keyboard, KB_Install, NULL, "Input_PS2KbMouse", NULL); +MODULE_DEFINE(0, 0x0100, PS2Mouse, PS2Mouse_Install, NULL, "Input_PS2KbMouse", NULL); + +// === CODE === +int PS2_Install(char **Arguments) +{ + #if ARCHDIR_is_x86 || ARCHDIR_is_x86_64 + KBC8042_Init(); + gpMouse_EnableFcn = KBC8042_EnableMouse; + #elif ARCHDIR_is_armv7 + PL050_Init(KEYBOARD_BASE, KEYBOARD_IRQ, MOUSE_BASE, MOUSE_IRQ); + gpMouse_EnableFcn = PL050_EnableMouse; + #endif + + return MODULE_ERR_OK; +} diff --git a/KernelLand/Modules/Input/PS2KbMouse/pl050.c b/KernelLand/Modules/Input/PS2KbMouse/pl050.c new file mode 100644 index 00000000..839c0bda --- /dev/null +++ b/KernelLand/Modules/Input/PS2KbMouse/pl050.c @@ -0,0 +1,129 @@ +/* + * Acess2 + * - By thePowersGang (John Hodge) + * + * PL050 (or comaptible) Driver + */ +#define DEBUG 1 + +#include +#include "common.h" + +// === CONSTANTS === +#define PL050_TXBUSY 0x20 + +// === PROTOTYPES === +void PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ); +void PL050_KeyboardHandler(int IRQ, void *Ptr); +void PL050_MouseHandler(int IRQ, void *Ptr); +void PL050_EnableMouse(void); +static inline void PL050_WriteMouseData(Uint8 data); +static inline void PL050_WriteKeyboardData(Uint8 data); +static inline Uint8 PL050_ReadMouseData(void); +static inline Uint8 PL050_ReadKeyboardData(void); + +// === GLOBALS === +Uint32 *gpPL050_KeyboardBase; +Uint32 *gpPL050_MouseBase; + +// === CODE === +void PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ) +{ + if( KeyboardBase ) { + LOG("KeyboardBase = 0x%x", KeyboardBase); + gpPL050_KeyboardBase = (void*)MM_MapHWPages(KeyboardBase, 1); + LOG("gpPL050_KeyboardBase = %p", gpPL050_KeyboardBase); + IRQ_AddHandler(KeyboardIRQ, PL050_KeyboardHandler, NULL); + + gpPL050_KeyboardBase[0] = 0x10; + } + if( MouseBase ) { + gpPL050_MouseBase = (void*)MM_MapHWPages(MouseBase, 1); + IRQ_AddHandler(MouseIRQ, PL050_MouseHandler, NULL); + + gpPL050_MouseBase[0] = 0x10; + } +} + +void PL050_KeyboardHandler(int IRQ, void *Ptr) +{ + Uint8 scancode; + + scancode = PL050_ReadKeyboardData(); + KB_HandleScancode( scancode ); +} + +void PL050_MouseHandler(int IRQ, void *Ptr) +{ + PS2Mouse_HandleInterrupt( PL050_ReadMouseData() ); +} + +void PL050_SetLEDs(Uint8 leds) +{ + PL050_WriteKeyboardData(0xED); + PL050_WriteKeyboardData(leds); +} + +void PL050_EnableMouse(void) +{ + Log_Log("PL050", "Enabling Mouse..."); + + //PL050_WriteMouseData(0xD4); + //PL050_WriteMouseData(0xF6); // Set Default Settings + PL050_WriteMouseData(0xD4); + PL050_WriteMouseData(0xF4); // Enable Packets + LOG("Done"); +} + +static inline void PL050_WriteMouseData(Uint8 Data) +{ + int timeout = 10000; + + if( !gpPL050_MouseBase ) { + Log_Error("PL050", "Mouse disabled (gpPL050_MouseBase = NULL)"); + return ; + } + + ENTER("xData", Data); + + while( --timeout && (gpPL050_MouseBase[1] & PL050_TXBUSY) ); + if(timeout) + gpPL050_MouseBase[2] = Data; + else + Log_Error("PL050", "Write to mouse timed out"); + LEAVE('-'); +} + +static inline Uint8 PL050_ReadMouseData(void) +{ + if( !gpPL050_MouseBase ) { + Log_Error("PL050", "Mouse disabled (gpPL050_MouseBase = NULL)"); + return 0; + } + return gpPL050_MouseBase[2]; +} +static inline void PL050_WriteKeyboardData(Uint8 Data) +{ + int timeout = 10000; + + if( !gpPL050_KeyboardBase ) { + Log_Error("PL050", "Keyboard disabled (gpPL050_KeyboardBase = NULL)"); + return ; + } + + while( --timeout && gpPL050_KeyboardBase[1] & PL050_TXBUSY ); + if(timeout) + gpPL050_KeyboardBase[2] = Data; + else + Log_Error("PL050", "Write to keyboard timed out"); +} +static inline Uint8 PL050_ReadKeyboardData(void) +{ + if( !gpPL050_KeyboardBase ) { + Log_Error("PL050", "Keyboard disabled (gpPL050_KeyboardBase = NULL)"); + return 0; + } + + return gpPL050_KeyboardBase[2]; +} + diff --git a/KernelLand/Modules/Input/PS2KbMouse/ps2mouse.c b/KernelLand/Modules/Input/PS2KbMouse/ps2mouse.c new file mode 100644 index 00000000..44be3b7b --- /dev/null +++ b/KernelLand/Modules/Input/PS2KbMouse/ps2mouse.c @@ -0,0 +1,206 @@ +/* + * Acess2 Mouse Driver + */ +#define DEBUG 0 +#include +#include +#include +#include +#include +#include +#include "common.h" + +// == CONSTANTS == +#define NUM_AXIES 2 // X+Y +#define NUM_BUTTONS 5 // Left, Right, Scroll Click, Scroll Up, Scroll Down + +// == PROTOTYPES == +// - Internal - + int PS2Mouse_Install(char **Arguments); +void PS2Mouse_HandleInterrupt(Uint8 InputByte); +// - Filesystem - +Uint64 PS2Mouse_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +int PS2Mouse_IOCtl(tVFS_Node *Node, int ID, void *Data); + +// == GLOBALS == +void (*gpMouse_EnableFcn)(void); +// - Settings + int giMouse_Sensitivity = 1; + int giMouse_MaxX = 640, giMouse_MaxY = 480; +// - File Data +Uint8 gMouse_FileData[sizeof(tJoystick_FileHeader) + NUM_AXIES*sizeof(tJoystick_Axis) + NUM_BUTTONS]; +tJoystick_FileHeader *gMouse_FileHeader = (void *)gMouse_FileData; +tJoystick_Axis *gMouse_Axies; +Uint8 *gMouse_Buttons; +tJoystick_Callback gMouse_Callback; + int gMouse_CallbackArg; + int giMouse_AxisLimits[2]; +// - Internal State + int giMouse_Cycle = 0; // IRQ Position +Uint8 gaMouse_Bytes[4] = {0,0,0,0}; +// - Driver definition +tVFS_NodeType gMouse_NodeType = { + .Read = PS2Mouse_Read, + .IOCtl = PS2Mouse_IOCtl +}; +tDevFS_Driver gMouse_DriverStruct = { + NULL, "PS2Mouse", + { + .NumACLs = 1, .ACLs = &gVFS_ACL_EveryoneRX, + .Type = &gMouse_NodeType + } +}; + +// == CODE == +int PS2Mouse_Install(char **Arguments) +{ + + + // Set up variables + gMouse_Axies = (void*)&gMouse_FileData[4]; + gMouse_Buttons = (void*)&gMouse_Axies[NUM_AXIES]; + + gMouse_FileHeader->NAxies = 2; gMouse_FileHeader->NButtons = 3; + gMouse_Axies[0].MinValue = -10; gMouse_Axies[0].MaxValue = 10; + gMouse_Axies[1].MinValue = -10; gMouse_Axies[1].MaxValue = 10; + + // Initialise Mouse Controller + giMouse_Cycle = 0; // Set Current Cycle position + gpMouse_EnableFcn(); + + DevFS_AddDevice(&gMouse_DriverStruct); + + return MODULE_ERR_OK; +} + +/* Handle Mouse Interrupt + */ +void PS2Mouse_HandleInterrupt(Uint8 InputByte) +{ + Uint8 flags; + int d[2], d_accel[2]; + int i; + + // Gather mouse data + gaMouse_Bytes[giMouse_Cycle] = InputByte; + LOG("gaMouse_Bytes[%i] = 0x%02x", gMouse_Axies[0].MaxValue); + // - If bit 3 of the first byte is not set, it's not a valid packet? + if(giMouse_Cycle == 0 && !(gaMouse_Bytes[0] & 0x08) ) + return ; + giMouse_Cycle++; + if(giMouse_Cycle < 3) + return ; + + giMouse_Cycle = 0; + + // Actual Processing (once we have all bytes) + flags = gaMouse_Bytes[0]; + + LOG("flags = 0x%x", flags); + + // Check for X/Y Overflow + if(flags & 0xC0) return; + + // Calculate dX and dY + d[0] = gaMouse_Bytes[1]; if(flags & 0x10) d[0] = -(256-d[0]); // x + d[1] = gaMouse_Bytes[2]; if(flags & 0x20) d[1] = -(256-d[1]); // y + d[1] = -d[1]; // Y is negated + LOG("RAW dx=%i, dy=%i\n", d[0], d[1]); + // Apply scaling + // TODO: Apply a form of curve to the mouse movement (dx*log(dx), dx^k?) + // TODO: Independent sensitivities? + // TODO: Disable acceleration via a flag? + d_accel[0] = d[0]*giMouse_Sensitivity; + d_accel[1] = d[1]*giMouse_Sensitivity; + + // Set Buttons (Primary) + for( i = 0; i < 3; i ++ ) + { + Uint8 newVal = (flags & (1 << i)) ? 0xFF : 0; + if(newVal != gMouse_Buttons[i]) { + if( gMouse_Callback ) + gMouse_Callback(gMouse_CallbackArg, 0, i, newVal - gMouse_Buttons[i]); + gMouse_Buttons[i] = newVal; + } + } + + // Update X and Y Positions + for( i = 0; i < 2; i ++ ) + { + Sint16 newCursor = 0; + if( giMouse_AxisLimits[i] ) + newCursor = MIN( MAX(0, gMouse_Axies[i].CursorPos + d_accel[i]), giMouse_AxisLimits[i] );; + + if( gMouse_Callback ) + { + if(giMouse_AxisLimits[i] && gMouse_Axies[i].CursorPos != newCursor) + gMouse_Callback(gMouse_CallbackArg, 1, i, newCursor - gMouse_Axies[i].CursorPos); + if(!giMouse_AxisLimits[i] && gMouse_Axies[i].CurValue != d_accel[i]) + gMouse_Callback(gMouse_CallbackArg, 1, i, d_accel[i] - gMouse_Axies[i].CurValue); + } + + gMouse_Axies[i].CurValue = d_accel[i]; + gMouse_Axies[i].CursorPos = newCursor; + } + +// Log("Mouse at %ix%i", gMouse_Axies[0].CursorPos, gMouse_Axies[1].CursorPos); + + VFS_MarkAvaliable(&gMouse_DriverStruct.RootNode, 1); +} + +/* Read mouse state (coordinates) + */ +Uint64 PS2Mouse_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + if(Offset > sizeof(gMouse_FileData)) return 0; + if(Length > sizeof(gMouse_FileData)) Length = sizeof(gMouse_FileData); + if(Offset + Length > sizeof(gMouse_FileData)) Length = sizeof(gMouse_FileData) - Offset; + + memcpy(Buffer, &gMouse_FileData[Offset], Length); + + VFS_MarkAvaliable(Node, 0); + return Length; +} + +static const char *csaIOCtls[] = {DRV_IOCTLNAMES, DRV_JOY_IOCTLNAMES, NULL}; +/* Handle messages to the device + */ +int PS2Mouse_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + tJoystick_NumValue *info = Data; + + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_JOYSTICK, "PS2Mouse", 0x100, csaIOCtls); + + case JOY_IOCTL_SETCALLBACK: // TODO: Implement + return -1; + + case JOY_IOCTL_SETCALLBACKARG: // TODO: Implement + return -1; + + case JOY_IOCTL_GETSETAXISLIMIT: + if(!info) return 0; + if(info->Num < 0 || info->Num >= 2) return 0; + if(info->Value != -1) + giMouse_AxisLimits[info->Num] = info->Value; + return giMouse_AxisLimits[info->Num]; + + case JOY_IOCTL_GETSETAXISPOSITION: + if(!info) return 0; + if(info->Num < 0 || info->Num >= 2) return 0; + if(info->Value != -1) + gMouse_Axies[info->Num].CursorPos = info->Value; + return gMouse_Axies[info->Num].CursorPos; + + case JOY_IOCTL_GETSETAXISFLAGS: + return -1; + + case JOY_IOCTL_GETSETBUTTONFLAGS: + return -1; + + default: + return 0; + } +} + diff --git a/KernelLand/Modules/Interfaces/EDI/Makefile b/KernelLand/Modules/Interfaces/EDI/Makefile new file mode 100644 index 00000000..a93a8b7c --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/Makefile @@ -0,0 +1,10 @@ +# +# EDI - Extensible Driver Interface +# +# Acess Interface + + +OBJ = main.o edi.o +NAME = EDI + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Interfaces/EDI/edi/acess-edi.h b/KernelLand/Modules/Interfaces/EDI/edi/acess-edi.h new file mode 100644 index 00000000..40713886 --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/edi/acess-edi.h @@ -0,0 +1,41 @@ +/*! \file acess-edi.h + * \brief Acess Specific EDI Objects + * + * Contains documentation and information for + * - Timers + */ + +/* Copyright (c) 2006 John Hodge + * Permission is granted to copy, distribute and/or modify this document + * under the terms of the GNU Free Documentation License, Version 1.2 + * or any later version published by the Free Software Foundation; + * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + * Texts. A copy of the license is included in the file entitled "COPYING". */ + +#ifndef ACESS_EDI_H +#define ACESS_EDI_H + +#include "edi_objects.h" + +/// \brief Name of Acess EDI Time Class +#define ACESS_TIMER_CLASS "ACESSEDI-TIMER" + +#ifndef IMPLEMENTING_EDI +/*! \brief int32_t ACESSEDI-TIMER.init_timer(uint32_t Delay, void (*Callback)(int), int Arg); + * + * Takes a timer pointer and intialises the timer object to fire after \a Delay ms + * When the timer fires, \a Callback is called with \a Arg passed to it. + */ +EDI_DEFVAR int32_t (*init_timer)(object_pointer port_object, uint32_t Delay, void (*fcn)(int), int arg); + +/*! \brief void ACESSEDI-TIMER.disable_timer(); + * + * Disables the timer and prevents it from firing + * After this has been called, the timer can then be initialised again. + */ +EDI_DEFVAR void (*disable_timer)(object_pointer port_object); + + +#endif // defined(IMPLEMENTING_EDI) + +#endif diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi.h b/KernelLand/Modules/Interfaces/EDI/edi/edi.h new file mode 100644 index 00000000..273f7a3d --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/edi/edi.h @@ -0,0 +1,114 @@ +#ifndef EDI_H + +/* Copyright (c) 2006 Eli Gottlieb. + * Permission is granted to copy, distribute and/or modify this document + * under the terms of the GNU Free Documentation License, Version 1.2 + * or any later version published by the Free Software Foundation; + * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + * Texts. A copy of the license is included in the file entitled "COPYING". */ + +#define EDI_H +/*! \file edi.h + * \brief The unitive EDI header to include others, start EDI, and stop EDI. + * + * Data structures and algorithms this header represents: + * DATA STRUCTURE: CLASS QUOTAS - The runtime and the driver have the right to set a quota on how many objects of a given class + * owned by that party the other may construct. These quotas are kept internally by the driver or runtime, are optional and are + * exposed to the other party via the quota() function (for quotas of runtime-owned classes) and the k_quota() function pointer given + * to the runtime by the driver. + * + * ALGORITHMS: INITIALIZATION AND SHUTDOWN - On initialization of the runtime's EDI environment for this driver it calls the + * driver's driver_init() routine (which must match driver_init_t) to initialize the driver with a list of EDI objects the runtime + * thinks the driver should run with. The driver then initializes. This can include calling edi_negotiate_resources() to try and + * obtain more or different objects. Eventually driver_init() returns an edi_initialization_t structure containing its quota + * function and the list of classes belonging to the driver which the runtime can construct. Either the driver or the runtime can + * shut down EDI by calling edi_shutdown(), which in turn calls the driver's driver_finish() routine. On shutdown all objects, of + * classes belonging to both the runtime and driver, are destroyed. */ + +#include "edi_objects.h" +#include "edi_dma_streams.h" +#include "edi_pthreads.h" +#include "edi_port_io.h" +#include "edi_memory_mapping.h" +#include "edi_devices.h" +#include "edi_interrupts.h" + +/*! \brief A pointer to a function the runtime can call if it fails to construct one of the driver's classes to find out what the + * runtime's quota is for that class. + * + * A pointer to a function which takes an edi_string_t as a parameter and returns in int32_t. This function follows the same + * semantics as the quota() function, returning the number of objects of the given class that can be constructed, -1 for infinity or + * -2 for an erroneous class name. It is used to tell the runtime the location of such a function in the driver so that the runtime + * can check quotas on driver-owned classes. */ +typedef int32_t (*k_quota_t)(edi_string_t resource_class); +/*!\struct edi_initialization_t + * \brief Structure containing driver classes available to the runtime and the driver's quota function after the driver has initialized. + * + * Structure containing driver classes available to runtime, the driver's quota function and the driver's name provided to the runtime + * after the driver has initialized. driver_bus, vendor_id, and device_id are all optional fields which coders should consider + * supplementary information. Kernels can require these fields if they so please, but doing so for devices which don't run on a Vendor + * ID/Product ID supporting bus is rather unwise. */ +typedef struct { + /*!\brief The number of driver classes in the driver_classes array. */ + int32_t num_driver_classes; + /*!\brief An array of declarations of driver classes available to the runtime. + * + * This array should not necessarily contain the entire list of EDI classes implemented by the driver. Instead, it should + * contain a list of those classes which the driver has correctly initialized itself to provide instances of with full + * functionality. */ + edi_class_declaration_t *driver_classes; + /*!\brief The driver's quota function. */ + k_quota_t k_quota; + /*!\brief The driver's name. */ + edi_string_t driver_name; + /*!\brief The bus of the device this driver wants to drive, if applicable. + * + * The driver does not have to supply this field, and can also supply "MULTIPLE BUSES" here to indicate that it drives devices + * on multiple buses. */ + edi_string_t driver_bus; + /*!\brief The driver's vendor ID, if applicable. + * + * The driver does not need to supply this field, and should supply -1 to indicate that it does not wish to. */ + int16_t vendor_id; + /*!\brief The driver's device ID, if applicable. + * + * The driver does not need to supply this field, but can supply it along with vendor_id. If either vendor_id or this field are + * set to -1 the runtime should consider this field not supplied. */ + int16_t driver_id; +} edi_initialization_t; +/*!\brief A pointer to a driver's initialization function. + * + * The protocol for the driver's initialization function. The runtime gives the driver a set of EDI objects representing the + * resources it thinks the driver should run with. This function returns an edi_initialization_t structure containing declarations + * of the EDI classes the driver can make available to the runtime after initialization. If any member of that structure contains 0 + * or NULL, it is considered invalid and the runtime should destroy the driver without calling its driver_finish() routine. */ +typedef edi_initialization_t (*driver_init_t)(int32_t num_resources,edi_object_metadata_t *resources); +/*!\brief Requests more resources from the runtime. Can be called during driver initialization. + * + * Called to negotiate with the runtime for the right to create further EDI objects/obtain further resources owned by the runtime. + * When the driver calls this routine, the runtime decides whether to grant more resources. If yes, this call returns true, and the + * driver can proceed to try and create the objects it desires, in addition to destroying EDI objects it doesn't want. Otherwise, + * it returns false. + * The driver must deal with whatever value this routine returns. */ +bool edi_negotiate_resources(); + +/*! \brief Returns the driver's quota of objects for a given runtime-owned class. + * + * This function takes an edi_string_t with the name of a runtime-owned class in it and returns the number of objects of that class + * which drivers can construct, -1 for infinity, or -2 for an erroneous class name. */ +int32_t quota(edi_string_t resource_class); +/*! \brief Sends a string to the operating systems debug output or logging facilities. */ +void edi_debug_write(uint32_t debug_string_length, char *debug_string); +/*! \brief This call destroys all objects and shuts down the entire EDI environment of the driver. + * + * This function shuts down EDI as described in INITIALIZATION AND SHUTDOWN above. All objects are destroyed, EDI functions can no + * longer be successfully called, etc. This function only succeeds when EDI has already been initialized, so it returns -1 when EDI + * hasn't been, 1 on success, or 0 for all other errors. */ +int32_t shutdown_edi(void); + +/*!\brief A pointer to the driver's finishing/shutdown function. + * + * The protocol for the driver's shutting down. This function should do anything the driver wants done before it dies. */ +typedef void (*driver_finish_t)(); + +#endif diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_devices.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_devices.h new file mode 100644 index 00000000..245e01f3 --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/edi/edi_devices.h @@ -0,0 +1,135 @@ +#ifndef EDI_DEVICES_H + +/* Copyright (c) 2006 Eli Gottlieb. + * Permission is granted to copy, distribute and/or modify this document + * under the terms of the GNU Free Documentation License, Version 1.2 + * or any later version published by the Free Software Foundation; + * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + * Texts. A copy of the license is included in the file entitled "COPYING". */ + +/* Edited by thePowersGang (John Hodge) June 2009 + * - Add #ifdef EDI_MAIN_FILE + */ + +#define EDI_DEVICES_H + +/*! \file edi_devices.h + * \brief Declaration and description of simple classes for implementation by EDI drivers to represent hardware devices. + * + * Data structures and algorithms this header represents: + * + * DATA STRUCTURE AND ALGORITHM: BASIC DEVICES - There are two functions, select() for waiting on devices and ioctl() for + * controlling them, common to many POSIX devices. Implementations of EDI-CHARACTER-DEVICE or EDI-BLOCK-DEVICE may implement either of + * these or both, and users of such objects much query for the methods to see if they're supported. Obviously, runtime or driver + * developers don't *need* to support these. + * + * DATA STRUCTURE AND ALGORITHM: CHARACTER DEVICES - The class EDI-CHARACTER-DEVICE provides a very basic interface to character + * devices, which read and write streams of characters. As such, this class only provides read() and write(). The calls attempt a + * likeness to POSIX. + * + * DATA STRUCTURE AND ALGORITHM: BLOCK DEVICES - The class EDI-BLOCK-DEVICE provides a very basic interface to block devices, which + * can read(), write() and seek() to blocks of a specific size in an array of blocks with a specific size. Its declarations and + * semantics should behave like those of most POSIX operating systems. + * + * Note that EDI runtimes should not implement these classes. Their declarations are provided for drivers to implement. */ + +#include "edi_objects.h" + +/* Methods common to all EDI device classes specified in this header. */ + +/*!\brief EAGAIN returned by functions for block and character devices. + * + * Means that the amount of data the device has ready is less than count. */ +#define EAGAIN -1 +/*!\brief EBADOBJ returned by functions for block and character devices. + * + * Means that the object passed as the method's this point was not a valid object of the needed class. */ +#define EBADOBJ -2 +/*!\brief EINVAL returned by functions for block and character devices. + * + * Means that the method got passed invalid parameters. */ +#ifdef EINVAL +# undef EINVAL +#endif +#define EINVAL -3 + +/*!\brief select() type to wait until device is writable. */ +#define EDI_SELECT_WRITABLE 0 +/*!\brief select() type to wait until device is readable. */ +#define EDI_SELECT_READABLE 1 + +/*!\brief Argument to seek(). Sets the block offset (ie: the "current block" index) to the given whence value. */ +#define EDI_SEEK_SET 0 +/*!\brief Argument to seek(). Sets the block offset (ie: the "current block" index) to its current value + whence. */ +#define EDI_SEEK_CURRENT 1 + +#ifdef EDI_MAIN_FILE +/*!\brief Arguments to EDI's basic select() function. */ +edi_variable_declaration_t select_arguments[2] = {{"pointer void","device",1}, + {"unsigned int32_t","select_type",1}}; +/*!\brief Declaration of EDI's basic select() function. + * + * Contrary to the POSIX version, this select() puts its error codes in its return value. */ +edi_function_declaration_t select_declaration = {"int32_t","edi_device_select",0,2,select_arguments,NULL}; +#else +extern edi_function_declaration_t select_declaration; // Declare for non main files +#endif + +#ifdef EDI_MAIN_FILE +/*!\brief Arguments to EDI's basic ioctl() function. */ +edi_variable_declaration_t ioctl_arguments[3] = {{"pointer void","device",1},{"int32_t","request",1},{"pointer void","argp",1}}; +/*!\brief Declaration of EDI's basic ioctl() function. + * + * Contrary to the POSIX version, this ioctl() puts its error codes in its return value. */ +edi_function_declaration_t ioctl_declaration = {"int32_t","edi_device_ioctl",0,3,ioctl_arguments,NULL}; +#else +extern edi_class_declaration_t ioctl_declaration; // Declare for non main files +#endif + +#ifdef EDI_MAIN_FILE +/*!\brief Declaration of the arguments EDI-CHARACTER-DEVICE's read() and write() methods. */ +edi_variable_declaration_t chardev_read_write_arguments[3] = {{"pointer void","chardev",1}, + {"pointer void","buffer",1}, + {"unsigned int32_t","char_count",1}}; +/*!\brief Declarations of the methods of EDI-CHARACTER-DEVICE, read() and write(). + * + * The code pointers of these function declarations are all given as NULL. Driver developers implementing EDI-CHARACTER-DEVICE should + * fill in these entries with pointers to their own functions. */ +EDI_DEFVAR edi_function_declaration_t chardev_methods[2]= {{"int32_t","edi_chardev_read",0,3,chardev_read_write_arguments,NULL}, + {"int32_t","edi_chardev_write",0,3,chardev_read_write_arguments,NULL}}; +/*!\brief Declaration of the EDI-CHARACTER-DEVICE class. + * + * Driver developers implementing this class should fill in their own values for constructor, destructor, and possibly even parent + * before passing the filled-in structure to the EDI runtime. */ +EDI_DEFVAR edi_class_declaration_t chardev_class = {"EDI-CHARACTER-DEVICE",0,2,chardev_methods,NULL,NULL,NULL}; +#else +extern edi_class_declaration_t chardev_class; // Declare for non main files +#endif + +#ifdef EDI_MAIN_FILE +/*!\brief Arguments to EDI-BLOCK-DEVICE's read() and write() methods. */ +edi_variable_declaration_t blockdev_read_write_arguments[3] = {{"pointer void","blockdev",1}, + {"pointer void","buffer",1}, + {"unsigned int32_t","blocks",1}}; +/*!\brief Arguments to EDI-BLOCK-DEVICE's seek() method. */ +edi_variable_declaration_t blockdev_seek_arguments[3] = {{"pointer void","blockdev",1}, + {"int32_t","offset",1}, + {"int32_t","whence",1}}; +/*!\brief Declaration of the methods of EDI-BLOCK-DEVICE, read(), write(), seek(), and get_block_size(). + * + * The code pointers of these function declarations are all given as NULL. Driver developers implementing EDI-BLOCK-DEVICE should fill + * these entries in with pointers to their own functions. */ +edi_function_declaration_t blockdev_methods[4] = {{"int32_t","edi_blockdev_read",0,3,blockdev_read_write_arguments,NULL}, + {"int32_t","edi_blockdev_write",0,3,blockdev_read_write_arguments,NULL}, + {"int32_t","edi_blockdev_seek",0,3,blockdev_seek_arguments,NULL}, + {"unsigned int32_t","edi_blockdev_get_block_size",0,0,NULL,NULL}}; +/*!\brief Declaration of the EDI-BLOCK-DEVICE class. + * + * Driver developers implementing this class should fill in their own values for constructor, destructor, and possibly even parent + * before passing the filled-in structure to the EDI runtime. */ +edi_class_declaration_t blockdev_class = {"EDI-BLOCK-DEVICE",0,4,blockdev_methods,NULL,NULL,NULL}; +#else +extern edi_class_declaration_t blockdev_class; // Declare for non main files +#endif + +#endif diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_dma_streams.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_dma_streams.h new file mode 100644 index 00000000..8ab80ccb --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/edi/edi_dma_streams.h @@ -0,0 +1,52 @@ +#ifndef EDI_DMA_STREAMS_H + +/* Copyright (c) 2006 Eli Gottlieb. + * Permission is granted to copy, distribute and/or modify this document + * under the terms of the GNU Free Documentation License, Version 1.2 + * or any later version published by the Free Software Foundation; + * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + * Texts. A copy of the license is included in the file entitled "COPYING". */ + +#define EDI_DMA_STREAMS_H + +/*! \file edi_dma_streams.h + * \brief EDI's stream subclass for handling Direct Memory Access hardware. + * + * Data structures and algorithms this header represents: + * + * DATA STRUCTURE: DMA STREAMS - DMA streams are objects of the class EDI-STREAM-DMA used to pass data between a buffer of + * memory and the computer's DMA hardware. It is the responsibility of the object to allocate memory for its stream memory buffer + * which can be used with DMA hardware and to program the DMA hardware for transmissions. DMA streams can be bidirectional if the + * correct DMA mode is used. */ + +#include "edi_objects.h" + +#define DMA_STREAM_CLASS "EDI-STREAM-DMA" + +/*! \brief The name of the EDI DMA stream class. + * + * An edi_string_t with the class name "EDI-STREAM-DMA" in it. */ +#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI) +const edi_string_t dma_stream_class = DMA_STREAM_CLASS; +#else +extern const edi_string_t dma_stream_class; +#endif + +#ifndef IMPLEMENTING_EDI +/*! \brief int32_t EDI-STREAM-DMA.init_dma_stream(unsigned int32_t channel,unsigned int32_t mode,unsigned int32_t buffer_pages); + * + * Pointer to the init_dma_stream() method of class EDI-STREAM-DMA, which initializes a DMA stream with a DMA channel, DMA mode, and + * the number of DMA-accessible memory pages to keep as a buffer. It will only work once per stream object. It's possible return + * values are 1 for sucess, -1 for invalid DMA channel, -2 for invalid DMA mode, -3 for inability to allocate enough buffer pages and + * 0 for all other errors. */ +EDI_DEFVAR int32_t (*init_dma_stream)(object_pointer stream, uint32_t channel, uint32_t mode, uint32_t buffer_pages); +/*! \brief int32_t EDI-STREAM-DMA.transmit(data_pointer *anchor,unsigned int32 num_bytes,bool sending); + * + * Pointer to the dma_stream_transmit() method of class EDI-STREAM-DMA, which transmits the given number of bytes of data through + * the DMA stream to/from the given anchor (either source or destination), in the given direction. It returns 1 on success, -1 on + * an uninitialized or invalid DMA stream object, -2 when the anchor was NULL or otherwise invalid, -3 if the DMA stream can't + * transmit in the given direction, and 0 for all other errors. */ +EDI_DEFVAR int32_t (*dma_stream_transmit)(object_pointer stream, data_pointer anchor, uint32_t num_bytes, bool sending); +#endif + +#endif diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_interrupts.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_interrupts.h new file mode 100644 index 00000000..ef2ffc9f --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/edi/edi_interrupts.h @@ -0,0 +1,65 @@ +#ifndef EDI_INTERRUPTS_H + +/* Copyright (c) 2006 Eli Gottlieb. + * Permission is granted to copy, distribute and/or modify this document + * under the terms of the GNU Free Documentation License, Version 1.2 + * or any later version published by the Free Software Foundation; + * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + * Texts. A copy of the license is included in the file entitled "COPYING". */ + +#define EDI_INTERRUPTS_H + +/*! \file edi_interrupts.h + * \brief Declaration and description of EDI's interrupt handling class. + * + * Data structures and algorithms this header represents: + * DATA STRUCTURE AND ALGORITHM: INTERRUPT OBJECTS - The class EDI-INTERRUPT encapsulates the handling of machine interrupts. + * It is initialized with an interrupt number to handle and a handler routine to call when that interrupt occurs. Only a couple of + * guarantees are made to the driver regarding the runtime's implementation of interrupt handling: 1) That the driver's handler is + * called for every time the interrupt associated with a valid and initialized interrupt object occurs, in the order of the + * occurences, 2) That the runtime handle the architecture-specific (general to the entire machine, not just this device) + * end-of-interrupt code when the driver is called without first returning from the machine interrupt. Note that the runtime hands + * out interrupt numbers at its own discretion and policy. */ + +#include "edi_objects.h" + +/*! \brief Macro constant containing the name of the interrupt class + */ +#define INTERRUPTS_CLASS "EDI-INTERRUPT" +/*! \brief The name of EDI's interrupt-handling class. + * + * An edi_string_t holding the name of the runtime-implemented interrupt object class. It's value is "EDI-INTERRUPT". */ +#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI) +const edi_string_t interrupts_class = INTERRUPTS_CLASS; +#else +extern const edi_string_t interrupts_class; +#endif + +/*! \brief A pointer to an interrupt handling function. + * + * A pointer to a function called to handle interrupts. Its unsigned int32_t parameter is the interrupt number that is being + * handled. */ +typedef void (*interrupt_handler_t)(uint32_t interrupt_number); + +#ifndef IMPLEMENTING_EDI +/*! \brief Initializes an interrupt object with an interrupt number and a pointer to a handler function. + * + * A pointer to the init_interrupt() method of class EDI-INTERRUPT. This method initializes a newly-created interrupt object with an + * interrupt number and a pointer to the driver's handler of type interrupt_handler_t. It can only be called once per object, and + * returns 1 on success, fails with -1 when the interrupt number is invalid or unacceptable to the runtime, fails with -2 when the + * pointer to the driver's interrupt handler is invalid, and fails with -3 for all other errors. */ +EDI_DEFVAR int32_t (*init_interrupt)(object_pointer interrupt, uint32_t interrupt_number, interrupt_handler_t handler); +/*! \brief Get this interrupt object's interrupt number. */ +EDI_DEFVAR uint32_t (*interrupt_get_irq)(object_pointer interrupt); +/*! \brief Set a new handler for this interrupt object. */ +EDI_DEFVAR void (*interrupt_set_handler)(object_pointer interrupt, interrupt_handler_t handler); +/*! \brief Return from this interrupt, letting the runtime run any necessary End-Of-Interrupt code. + * + * A pointer to the interrupt_return() method of class EDI-INTERRUPT. This method returns from the interrupt designated by the + * calling interrupt object. If there is a machine-wide end-of-interrupt procedure and the driver was called during the handling of + * the machine interrupt (as opposed to delaying the handling and letting the runtime EOI), the runtime runs it during this method. + * This method has no return value, since once it's called control leaves the calling thread. */ +EDI_DEFVAR void (*interrupt_return)(object_pointer interrupt); +#endif + +#endif diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_memory_mapping.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_memory_mapping.h new file mode 100644 index 00000000..ba8dff35 --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/edi/edi_memory_mapping.h @@ -0,0 +1,86 @@ +#ifndef EDI_MEMORY_MAPPING_H + +/* Copyright (c) 2006 Eli Gottlieb. + * Permission is granted to copy, distribute and/or modify this document + * under the terms of the GNU Free Documentation License, Version 1.2 + * or any later version published by the Free Software Foundation; + * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + * Texts. A copy of the license is included in the file entitled "COPYING". */ + +#define EDI_MEMORY_MAPPING_H + +/*! \file edi_memory_mapping.h + * \brief Declaration and description of EDI's class for mapping physical pages into the driver's address space. + * + * Data structures and algorithms this header represents: + * ALGORITHM: MEMORY MAPPINGS - Memory mapping objects of the class EDI-MEMORY-MAPPING are used to give virtual (driver-visible) + * addresses to sections of physical memory. These can either be memory mappings belonging to hardware devices or plain RAM which + * the driver wants page-aligned. A memory mapping object is initialized with the physical address for the memory mapping and the + * number of pages the mapping takes up, or simply the desired length of the a physically contiguous buffer in pages. The class's + * two methods map the section of memory into and out of the driver's virtual address space. */ + +#include "edi_objects.h" + +/*! \brief The name of EDI's memory mapping class. + * + * An edi_string_t with the name of the memory mapping class, "EDI-MEMORY-MAPPING". */ +#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI) +const edi_string_t memory_mapping_class = "EDI-MEMORY-MAPPING"; +#else +extern const edi_string_t memory_mapping_class; +#endif + +/*! \brief Flag representing Strong Uncacheable caching method. */ +#define CACHING_STRONG_UNCACHEABLE 0 +/*! \brief Flag representing Uncacheable caching method. */ +#define CACHING_UNCACHEABLE 1 +/*! \brief Flag representing Write combining caching method. */ +#define CACHING_WRITE_COMBINING 2 +/*! \brief Flag representing Write Through caching method. */ +#define CACHING_WRITE_THROUGH 3 +/*! \brief Flag representing Write Back caching method. */ +#define CACHING_WRITE_BACK 3 +/*! \brief Flag representing Write Protected caching method. */ +#define CACHING_WRITE_PROTECTED 3 + +#ifndef IMPLEMENTING_EDI +/*! \brief Initialize an EDI-MEMORY-MAPPING object with a physical address range. + * + * This method takes the start_physical_address of a memory mapping and the number of pages in that mapping and uses these arguments + * to initialize an EDI-MEMORY-MAPPING object. It can only be called once per object. It returns 1 when successful, -1 when an + * invalid physical address is given (one that the runtime knows is neither a physical memory mapping belonging to a device nor + * normal RAM), -2 when the number of pages requested is bad (for the same reasons as the starting address can be bad), and 0 for + * all other errors. + * + * Note that this method can't be invoked on an object which has already initialized via init_memory_mapping_with_pages(). */ +EDI_DEFVAR int32_t (*init_memory_mapping_with_address)(object_pointer mapping, data_pointer start_physical_address, uint32_t pages); +/*! \brief Initialize an EDI-MEMORY-MAPPING object by requesting a number of new physical pages. + * + * This method takes a desired number of physical pages for a memory mapping, and uses that number to initialize an + * EDI-MEMORY-MAPPING object by creating a buffer of contiguous physical pages. It can only be called once per object. It returns + * 1 when successful, -1 when the request for pages cannot be fulfilled, and 0 for all other errors. + * + * Note that this method cannot be called if init_memory_mapping_with_address() has already been used on the given object. */ +EDI_DEFVAR int32_t (*init_memory_mapping_with_pages)(object_pointer mapping, uint32_t pages); +/*! \brief Map the memory-mapping into this driver's visible address space. + * + * This asks the runtime to map a given memory mapping into the driver's virtual address space. Its parameter is the address of a + * data_pointer to place the virtual address of the mapping into. This method returns 1 on success, -1 on an invalid argument, -2 + * for an uninitialized object, and 0 for all other errors. */ +EDI_DEFVAR int32_t (*map_in_mapping)(object_pointer mapping, data_pointer *address_mapped_to); +/*! \brief Unmap the memory mapping from this driver's visible address space. + * + * This method tries to map the given memory mapping out of the driver's virtual address space. It returns 1 for success, -1 + * for an uninitialized memory mapping object, -2 if the mapping isn't mapped into the driver's address space already, and 0 + * for all other errors. */ +EDI_DEFVAR int32_t (*map_out_mapping)(object_pointer mapping); + +/*! \brief Set the caching flags for a memory mapping. */ +EDI_DEFVAR void (*mapping_set_caching_method)(object_pointer mapping, uint32_t caching_method); +/*! \brief Get the current caching method for a memory mapping. */ +EDI_DEFVAR uint32_t (*mapping_get_caching_method)(object_pointer mapping); +/*! \brief Flush write-combining buffers on CPU to make sure changes to memory mapping actually get written. Only applies to a Write Combining caching method (I think.).*/ +EDI_DEFVAR void (*flush_write_combining_mapping)(object_pointer mapping); +#endif + +#endif diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_objects.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_objects.h new file mode 100644 index 00000000..0e479517 --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/edi/edi_objects.h @@ -0,0 +1,257 @@ +#ifndef EDI_OBJECTS_H + +/* Copyright (c) 2006 Eli Gottlieb. + * Permission is granted to copy, distribute and/or modify this document + * under the terms of the GNU Free Documentation License, Version 1.2 + * or any later version published by the Free Software Foundation; + * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + * Texts. A copy of the license is included in the file entitled "COPYING". */ + +#define EDI_OBJECTS_H + +/*! \file edi_objects.h + * \brief The header file for basic EDI types and the object system. + * + * This file contains declarations of EDI's primitive data types, as well as structures and functions for with the object system. + * It represents these data structures and algorithms: + * + * DATA STRUCTURE: THE CLASS LIST - EDI implementing runtime's must keep an internal list of classes implemented by the runtime + * and separate lists of classes implemented by each driver. Whoever implements a class is said to "own" that class. The + * internal format of this list is up to the runtime coders, but it must be possible to recreate the original list of + * edi_class_declaration structures the driver declared to the runtime from it. This list is declared to the runtime in an + * initialization function in the header edi.h. The object_class member of an edi_object_metadata structure must point to that + * object's class's entry in this list. + * + * ALGORITHM AND DATA STRUCTURE: CLASSES AND INHERITANCE - Classes are described using edi_class_declaration_t structures and + * follow very simple rules. All data is private and EDI provides no way to access instance data, so there are no member + * variable declarations. However, if the data isn't memory-protected (for example, driver data on the driver heap) EDI allows + * the possibility of pointer access to data, since runtime and driver coders could make use of that behavior. Classes may have + * one ancestor by declaring so in their class declaration structure, and if child methods are different then parent methods + * the children always override their parents. An EDI runtime must also be able to check the existence and ownership of a given + * class given its name in an edi_string_t. + * + * ALGORITHM: OBJECT CREATION AND DESTRUCTION - An EDI runtime should be able to call the constructor of a named class, put the + * resulting object_pointer into an edi_object_metadata_t and return that structure. The runtime should also be able to call an + * object's class's destructor when given a pointer to a valid edi_metadata_t for an already-existing object. Data equivalent + * to an edi_object_metadata_t should also be tracked by the runtime for every object in existence in case of sudden EDI shutdown + * (see edi.h). + * + * ALGORITHM: RUNTIME TYPE INFORMATION - When passed the data_pointer member of an edi_object_metadata_t to a valid object, an + * EDI runtime must be able to return an edi_string_t containing the name of that object's class and to return function_pointers + * to methods when the required information to find the correct method is given by calling a class's method getting function.*/ + +/* If the EDI headers are linked with the standard C library, they use its type definitions. Otherwise, equivalent definitions are + * made.*/ +#if __STDC_VERSION__ == 199901L +# include +# include +#else +# ifndef NULL +# define NULL ((void*)0) +# endif +typedef unsigned char bool; +# define true 1 +# define false 0 +typedef char int8_t; +typedef short int16_t; +typedef long int32_t; +typedef long long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +typedef unsigned long long uint64_t; +#endif + +/*! \brief Define a variable in the header + */ +#ifdef EDI_MAIN_FILE +# define EDI_DEFVAR +#else +# define EDI_DEFVAR extern +#endif + +/*! \brief A pointer to the in-memory instance of an object. + * + * This type is sized just like a general C pointer type (whatever*) for the target architecture. It's passed as a first parameter + * to all methods, thus allowing EDI classes to be implemented as C++ classes and providing some protection from confusing objects + * with normal pointers. Equivalent to a C++ this pointer or an Object Pascal Self argument. */ +typedef void *object_pointer; +/*! \brief A basic pointer type pointing to arbitrary data in an arbitrary location. */ +typedef void *data_pointer; +/*! \brief A basic function pointer type. + * + * A pointer to a piece of code which can be called and return to its caller, used to distinguish between pointers to code and + * pointers to data. Its size is hardware-dependent. */ +typedef void (*function_pointer)(void); +/*! \brief The length of an EDI string without its null character. */ +#define EDI_STRING_LENGTH 31 +/*! \brief A type representing a 31-character long string with a terminating NULL character at the end. All of EDI uses this type + * for strings. + * + * A null-terminated string type which stores characters in int8s. It allows for 31 characters in each string, with the final + * character being the NULL terminator. Functions which use this type must check that its final character is NULL, a string which + * doesn't not have this property is invalid and insecure. I (the author of EDI) know and understand that this form of a string + * suffers from C programmer's disease, but I can't use anything else without either making string use far buggier or dragging + * everyone onto a better language than C. */ +typedef int8_t edi_string_t[0x20]; +/*! \brief A type representing a pointer form of #edi_string_t suitable for function returns + */ +typedef int8_t *edi_string_ptr_t; + +/*! \var EDI_BASE_TYPES + * \brief A constant array of edi_string_t's holding every available EDI primitive type. */ +/*! \var EDI_TYPE_MODIFIERS + * \brief A constant array of edi_string_t's holding available modifiers for EDI primitive types. */ +#ifdef IMPLEMENTING_EDI + const edi_string_t EDI_BASE_TYPES[9] = {"void","bool","int8_t","int16_t","int32_t","int64_t","function_pointer","intreg","edi_string_t"}; + const edi_string_t EDI_TYPE_MODIFIERS[2] = {"pointer","unsigned"}; +#else + //extern const edi_string_t EDI_BASE_TYPES[9] = {"void","bool","int8_t","int16_t","int32_t","int64_t","function_pointer","intreg", "edi_string_t"}; + //extern const edi_string_t EDI_TYPE_MODIFIERS[2] = {"pointer","unsigned"}; + extern const edi_string_t EDI_BASE_TYPES[9]; + extern const edi_string_t EDI_TYPE_MODIFIERS[2]; +#endif + +/*! \struct edi_object_metadata_t + * \brief A packed structure holding all data to identify an object to the EDI object system. */ +typedef struct { + /*! \brief Points to the instance data of the object represented by this structure. + * + * An object_pointer to the object this structure refers to. The this pointer, so to speak. */ + object_pointer object; + /*! \brief Points the internal record kept by the runtime describing the object's class. + * + * Points to wherever the runtime has stored the class data this object was built from. The class data doesn't need to be + * readable to the driver, and so this pointer can point to an arbitrary runtime-reachable location. */ + data_pointer object_class; +} edi_object_metadata_t; + +/*! \struct edi_variable_declaration_t + * \brief The data structure used to describe a variable declaration to the EDI object system. + * + * The data structure used to describe a variable declaration to the EDI object system. The context of the declaration depends on + * where the data structure appears, ie: alone, in a class declaration, in a parameter list, etc. */ +typedef struct { + /*! \brief The type of the declared variable. + * + * The type of the variable, which must be a valid EDI primitive type as specified in the constant EDI_BASE_TYPES and + * possibly modified by a modifier specified in the constant EDI_TYPE_MODIFIERS. */ + edi_string_t type; + /*! \brief The name of the declared variable. */ + edi_string_t name; + /*! \brief Number of array entries if this variable is an array declaration. + * + * An int32_t specifying the number of variables of 'type' in the array 'name'. For a single variable this value should + * simply be set to 1, for values greater than 1 a packed array of contiguous variables is being declared, and a value of 0 + * is invalid. */ + int32_t array_length; +} edi_variable_declaration_t; + +/*! \struct edi_function_declaration_t + * \brief The data structure used to declare a function to the EDI object system. */ +typedef struct { + /*! \brief The return type of the function. The same type rules which govern variable definitions apply here. */ + edi_string_t return_type; + /*! \brief The name of the declared function. */ + edi_string_t name; + /*! \brief The version number of the function, used to tell different implementations of the same function apart. */ + uint32_t version; + /*! \brief The number of arguments passed to the function. + * + * The number of entries in the member arguments that the object system should care about. Caring about less misses + * parameters to functions, caring about more results in buffer overflows. */ + uint32_t num_arguments; + /*! \brief An array of the declared function's arguments. + * + * A pointer to an array num_arguments long containing edi_variable_declaration_t's for each argument to the declared + * function.*/ + edi_variable_declaration_t *arguments; + /*!\brief A pointer to the declared function's code in memory. */ + function_pointer code; +} edi_function_declaration_t; + +/*! \brief A pointer to a function for constructing instances of a class. + * + * A pointer to a function which takes no parameters and returns an object_pointer pointing to the newly made instance of a class. + * It is the constructor's responsibility to allocate memory for the new object. Each EDI class needs one of these. */ +typedef object_pointer (*edi_constructor_t)(void); +/*! \brief A pointer to a function for destroying instances of a class. + * + * A pointer to a function which takes an object_pointer as a parameter and returns void. This is the destructor counterpart to a + * class's edi_constructor_t, it destroys the object pointed to by its parameter and frees the object's memory. Every class must + * have one */ +typedef void (*edi_destructor_t)(object_pointer); + +/*! \brief Information the driver must give the runtime about its classes so EDI can construct them and call their methods. + * + * A structure used to declare a class to an EDI runtime so instances of it can be constructed by the EDI object system. */ +typedef struct { + /*! \brief The name of the class declared by the structure. */ + edi_string_t name; + /*! \brief The version of the class. This number is used to tell identically named but differently + * implemented classes apart.*/ + uint32_t version; + /*! \brief The number of methods in the 'methods' function declaration array. */ + uint32_t num_methods; + /*! \brief An array of edi_function_declaration_t declaring the methods of this class. */ + edi_function_declaration_t *methods; + /*! \brief Allocates the memory for a new object of the declared class and constructs the object. Absolutely required.*/ + edi_constructor_t constructor; + /*! \brief Destroys the given object of the declared class and frees its memory. Absolutely required. */ + edi_destructor_t destructor; + /*! \brief A pointer to another EDI class declaration structure specifying the declared class's parent class. + * + * Points to a parent class declared in another class declaration. It can be NULL to mean this class has no parent. */ + struct edi_class_declaration_t *parent; +} edi_class_declaration_t; + +/*! \brief Checks the existence of the named class. + * + * This checks for the existence on THE CLASS LIST of the class named by its edi_string_t parameter and returns a signed int32_t. If + * the class isn't found (ie: it doesn't exist as far as EDI is concerned) -1 is returned, if the class is owned by the driver + * (implemented by the driver and declared to the runtime by the driver) 0, and if the class is owned by the runtime (implemented by + * the runtime) 1. */ +int32_t check_class_existence(edi_string_t class_name); +/*! \brief Constructs an object of the named class and returns its object_pointer and a data_pointer to its class data. + * + * Given a valid class name in an edi_string_t this function constructs the specified class and returns an edi_metadata_t describing + * the new object as detailed in OBJECT CREATION AND DESTRUCTION. If the construction fails it returns a structure full of NULL + * pointers. */ +edi_object_metadata_t construct_object(edi_string_t class_name); +/*! \brief Destroys the given object using its class data. + * + * As specified in OBJECT CREATION AND DESTRUCTION this function should destroy an object when given its valid edi_metadata_t. The + * destruction is accomplished by calling the class's destructor. */ +void destroy_object(edi_object_metadata_t object); +/*! \brief Obtains a function pointer to a named method of a given class. + * + * When given a valid data_pointer object_class from an edi_object_metadata_t and an edi_string_t representing the name of the + * desired method retrieves a function_pointer to the method's machine code in memory. If the desired method isn't found, NULL is + * returned. */ +function_pointer get_method_by_name(data_pointer object_class,edi_string_t method_name); +/*! \brief Obtains a function pointer to a method given by a declaration of the given class if the class's method matches the + * declaration. + * + * Works just like get_method_by_name(), but by giving an edi_function_declaration_t for the desired method instead of just its name. + * Performs detailed checking against THE CLASS LIST to make sure that the method returned exactly matches the declaration passed + * in. */ +function_pointer get_method_by_declaration(data_pointer object_class,edi_function_declaration_t declaration); + +/* Runtime typing information. */ +/*! \brief Returns the name of the class specified by a pointer to class data. + * + * Given the data_pointer to an object's class data as stored in an edi_object_metadata_t retrieves the name of the object's class + * and returns it in an edi_string_t. */ +edi_string_ptr_t get_object_class(data_pointer object_class); +/*! \brief Returns the name of a class's parent class. + * + * When given an edi_string_t with a class name in it, returns another edi_string_t containing the name of the class's parent, or an + * empty string. */ +edi_string_ptr_t get_class_parent(edi_string_t some_class); +/*! \brief Returns the internal class data of a named class (if it exists) or NULL. + * + * When given an edi_string_t with a valid class name in it, returns a pointer to the runtime's internal class data for that class. + * Otherwise, it returns NULL. */ +data_pointer get_internal_class(edi_string_t some_class); + +#endif diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_port_io.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_port_io.h new file mode 100644 index 00000000..a2a1773d --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/edi/edi_port_io.h @@ -0,0 +1,90 @@ +#ifndef EDI_PORT_IO_H + +/* Copyright (c) 2006 Eli Gottlieb. + * Permission is granted to copy, distribute and/or modify this document + * under the terms of the GNU Free Documentation License, Version 1.2 + * or any later version published by the Free Software Foundation; + * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + * Texts. A copy of the license is included in the file entitled "COPYING". */ + +/* Modified by thePowersGang (John Hodge) + * - Surround variable definitions with an #ifdef IMPLEMENTING_EDI + */ + +#define EDI_PORT_IO_H + +/*! \file edi_port_io.h + * \brief Declaration and description of EDI's port I/O class. + * + * Data structures and algorithms this header represents: + * + * DATA STRUCTURE AND ALGORITHM: PORT I/O OBJECTS - A class named "EDI-IO-PORT" is defined as an encapsulation of the port I/O + * used on some machine architectures. Each object of this class represents a single I/O port which can be read from and written to + * in various sizes. Each port can be held by one object only at a time. */ + +#include "edi_objects.h" + +/*! \brief Macro to create methods for reading from ports. + * + * This macro creates four similar methods, differing in the size of the type they read from the I/O port held by the object. Their + * parameter is a pointer to the output type, which is filled with the value read from the I/O port. They return 1 for success, -1 + * for an uninitialized I/O port object, and 0 for other errors. */ +#define port_read_method(type,name) int32_t (*name)(object_pointer port_object, type *out) +/*! \brief Macro to create methods for writing to ports. + * + * This macro creates four more similar methods, differing in the size of the type they write to the I/O port held by the object. + * Their parameter is the value to write to the port. They return 1 for success, -1 for an uninitialized I/O port object and 0 for + * other errors. */ +#define port_write_method(type,name) int32_t (*name)(object_pointer port_object, type in) + +/*! \brief Name of EDI I/O port class. (Constant) + * + * A CPP constant with the value of #io_port_class */ +#define IO_PORT_CLASS "EDI-IO-PORT" +/*! \brief Name of EDI I/O port class. + * + * An edi_string_t containing the class name "EDI-IO-PORT". */ +#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI) +const edi_string_t io_port_class = IO_PORT_CLASS; +#else +extern const edi_string_t io_port_class; +#endif + +#ifndef IMPLEMENTING_EDI +/*! \brief int32_t EDI-IO-PORT.init_io_port(unsigned int16_t port); + * + * This method takes an unsigned int16_t representing a particular I/O port and initializes the invoked EDI-IO-PORT object with it. + * The method returns 1 if successful, -1 if the I/O port could not be obtained for the object, and 0 for all other errors. */ +EDI_DEFVAR int32_t (*init_io_port)(object_pointer port_object, uint16_t port); +/*! \brief Get the port number from a port object. */ +EDI_DEFVAR uint16_t (*get_port_number)(object_pointer port); +/*! \brief Method created by port_read_method() in order to read bytes (int8s) from I/O ports. */ +EDI_DEFVAR int32_t (*read_byte_io_port)(object_pointer port_object, int8_t *out); +/*! \brief Method created by port_read_method() in order to read words (int16s) from I/O ports. */ +EDI_DEFVAR int32_t (*read_word_io_port)(object_pointer port_object, int16_t *out); +/*! \brief Method created by port_read_method() in order to read longwords (int32s) from I/O ports. */ +EDI_DEFVAR int32_t (*read_long_io_port)(object_pointer port_object, int32_t *out); +/*! \brief Method created by port_read_method() in order to read long longwords (int64s) from I/O ports. */ +EDI_DEFVAR int32_t (*read_longlong_io_port)(object_pointer port_object,int64_t *out); +/*! \brief Method of EDI-IO-PORT to read long strings of data from I/O ports. + * + * Reads arbitrarily long strings of data from the given I/O port. Returns 1 for success, -1 for an uninitialized port object, -2 + * for a bad pointer to the destination buffer, and 0 for all other errors. */ +EDI_DEFVAR int32_t (*read_string_io_port)(object_pointer port_object, uint32_t data_length, uint8_t *out); +/*! \brief Method created by port_write_method() in order to write bytes (int8s) to I/O ports. */ +EDI_DEFVAR int32_t (*write_byte_io_port)(object_pointer port_object, int8_t in); +/*! \brief Method created by port_write_method() in order to write words (int16s) to I/O ports. */ +EDI_DEFVAR int32_t (*write_word_io_port)(object_pointer port_object, int16_t in); +/*! \brief Method created by port_write_method() in order to write longwords (int32s) to I/O ports. */ +EDI_DEFVAR int32_t (*write_long_io_port)(object_pointer port_object, int32_t in); +/*! \brief Method created by port_write_method() in order to write long longwords (int64s) to I/O ports. */ +EDI_DEFVAR int32_t (*write_longlong_io_port)(object_pointer port_object, int64_t in); +/*! \brief Method of EDI-IO-PORT to write long strings of data to I/O ports. + * + * Writes arbitrarily long strings of data to the given I/O port. Returns 1 for success, -1 for an uninitialized port object, -2 + * for a bad pointer to the source buffer, and 0 for all other errors. */ +EDI_DEFVAR int32_t (*write_string_io_port)(object_pointer port_object, uint32_t data_length, uint8_t *in); + +#endif // defined(IMPLEMENTING_EDI) + +#endif diff --git a/KernelLand/Modules/Interfaces/EDI/edi/edi_pthreads.h b/KernelLand/Modules/Interfaces/EDI/edi/edi_pthreads.h new file mode 100644 index 00000000..c21fa751 --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/edi/edi_pthreads.h @@ -0,0 +1,175 @@ +#ifndef EDI_PTHREADS + +/* Copyright (c) 2006 Eli Gottlieb. + * Permission is granted to copy, distribute and/or modify this document + * under the terms of the GNU Free Documentation License, Version 1.2 + * or any later version published by the Free Software Foundation; + * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + * Texts. A copy of the license is included in the file entitled "COPYING". */ + +#define EDI_PTHREADS +/*!\file edi_pthreads.h + * \brief A basic subset of POSIX Threads functionality, providing threading and thread synchronization. + * + * A very basic POSIX Threads interface. Note that pthreads are not a class, because none of these calls really gels with + * object-oriented programming. Also, if drivers aren't processes or threads under the implementing operating system a small + * threading system must be implemented in-runtime just to multiplex the pthreads of EDI drivers. Sorry about that. + * + * Data structures and algorithms this header represents: + * + * ALGORITHM AND DATA STRUCTURE: POSIX Threading - The runtime must provide enough of a POSIX threading interface to implement + * the calls described here. The actual multithreading must be performed by the runtime, and the runtime can implement that + * multithreading however it likes as long as the given POSIX Threads subset works. There is, however, a caveat: since the runtime + * calls the driver like it would a library, the driver must perceive all calls made to it by the runtime as running under one thread. + * From this thread the driver can create others. Such behavior is a quirk of EDI, and does not come from the POSIX standard. + * However, it is necessary to provide the driver with a thread for its own main codepaths. For further details on a given POSIX + * Threading routine, consult its Unix manual page. */ + +#include "edi_objects.h" + +/* Placeholder type definitions. Users of the PThreads interface only ever need to define pointers to these types. */ +/*!\brief Opaque POSIX Threading thread attribute type. */ +typedef void pthread_attr_t; +/*!\brief Opaque POSIX Threading mutex (mutual exclusion semaphore) type. */ +typedef void pthread_mutex_t; +/*!\brief Opaque POSIX Threading mutex attribute type. */ +typedef void pthread_mutex_attr_t; + +/*!\struct sched_param + * \brief POSIX Threading scheduler parameters for a thread. */ +typedef struct { + /*!\brief The priority of the thread. */ + int32_t sched_priority; +} sched_param; + +/*!\brief POSIX Threading thread identifier. */ +typedef uint32_t pthread_t; +/*!\brief POSIX Threading thread function type. + * + * A function pointer to a thread function, with the required signature of a thread function. A thread function takes one untyped + * pointer as an argument and returns an untyped pointer. Such a function is a thread's main routine: it's started with the thread, + * and the thread exits if it returns. */ +typedef void *(*pthread_function_t)(void*); + +/*!\brief Insufficient resources. */ +#define EAGAIN -1 +/*!\brief Invalid parameter. */ +#define EINVAL -2 +/*!\brief Permission denied. */ +#define EPERM -3 +/*!\brief Operation not supported. */ +#define ENOTSUP -4 +/*!\brief Priority scheduling for POSIX/multiple schedulers is not implemented. */ +#define ENOSYS -5 +/*!\brief Out of memory. */ +#define ENOMEM -6 +/*!\brief Deadlock. Crap. */ +#define EDEADLK -7 +/*!\brief Busy. Mutex already locked. */ +#define EBUSY -8 + +/*!\brief Scheduling policy for regular, non-realtime scheduling. The default. */ +#define SCHED_OTHER 0 +/*!\brief Real-time, first-in first-out scheduling policy. Requires special (superuser, where such a thing exists) permissions. */ +#define SCHED_FIFO 1 +/*!\brief Real-time, round-robin scheduling policy. Requires special (superuser, where such a thing exists) permissions. */ +#define SCHED_RR 0 + +/*!\brief Creates a new thread with the given attributes, thread function and arguments, giving back the thread ID of the new + * thread. + * + * pthread_create() creates a new thread of control that executes concurrently with the calling thread. The new thread applies the + * function start_routine, passing it arg as its argument. The attr argument specifies thread attributes to apply to the new thread; + * it can also be NULL for the default thread attributes (joinable with default scheduling policy). On success this function returns + * 0 and places the identifier of the new thread into thread_id. On an error, pthread_create() can return EAGAIN if insufficient + * runtime resources are available to create the requested thread, EINVAL a value specified by attributes is invalid, or EPERM if the + * caller doesn't have permissions to set the given attributes. + * + * For further information: man 3 pthread_create */ +int32_t pthread_create(pthread_t *thread_id, const pthread_attr_t *attributes, pthread_function_t thread_function, void *arguments); +/*!\brief Terminates the execution of the calling thread. The thread's exit code with by status, and this routine never returns. */ +void pthread_exit(void *status); +/*!\brief Returns the thread identifier of the calling thread. */ +pthread_t pthread_self(); +/*!\brief Compares two thread identifiers. + * + * Determines of the given two thread identifiers refer to the same thread. If so, returns non-zero. Otherwise, 0 is returned. */ +int32_t pthread_equal(pthread_t thread1, pthread_t thread2); +/*!\brief Used by the calling thread to relinquish use of the processor. The thread then waits in the run queue to be scheduled + * again. */ +void pthread_yield(); + +/*!\brief Gets the scheduling policy of the given attributes. + * + * Places the scheduling policy for attributes into policy. Returns 0 on success, EINVAL if attributes was invalid, and ENOSYS if + * priority scheduling/multiple scheduler support is not implemented. */ +int32_t pthread_attr_getschedpolicy(const pthread_attr_t *attributes, int32_t *policy); +/*!\brief Sets the scheduling policy of the given attributes. + * + * Requests a switch of scheduling policy to policy for the given attributes. Can return 0 for success, EINVAL if the given policy + * is not one of SCHED_OTHER, SCHED_FIFO or SCHED_RR or ENOTSUP if policy is either SCHED_FIFO or SCHED_RR and the driver is not + * running with correct privileges. */ +int32_t pthread_attr_setschedpolicy(pthread_attr_t *attributes, int32_t policy); + +/*!\brief Gets the scheduling paramaters (priority) from the given attributes. + * + * On success, stores scheduling parameters in param from attributes, and returns 0. Otherwise, returns non-zero error code, such + * as EINVAL if the attributes object is invalid. */ +int32_t pthread_attr_getschedparam(const pthread_attr_t *attributes, sched_param *param); +/*!\brief Sets the scheduling parameters (priority) of the given attributes. + * + * Requests that the runtime set the scheduling parameters (priority) of attributes from param. Returns 0 for success, EINVAL for an + * invalid attributes object, ENOSYS when multiple schedulers/priority scheduling is not implemented, and ENOTSUP when the value of + * param isn't supported/allowed. */ +int32_t pthread_attr_setschedparam(pthread_attr_t *attributes, const sched_param *param); + +/*!\brief The thread obtains its scheduling properties explicitly from its attributes structure. */ +#define PTHREAD_EXPLICIT_SCHED 1 +/*!\brief The thread inherits its scheduling properties from its parent thread. */ +#define PTHREAD_INHERIT_SCHED 0 + +/*!\brief Returns the inheritsched attribute of the given attributes. + * + * On success, returns 0 and places the inheritsched attribute from attributes into inherit. This attribute specifies where the + * thread's scheduling properites shall come from, and can be set to PTHREAD_EXPLICIT_SCHED or PTHREAD_INHERIT_SCHED. On failure it + * returns EINVAL if attributes was invalid or ENOSYS if multiple schedulers/priority scheduling isn't implemented. */ +int32_t pthread_attr_getinheritsched(const pthread_attr_t *attributes, int32_t *inherit); +/*!\brief Sets the inheritsched attribute of the given attributes. + * + * On success, places inherit into the inheritsched attribute of attributes and returns 0. inherit must either contain + * PTHREAD_EXPLICIT_SCHED or PTHREAD_INHERIT_SCHED. On failure, this routine returns EINVAL if attributes is invalid, ENOSYS when + * multiple schedulers/priority scheduling isn't implemented, and ENOSUP if the inheritsched attribute isn't supported. */ +int32_t pthread_attr_setinheritsched(pthread_attr_t *attributes, int32_t inherit); + +/*!\brief Creates a new POSIX Threads mutex, which will initially be unlocked. + * + * Creates a new mutex with the given attributes. If attributes is NULL, the default attributes will be used. The mutex starts out + * unlocked. On success, the new mutex resides in the mutex structure pointed to by mutex, and this routine routines 0. On failure, + * it returns EAGAIN if the system lacked sufficient non-memory resources to initialize the mutex, EBUSY if the given mutex is + * already initialized and in use, EINVAL if either parameter is invalid, and ENOMEM if the system lacks the memory for a new + * mutex. Note: All EDI mutexes are created with the default attributes, and are of type PTHREAD_MUTEX_ERRORCHECK. This means + * undefined behavior can never result from an badly placed function call. */ +int32_t pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutex_attr_t *attributes); +/*!\brief Locks the given mutex. + * + * Locks the given mutex. If the mutex is already locked, blocks the calling thread until it can acquire the lock. When this + * routine returns successfully, it will return 0 and the calling thread will own the lock of the mutex. If the call fails, it can + * return EINVAL when mutex is invalid or EDEADLK if the calling thread already owns the mutex. */ +int32_t pthread_mutex_lock(pthread_mutex_t *mutex); +/*!\brief Unlocks the given mutex. + * + * Unlocks the given mutex, returning 0 on success. On failure, it can return EINVAL when mutex is invalid or EPERM when the + * calling thread doesn't own the mutex. */ +int32_t pthread_mutex_unlock(pthread_mutex_t *mutex); +/*!\brief Tries to lock the given mutex, returning immediately even if the mutex is already locked. + * + * Attempts to lock the given mutex, but returns immediately if it can't acquire a lock. Returns 0 when it has acquired a lock, + * EBUSY if the mutex is already locked, or EINVAL if mutex is invalid. */ +int32_t pthread_mutex_trylock(pthread_mutex_t *mutex); +/*!\brief Destroys the given mutex, or at least the internal structure of it. + * + * Deletes the given mutex, making mutex invalid until it should be initialized by pthread_mutex_init(). Returns 0 on success, + * EINVAL when mutex is invalid, or EBUSY when mutex is locked or referenced by another thread. */ +int32_t pthread_mutex_destroy (pthread_mutex_t *mutex); + +#endif diff --git a/KernelLand/Modules/Interfaces/EDI/edi/helpers.h b/KernelLand/Modules/Interfaces/EDI/edi/helpers.h new file mode 100644 index 00000000..d7bc1388 --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/edi/helpers.h @@ -0,0 +1,20 @@ +#ifndef HELPERS_H + +#define HELPERS_H + +#include + +// Locally Defined +bool edi_string_equal(edi_string_t x,edi_string_t y); +bool descends_from(data_pointer object_class,edi_string_t desired_class); +data_pointer get_actual_class(edi_string_t ancestor,int32_t num_objects,edi_object_metadata_t *objects); + +// Local Copy/set +void *memcpyd(void *dest, void *src, unsigned int count); + +// Implementation Defined Common functions +void *memcpy(void *dest, void *src, unsigned int count); +void *memmove(void *dest, void *src, unsigned int count); +void *realloc(void *ptr, unsigned int size); + +#endif diff --git a/KernelLand/Modules/Interfaces/EDI/edi_int.inc.c b/KernelLand/Modules/Interfaces/EDI/edi_int.inc.c new file mode 100644 index 00000000..0ab3359e --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/edi_int.inc.c @@ -0,0 +1,195 @@ +/* + * AcessOS EDI Interface + * - IRQ Class + * + * By John Hodge (thePowersGang) + * + * This file has been released into the public domain. + * You are free to use it as you wish. + */ +#include "edi/edi.h" + +// === TYPES === +typedef struct { + uint16_t State; // 0: Unallocated, 1: Allocated, 2: Initialised, (Bit 0x8000 set if in heap) + uint16_t Num; + interrupt_handler_t Handler; +} tEdiIRQ; + +// === PROTOTYPES === +void EDI_Int_IRQ_Handler(tRegs *Regs); + +// === GLOBALS === +tEdiIRQ gEdi_IRQObjects[16]; + +// === FUNCTIONS === +/** + * \fn object_pointer Edi_Int_IRQ_Construct(void) + * \brief Creates a new IRQ Object + * \return Pointer to object + */ +object_pointer Edi_Int_IRQ_Construct(void) +{ + int i; + // Search for a free irq + for( i = 0; i < 16; i ++ ) + { + if(gEdi_IRQObjects[i].State) continue; + gEdi_IRQObjects[i].State = 1; + gEdi_IRQObjects[i].Num = 0; + gEdi_IRQObjects[i].Handler = NULL; + return &gEdi_IRQObjects[i]; + } + return NULL; +} + +/** + * \fn void Edi_Int_IRQ_Destruct(object_pointer Object) + * \brief Destruct an IRQ Object + * \param Object Object to destroy + */ +void Edi_Int_IRQ_Destruct(object_pointer Object) +{ + tEdiIRQ *obj; + + VALIDATE_PTR(Object,); + obj = GET_DATA(Object); + + if( !obj->State ) return; + + if( obj->Handler ) + irq_uninstall_handler( obj->Num ); + + if( obj->State & 0x8000 ) { // If in heap, free + free(Object); + } else { // Otherwise, mark as unallocated + obj->State = 0; + } +} + +/** + * \fn int32_t Edi_Int_IRQ_InitInt(object_pointer Object, uint16_t Num, interrupt_handler_t Handler) + * \brief Initialises an IRQ + * \param Object Object Pointer (this) + * \param Num IRQ Number to use + * \param Handler Callback for IRQ + */ +int32_t Edi_Int_IRQ_InitInt(object_pointer Object, uint16_t Num, interrupt_handler_t Handler) +{ + tEdiIRQ *obj; + + //LogF("Edi_Int_IRQ_InitInt: (Object=0x%x, Num=%i, Handler=0x%x)\n", Object, Num, Handler); + + VALIDATE_PTR(Object,0); + obj = GET_DATA(Object); + + if( !obj->State ) return 0; + + if(Num > 15) return 0; + + // Install the IRQ if a handler is passed + if(Handler) { + if( !irq_install_handler(Num, Edi_Int_IRQ_Handler) ) + return 0; + obj->Handler = Handler; + } + + obj->Num = Num; + obj->State &= ~0x3FFF; + obj->State |= 2; // Set initialised flag + return 1; +} + +/** + * \fn uint16_t Edi_Int_IRQ_GetInt(object_pointer Object) + * \brief Returns the irq number associated with the object + * \param Object IRQ Object to get number from + * \return IRQ Number + */ +uint16_t Edi_Int_IRQ_GetInt(object_pointer Object) +{ + tEdiIRQ *obj; + + VALIDATE_PTR(Object,0); + obj = GET_DATA(Object); + + if( !obj->State ) return 0; + return obj->Num; +} + +/** + * \fn void EDI_Int_IRQ_SetHandler(object_pointer Object, interrupt_handler_t Handler) + * \brief Set the IRQ handler for an IRQ object + * \param Object IRQ Object to alter + * \param Handler Function to use as handler + */ +void EDI_Int_IRQ_SetHandler(object_pointer Object, interrupt_handler_t Handler) +{ + tEdiIRQ *obj; + + // Get Data Pointer + VALIDATE_PTR(Object,); + obj = GET_DATA(Object); + + // Sanity Check arguments + if( !obj->State ) return ; + + // Only register the mediator if it is not already + if( Handler && !obj->Handler ) + if( !irq_install_handler(obj->Num, Edi_Int_IRQ_Handler) ) + return ; + obj->Handler = Handler; +} + +/** + * \fn void EDI_Int_IRQ_Return(object_pointer Object) + * \brief Return from interrupt + * \param Object IRQ Object + * \note Due to the structure of acess interrupts, this is a dummy + */ +void EDI_Int_IRQ_Return(object_pointer Object) +{ +} + +/** + * \fn void Edi_Int_IRQ_Handler(struct regs *Regs) + * \brief EDI IRQ Handler - Calls the handler + * \param Regs Register state at IRQ call + */ +void Edi_Int_IRQ_Handler(struct regs *Regs) +{ + int i; + for( i = 0; i < 16; i ++ ) + { + if(!gEdi_IRQObjects[i].State) continue; // Unused, Skip + if(gEdi_IRQObjects[i].Num != Regs->int_no) continue; // Another IRQ, Skip + if(!gEdi_IRQObjects[i].Handler) continue; // No Handler, Skip + gEdi_IRQObjects[i].Handler( Regs->int_no ); // Call Handler + return; + } +} + + +// === CLASS DECLARATION === +static edi_function_declaration_t scEdi_Int_Functions_IRQ[] = { + {"int32_t", "init_interrupt", 1, 3, NULL, //scEdi_Int_Variables_IO[0], + (function_pointer)Edi_Int_IRQ_InitInt + }, + {"uint32_t", "interrupt_get_irq", 1, 1, NULL, //scEdi_Int_Variables_IO[1], + (function_pointer)Edi_Int_IRQ_GetInt + }, + {"void", "interrupt_set_handler", 1, 2, NULL, //scEdi_Int_Variables_IO[2], + (function_pointer)Edi_Int_IRQ_GetInt + }, + {"void", "interrupt_return", 1, 1, NULL, //scEdi_Int_Variables_IO[3], + (function_pointer)Edi_Int_IRQ_GetInt + } + }; +static edi_class_declaration_t scEdi_Int_Class_IRQ = + { + INTERRUPTS_CLASS, 1, 12, + scEdi_Int_Functions_IRQ, + Edi_Int_IRQ_Construct, + Edi_Int_IRQ_Destruct, + NULL + }; diff --git a/KernelLand/Modules/Interfaces/EDI/edi_io.inc.c b/KernelLand/Modules/Interfaces/EDI/edi_io.inc.c new file mode 100644 index 00000000..8a6ef22e --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/edi_io.inc.c @@ -0,0 +1,391 @@ +/* + * AcessOS EDI Interface + * - IO Port Class + * + * By John Hodge (thePowersGang) + * + * This file has been released into the public domain. + * You are free to use it as you wish. + */ +#include "edi/edi.h" + +// === TYPES === +typedef struct { + uint16_t State; // 0: Unallocated, 1: Allocated, 2: Initialised, (Bit 0x8000 set if in heap) + uint16_t Num; +} tEdiPort; + +// === GLOBALS === +#define NUM_PREALLOC_PORTS 128 +tEdiPort gEdi_PortObjects[NUM_PREALLOC_PORTS]; + +// === FUNCTIONS === +/** + * \fn object_pointer Edi_Int_IO_Construct(void) + * \brief Creates a new IO Port Object + * \return Pointer to object + */ +object_pointer Edi_Int_IO_Construct(void) +{ + tEdiPort *ret; + int i; + // Search for a free preallocated port + for( i = 0; i < NUM_PREALLOC_PORTS; i ++ ) + { + if(gEdi_PortObjects[i].State) continue; + gEdi_PortObjects[i].State = 1; + gEdi_PortObjects[i].Num = 0; + return &gEdi_PortObjects[i]; + } + // Else, use heap space + ret = malloc( sizeof(tEdiPort) ); + ret->State = 0x8001; + ret->Num = 0; + return ret; +} + +/** + * \fn void Edi_Int_IO_Destruct(object_pointer Object) + * \brief Destruct an IO Port Object + * \param Object Object to destroy + */ +void Edi_Int_IO_Destruct(object_pointer Object) +{ + tEdiPort *obj; + // Get Data Pointer + VALIDATE_PTR(Object,); + obj = GET_DATA(Object); + + if(obj->State & 0x8000) { // If in heap, free + free(Object); + } else { // Otherwise, mark as unallocated + obj->State = 0; + } +} + +/** + * \fn int32_t Edi_Int_IO_InitPort(object_pointer Object, uint16_t Port) + * \brief Initialises an IO Port + * \param Object Object Pointer (this) + * \param Port Port Number to use + */ +int32_t Edi_Int_IO_InitPort(object_pointer Object, uint16_t Port) +{ + tEdiPort *obj; + // Get Data Pointer + VALIDATE_PTR(Object, 0); + obj = GET_DATA(Object); + + if( !obj->State ) return 0; + obj->Num = Port; + obj->State &= ~0x3FFF; + obj->State |= 2; // Set initialised flag + return 1; +} + +/** + * \fn uint16_t Edi_Int_IO_GetPortNum(object_pointer Object) + * \brief Returns the port number associated with the object + * \param Object Port Object to get number from + * \return Port Number + */ +uint16_t Edi_Int_IO_GetPortNum(object_pointer Object) +{ + tEdiPort *obj; + // Get Data Pointer + VALIDATE_PTR(Object, 0); + obj = GET_DATA(Object); + // Check if valid + if( !obj->State ) return 0; + // Return Port No + return obj->Num; +} + +/** + * \fn int32_t Edi_Int_IO_ReadByte(object_pointer Object, uint8_t *out) + * \brief Read a byte from an IO port + * \param Object Port Object + * \param out Pointer to put read data + */ +int32_t Edi_Int_IO_ReadByte(object_pointer Object, uint8_t *out) +{ + tEdiPort *obj; + // Get Data Pointer + VALIDATE_PTR(Object, 0); + obj = GET_DATA(Object); + + if( !obj->State ) return 0; + if( obj->State & 1 ) return -1; // Unintialised + + __asm__ __volatile__ ( "inb %%dx, %%al" : "=a" (*out) : "d" ( obj->Num ) ); + + return 1; +} + +/** + * \fn int32_t Edi_Int_IO_ReadWord(object_pointer Object, uint16_t *out) + * \brief Read a word from an IO port + * \param Object Port Object + * \param out Pointer to put read data + */ +int32_t Edi_Int_IO_ReadWord(object_pointer Object, uint16_t *out) +{ + + tEdiPort *obj; + // Get Data Pointer + VALIDATE_PTR(Object, 0); + obj = GET_DATA(Object); + if( !obj->State ) return 0; + if( obj->State & 1 ) return -1; // Unintialised + + __asm__ __volatile__ ( "inw %%dx, %%ax" : "=a" (*out) : "d" ( obj->Num ) ); + + return 1; +} + +/** + * \fn int32_t Edi_Int_IO_ReadDWord(object_pointer Object, uint32_t *out) + * \brief Read a double word from an IO port + * \param Object Port Object + * \param out Pointer to put read data + */ +int32_t Edi_Int_IO_ReadDWord(object_pointer Object, uint32_t *out) +{ + + tEdiPort *obj; + // Get Data Pointer + VALIDATE_PTR(Object, 0); + obj = GET_DATA(Object); + if( !obj->State ) return 0; + if( obj->State & 1 ) return -1; // Unintialised + + __asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*out) : "d" ( obj->Num ) ); + + return 1; +} + +/** + * \fn int32_t Edi_Int_IO_ReadQWord(object_pointer Object, uint64_t *out) + * \brief Read a quad word from an IO port + * \param Object Port Object + * \param out Pointer to put read data + */ +int32_t Edi_Int_IO_ReadQWord(object_pointer Object, uint64_t *out) +{ + uint32_t *out32 = (uint32_t*)out; + tEdiPort *obj; + // Get Data Pointer + VALIDATE_PTR(Object, 0); + obj = GET_DATA(Object); + if( !obj->State ) return 0; + if( obj->State & 1 ) return -1; // Unintialised + + __asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*out32) : "d" ( obj->Num ) ); + __asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*(out32+1)) : "d" ( obj->Num+4 ) ); + + return 1; +} + +/** + * \fn int32_t Edi_Int_IO_ReadString(object_pointer Object, uint32_t Length, uint8_t *out) + * \brief Read a byte from an IO port + * \param Object Port Object + * \param Length Number of bytes to read + * \param out Pointer to put read data + */ +int32_t Edi_Int_IO_ReadString(object_pointer Object, uint32_t Length, uint8_t *out) +{ + tEdiPort *obj; + // Get Data Pointer + VALIDATE_PTR(Object, 0); + obj = GET_DATA(Object); + if( !obj->State ) return 0; + if( obj->State & 1 ) return -1; // Unintialised + + __asm__ __volatile__ ( "rep insb" : : "c" (Length), "D" (out), "d" ( obj->Num ) ); + + return 1; +} + +/** + * \fn int32_t Edi_Int_IO_WriteByte(object_pointer Object, uint8_t in) + * \brief Write a byte from an IO port + * \param Object Port Object + * \param in Data to write + */ +int32_t Edi_Int_IO_WriteByte(object_pointer Object, uint8_t in) +{ + tEdiPort *obj; + // Get Data Pointer + VALIDATE_PTR(Object, 0); + obj = GET_DATA(Object); + if( !obj->State ) return 0; + if( obj->State & 1 ) return -1; // Unintialised + + __asm__ __volatile__ ( "outb %%al, %%dx" : : "a" (in), "d" ( obj->Num ) ); + + return 1; +} + +/** + * \fn int32_t Edi_Int_IO_WriteWord(object_pointer Object, uint16_t in) + * \brief Write a word from an IO port + * \param Object Port Object + * \param in Data to write + */ +int32_t Edi_Int_IO_WriteWord(object_pointer Object, uint16_t in) +{ + tEdiPort *obj; + // Get Data Pointer + VALIDATE_PTR(Object, 0); + obj = GET_DATA(Object); + if( !obj->State ) return 0; + if( obj->State & 1 ) return -1; // Unintialised + + __asm__ __volatile__ ( "outw %%ax, %%dx" : : "a" (in), "d" ( obj->Num ) ); + + return 1; +} + +/** + * \fn int32_t Edi_Int_IO_WriteDWord(object_pointer Object, uint32_t in) + * \brief Write a double word from an IO port + * \param Object Port Object + * \param in Data to write + */ +int32_t Edi_Int_IO_WriteDWord(object_pointer Object, uint32_t in) +{ + tEdiPort *obj; + // Get Data Pointer + VALIDATE_PTR(Object, 0); + obj = GET_DATA(Object); + if( !obj->State ) return 0; + if( obj->State & 1 ) return -1; // Unintialised + + __asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (in), "d" ( obj->Num ) ); + + return 1; +} + +/** + * \fn int32_t Edi_Int_IO_WriteQWord(object_pointer Object, uint64_t in) + * \brief Write a quad word from an IO port + * \param Object Port Object + * \param in Data to write + */ +int32_t Edi_Int_IO_WriteQWord(object_pointer Object, uint64_t in) +{ + uint32_t *in32 = (uint32_t*)∈ + tEdiPort *obj; + // Get Data Pointer + VALIDATE_PTR(Object, 0); + obj = GET_DATA(Object); + if( !obj->State ) return 0; + if( obj->State & 1 ) return -1; // Unintialised + + __asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (*in32), "d" ( obj->Num ) ); + __asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (*(in32+1)), "d" ( obj->Num+4 ) ); + + return 1; +} + +/** + * \fn int32_t Edi_Int_IO_WriteString(object_pointer Object, uint32_t Length, uint8_t *in) + * \brief Read a byte from an IO port + * \param Object Port Object + * \param Length Number of bytes to write + * \param in Pointer to of data to write + */ +int32_t Edi_Int_IO_WriteString(object_pointer Object, uint32_t Length, uint8_t *in) +{ + tEdiPort *obj; + // Get Data Pointer + VALIDATE_PTR(Object, 0); + obj = GET_DATA(Object); + if( !obj->State ) return 0; + if( obj->State & 1 ) return -1; // Unintialised + + __asm__ __volatile__ ( "rep outsb" : : "c" (Length), "D" (in), "d" ( obj->Num ) ); + + return 1; +} + +// === CLASS DECLARATION === +/*static edi_variable_declaration_t *scEdi_Int_Variables_IO[] = { + { + {"pointer", "port_object", 0}, + {"uint16_t", "port", 0} + }, + { + {"pointer", "port_object", 0} + }, + { + {"pointer", "port_object", 0}, + {"pointer int8_t", "out", 0} + } +};*/ +static edi_function_declaration_t scEdi_Int_Functions_IO[] = { + {"int32_t", "init_io_port", 1, 2, NULL, //scEdi_Int_Variables_IO[0], + (function_pointer)Edi_Int_IO_InitPort + }, + {"uint16_t", "get_port_number", 1, 1, NULL, //scEdi_Int_Variables_IO[1], + (function_pointer)Edi_Int_IO_GetPortNum + }, + {"int32_t", "read_byte_io_port", 1, 2, NULL, //scEdi_Int_Variables_IO[2], + (function_pointer)Edi_Int_IO_ReadByte + }, + {"int32_t", "read_word_io_port", 1, 2, NULL/*{ + {"pointer", "port_object", 0}, + {"pointer int16_t", "out", 0} + }*/, + (function_pointer)Edi_Int_IO_ReadWord + }, + {"int32_t", "read_long_io_port", 1, 2, NULL/*{ + {"pointer", "port_object", 0}, + {"pointer int32_t", "out", 0} + }*/, + (function_pointer)Edi_Int_IO_ReadDWord + }, + {"int32_t", "read_longlong_io_port", 1, 2, NULL/*{ + {"pointer", "port_object", 0}, + {"pointer int64_t", "out", 0} + }*/, + (function_pointer)Edi_Int_IO_ReadQWord + }, + {"int32_t", "read_string_io_port", 1, 3, NULL/*{ + {"pointer", "port_object", 0}, + {"int32_T", "data_length", 0}, + {"pointer int64_t", "out", 0} + }*/, + (function_pointer)Edi_Int_IO_ReadString + }, + + {"int32_t", "write_byte_io_port", 1, 2, NULL/*{ + {"pointer", "port_object", 0}, + {"int8_t", "in", 0} + }*/, + (function_pointer)Edi_Int_IO_WriteByte}, + {"int32_t", "write_word_io_port", 1, 2, NULL/*{ + {"pointer", "port_object", 0}, + {"int16_t", "in", 0} + }*/, + (function_pointer)Edi_Int_IO_WriteWord}, + {"int32_t", "write_long_io_port", 1, 2, NULL/*{ + {"pointer", "port_object", 0}, + {"int32_t", "in", 0} + }*/, + (function_pointer)Edi_Int_IO_WriteDWord}, + {"int32_t", "write_longlong_io_port", 1, 2, NULL/*{ + {"pointer", "port_object", 0}, + {"int64_t", "in", 0} + }*/, + (function_pointer)Edi_Int_IO_WriteQWord} + }; +static edi_class_declaration_t scEdi_Int_Class_IO = + { + IO_PORT_CLASS, 1, 12, + scEdi_Int_Functions_IO, + Edi_Int_IO_Construct, + Edi_Int_IO_Destruct, + NULL + }; diff --git a/KernelLand/Modules/Interfaces/EDI/main.c b/KernelLand/Modules/Interfaces/EDI/main.c new file mode 100644 index 00000000..c3d0272a --- /dev/null +++ b/KernelLand/Modules/Interfaces/EDI/main.c @@ -0,0 +1,496 @@ +/* + * Acess2 EDI Layer + */ +#define DEBUG 0 +#define VERSION ((0<<8)|1) +#include +#include +#include +#define IMPLEMENTING_EDI 1 +#include "edi/edi.h" + +#define VALIDATE_PTR(_ptr,_err) do{if(!(_ptr))return _err; }while(0) +#define GET_DATA(_ptr) (Object) + +#include "edi_io.inc.c" +#include "edi_int.inc.c" + +// === STRUCTURES === +typedef struct sAcessEdiDriver { + struct sAcessEdiDriver *Next; + tDevFS_Driver Driver; + int FileCount; + struct { + char *Name; + tVFS_Node Node; + } *Files; + edi_object_metadata_t *Objects; + edi_initialization_t Init; + driver_finish_t Finish; +} tAcessEdiDriver; + +// === PROTOTYPES === + int EDI_Install(char **Arguments); + int EDI_DetectDriver(void *Base); + int EDI_LoadDriver(void *Base); +vfs_node *EDI_FS_ReadDir(vfs_node *Node, int Pos); +vfs_node *EDI_FS_FindDir(vfs_node *Node, char *Name); + int EDI_FS_CharRead(vfs_node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + int EDI_FS_CharWrite(vfs_node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + int EDI_FS_IOCtl(vfs_node *Node, int Id, void *Data); +data_pointer EDI_GetInternalClass(edi_string_t ClassName); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, EDI, EDI_Install, NULL, NULL); +tModuleLoader gEDI_Loader = { + NULL, "EDI", EDI_DetectDriver, EDI_LoadDriver, NULL +}; +tSpinlock glEDI_Drivers; +tAcessEdiDriver *gEdi_Drivers = NULL; +edi_class_declaration_t *gcEdi_IntClasses[] = { + &scEdi_Int_Class_IO, &scEdi_Int_Class_IRQ +}; +#define NUM_INT_CLASSES (sizeof(gcEdi_IntClasses)/sizeof(gcEdi_IntClasses[0])) +char *csCharNumbers[] = {"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9"}; +char *csBlockNumbers[] = {"blk0", "blk1", "blk2", "blk3", "blk4", "blk5", "blk6", "blk7", "blk8", "blk9"}; + +// === CODE === +/** + * \fn int EDI_Install(char **Arguments) + * \brief Stub intialisation routine + */ +int EDI_Install(char **Arguments) +{ + Module_RegisterLoader( &gEDI_Loader ); + return 1; +} + +/** + * \brief Detects if a driver should be loaded by the EDI subsystem + */ +int EDI_DetectDriver(void *Base) +{ + if( Binary_FindSymbol(BASE, "driver_init", NULL) == 0 ) + return 0; + + return 1; +} + +/** + * \fn int Edi_LoadDriver(void *Base) + * \brief Load an EDI Driver from a loaded binary + * \param Base Binary Handle + * \return 0 on success, non zero on error + */ +int EDI_LoadDriver(void *Base) +{ + driver_init_t init; + driver_finish_t finish; + tAcessEdiDriver *info; + int i, j; + int devfsId; + edi_class_declaration_t *classes; + + ENTER("pBase", Base); + + // Get Functions + if( !Binary_FindSymbol(Base, "driver_init", (Uint*)&init) + || !Binary_FindSymbol(Base, "driver_finish", (Uint*)&finish) ) + { + Warning("[EDI ] Driver %p does not provide both `driver_init` and `driver_finish`\n", Base); + Binary_Unload(Base); + return 0; + } + + // Allocate Driver Information + info = malloc( sizeof(tAcessEdiDriver) ); + info->Finish = finish; + + // Initialise Driver + info->Init = init( 0, NULL ); // TODO: Implement Resources + + LOG("info->Init.driver_name = '%s'", info->Init.driver_name); + LOG("info->Init.num_driver_classes = %i", info->Init.num_driver_classes); + + // Count mappable classes + classes = info->Init.driver_classes; + info->FileCount = 0; + info->Objects = NULL; + for( i = 0, j = 0; i < info->Init.num_driver_classes; i++ ) + { + if( strncmp(classes[i].name, "EDI-CHARACTER-DEVICE", 32) == 0 ) + { + data_pointer *obj; + // Initialise Object Instances + for( ; (obj = classes[i].constructor()); j++ ) { + LOG("%i - Constructed '%s'", j, classes[i].name); + info->FileCount ++; + info->Objects = realloc(info->Objects, sizeof(*info->Objects)*info->FileCount); + info->Objects[j].object = obj; + info->Objects[j].object_class = &classes[i]; + } + } + else + LOG("%i - %s", i, classes[i].name); + } + + if(info->FileCount) + { + int iNumChar = 0; + // Create VFS Nodes + info->Files = malloc( info->FileCount * sizeof(*info->Files) ); + memset(info->Files, 0, info->FileCount * sizeof(*info->Files)); + j = 0; + for( j = 0; j < info->FileCount; j++ ) + { + classes = info->Objects[j].object_class; + if( strncmp(classes->name, "EDI-CHARACTER-DEVICE", 32) == 0 ) + { + LOG("%i - %s", j, csCharNumbers[iNumChar]); + info->Files[j].Name = csCharNumbers[iNumChar]; + info->Files[j].Node.NumACLs = 1; + info->Files[j].Node.ACLs = &gVFS_ACL_EveryoneRW; + info->Files[j].Node.ImplPtr = &info->Objects[j]; + info->Files[j].Node.Read = EDI_FS_CharRead; + info->Files[j].Node.Write = EDI_FS_CharWrite; + info->Files[j].Node.IOCtl = EDI_FS_IOCtl; + info->Files[j].Node.CTime = + info->Files[j].Node.MTime = + info->Files[j].Node.ATime = now(); + + iNumChar ++; + continue; + } + } + + // Create DevFS Driver + info->Driver.ioctl = EDI_FS_IOCtl; + memsetda(&info->Driver.rootNode, 0, sizeof(vfs_node) / 4); + info->Driver.Name = info->Init.driver_name; + info->Driver.RootNode.Flags = VFS_FFLAG_DIRECTORY; + info->Driver.RootNode.NumACLs = 1; + info->Driver.RootNode.ACLs = &gVFS_ACL_EveryoneRX; + info->Driver.RootNode.Length = info->FileCount; + info->Driver.RootNode.ImplPtr = info; + info->Driver.RootNode.ReadDir = EDI_FS_ReadDir; + info->Driver.RootNode.FindDir = EDI_FS_FindDir; + info->Driver.RootNode.IOCtl = EDI_FS_IOCtl; + + // Register + devfsId = dev_addDevice( &info->Driver ); + if(devfsId == -1) { + free(info->Files); // Free Files + info->Finish(); // Clean up driver + free(info); // Free info structure + Binary_Unload(iDriverBase); // Unload library + return -3; // Return error + } + } + + // Append to loaded list; + LOCK(&glEDI_Drivers); + info->Next = gEDI_Drivers; + gEDI_Drivers = info; + RELEASE(&glEDI_Drivers); + + LogF("[EDI ] Loaded Driver '%s' (%s)\n", info->Init.driver_name, Path); + LEAVE('i', 1); + return 1; +} + +// --- Filesystem Interaction --- +/** + * \brief Read from a drivers class list + * \param Node Driver's Root Node + * \param Pos Index of file to get + */ +char *EDI_FS_ReadDir(tVFS_Node *Node, int Pos) +{ + tAcessEdiDriver *info; + + // Sanity Check + if(!Node) return NULL; + + // Get Information Structure + info = (void *) Node->ImplPtr; + if(!info) return NULL; + + // Check Position + if(Pos < 0) return NULL; + if(Pos >= info->FileCount) return NULL; + + return strdup( info->Files[Pos].Name ); +} + +/** + * \fn tVFS_Node *EDI_FS_FindDir(tVFS_Node *Node, char *Name) + * \brief Find a named file in a driver + * \param Node Driver's Root Node + * \param Name Name of file to find + */ +tVFS_Node *EDI_FS_FindDir(tVFS_Node *Node, char *Name) +{ + tAcessEdiDriver *info; + int i; + + // Sanity Check + if(!Node) return NULL; + if(!Name) return NULL; + + // Get Information Structure + info = (void *) Node->ImplPtr; + if(!info) return NULL; + + for( i = 0; i < info->FileCount; i++ ) + { + if(strcmp(info->Files[i].name, Name) == 0) + return &info->Files[i].Node; + } + + return NULL; +} + +/** + * \fn Uint64 EDI_FS_CharRead(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Read from an EDI Character Device + * \param Node File Node + * \param Offset Offset into file (ignored) + * \param Length Number of characters to read + * \param Buffer Destination for data + * \return Number of characters read + */ +Uint64 EDI_FS_CharRead(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + edi_object_metadata_t *meta; + edi_class_declaration_t *class; + + // Sanity Check + if(!Node || !Buffer) return 0; + if(Length <= 0) return 0; + // Get Object Metadata + meta = (void *) Node->ImplPtr; + if(!meta) return 0; + + // Get Class + class = meta->object_class; + if(!class) return 0; + + // Read from object + if( ((tAnyFunction) class->methods[0].code)( meta->object, Buffer, Length )) + return Length; + + return 0; +} + +/** + * \fn Uint64 EDI_FS_CharWrite(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Write to an EDI Character Device + * \param Node File Node + * \param Offset Offset into file (ignored) + * \param Length Number of characters to write + * \param Buffer Source for data + * \return Number of characters written + */ +Uint64 EDI_FS_CharWrite(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + edi_object_metadata_t *meta; + edi_class_declaration_t *class; + + // Sanity Check + if(!Node || !Buffer) return 0; + if(Length <= 0) return 0; + // Get Object Metadata + meta = (void *) Node->ImplPtr; + if(!meta) return 0; + + // Get Class + class = meta->object_class; + if(!class) return 0; + + // Write to object + if( ((tAnyFunction) class->methods[1].code)( meta->object, Buffer, Length )) + return Length; + + return 0; +} + +/** + * \fn int EDI_FS_IOCtl(tVFS_Node *Node, int Id, void *Data) + * \brief Perfom an IOCtl call on the object + */ +int EDI_FS_IOCtl(tVFS_Node *Node, int Id, void *Data) +{ + return 0; +} + +// --- EDI Functions --- +/** + * \fn data_pointer EDI_GetDefinedClass(edi_string_t ClassName) + * \brief Gets the structure of a driver defined class + * \param ClassName Name of class to find + * \return Class definition or NULL + */ +data_pointer EDI_GetDefinedClass(edi_string_t ClassName) +{ + int i; + tAcessEdiDriver *drv; + edi_class_declaration_t *classes; + + for(drv = gEdi_Drivers; + drv; + drv = drv->Next ) + { + classes = drv->Init.driver_classes; + for( i = 0; i < drv->Init.num_driver_classes; i++ ) + { + if( strncmp(classes[i].name, ClassName, 32) == 0 ) + return &classes[i]; + } + } + return NULL; +} + +/** + * \fn int32_t EDI_CheckClassExistence(edi_string_t ClassName) + * \brief Checks if a class exists + * \param ClassName Name of class + * \return 1 if the class exists, -1 otherwise + */ +int32_t EDI_CheckClassExistence(edi_string_t ClassName) +{ + //LogF("check_class_existence: (ClassName='%s')\n", ClassName); + if(EDI_GetInternalClass(ClassName)) + return 1; + + if(EDI_GetDefinedClass(ClassName)) // Driver Defined + return 1; + + return -1; +} + +/** + * \fn edi_object_metadata_t EDI_ConstructObject(edi_string_t ClassName) + * \brief Construct an instance of an class (an object) + * \param ClassName Name of the class to construct + */ +edi_object_metadata_t EDI_ConstructObject(edi_string_t ClassName) +{ + edi_object_metadata_t ret = {0, 0}; + edi_class_declaration_t *class; + + //LogF("EDI_ConstructObject: (ClassName='%s')\n", ClassName); + + // Get class definition + if( !(class = EDI_GetInternalClass(ClassName)) ) // Internal + if( !(class = EDI_GetDefinedClass(ClassName)) ) // Driver Defined + return ret; // Return ERROR + + // Initialise + ret.object = class->constructor(); + if( !ret.object ) + return ret; // Return ERROR + + // Set declaration pointer + ret.object_class = class; + + //LogF("EDI_ConstructObject: RETURN {0x%x,0x%x}\n", ret.object, ret.object_class); + return ret; +} + +/** + * \fn void EDI_DestroyObject(edi_object_metadata_t Object) + * \brief Destroy an instance of a class + * \param Object Object to destroy + */ +void EDI_DestroyObject(edi_object_metadata_t Object) +{ + if( !Object.object ) return; + if( !Object.object_class ) return; + + ((edi_class_declaration_t*)(Object.object_class))->destructor( &Object ); +} + +/** + * \fn function_pointer EDI_GetMethodByName(data_pointer ObjectClass, edi_string_t MethodName) + * \brief Get a method of a class by it's name + * \param ObjectClass Pointer to a ::edi_object_metadata_t of the object + * \param MethodName Name of the desired method + * \return Function address or NULL + */ +function_pointer EDI_GetMethodByName(data_pointer ObjectClass, edi_string_t MethodName) +{ + edi_class_declaration_t *dec = ObjectClass; + int i; + + //LogF("get_method_by_name: (ObjectClass=0x%x, MethodName='%s')\n", ObjectClass, MethodName); + + if(!ObjectClass) return NULL; + + for(i = 0; i < dec->num_methods; i++) + { + if(strncmp(MethodName, dec->methods[i].name, 32) == 0) + return dec->methods[i].code; + } + return NULL; +} + +#if 0 +function_pointer get_method_by_declaration(data_pointer Class, edi_function_declaration_t Declaration); +#endif + +/** + * \fn edi_string_ptr_t EDI_GetClassParent(edi_string_t ClassName) + * \brief Get the parent of the named class + * \todo Implement + */ +edi_string_ptr_t EDI_GetClassParent(edi_string_t ClassName) +{ + WarningEx("EDI", "`get_class_parent` is unimplemented"); + return NULL; +} + +/** + * \fn data_pointer EDI_GetInternalClass(edi_string_t ClassName) + * \brief Get a builtin class + * \param ClassName Name of class to find + * \return Pointer to the ::edi_class_declaration_t of the class + */ +data_pointer EDI_GetInternalClass(edi_string_t ClassName) +{ + int i; + //LogF("get_internal_class: (ClassName='%s')\n", ClassName); + for( i = 0; i < NUM_INT_CLASSES; i++ ) + { + if( strncmp( gcEdi_IntClasses[i]->name, ClassName, 32 ) == 0 ) { + return gcEdi_IntClasses[i]; + } + } + //LogF("get_internal_class: RETURN NULL\n"); + return NULL; +} + +/** + * \fn edi_string_ptr_t EDI_GetObjectClass(data_pointer Object) + * \brief Get the name of the object of \a Object + * \param Object Object to get name of + * \return Pointer to the class name + */ +edi_string_ptr_t EDI_GetObjectClass(data_pointer ObjectClass) +{ + edi_object_metadata_t *Metadata = ObjectClass; + // Sanity Check + if(!ObjectClass) return NULL; + if(!(edi_class_declaration_t*) Metadata->object_class) return NULL; + + // Return Class Name + return ((edi_class_declaration_t*) Metadata->object_class)->name; +} + +// === EXPORTS === +EXPORTAS(EDI_CheckClassExistence, check_class_existence); +EXPORTAS(EDI_ConstructObject, construct_object); +EXPORTAS(EDI_DestroyObject, destroy_object); +EXPORTAS(EDI_GetMethodByName, get_method_by_name); +EXPORTAS(EDI_GetClassParent, get_class_parent); +EXPORTAS(EDI_GetInternalClass, get_internal_class); +EXPORTAS(EDI_GetObjectClass, get_object_class); diff --git a/KernelLand/Modules/Interfaces/Makefile.tpl b/KernelLand/Modules/Interfaces/Makefile.tpl new file mode 100644 index 00000000..80c6d4dd --- /dev/null +++ b/KernelLand/Modules/Interfaces/Makefile.tpl @@ -0,0 +1 @@ +-include ../../Makefile.tpl diff --git a/KernelLand/Modules/Interfaces/UDI/Makefile b/KernelLand/Modules/Interfaces/UDI/Makefile new file mode 100644 index 00000000..4c199f18 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/Makefile @@ -0,0 +1,10 @@ +# +# + +CPPFLAGS = -I./include +OBJ = main.o logging.o strmem.o imc.o mem.o buf.o cb.o +OBJ += meta_mgmt.o meta_gio.o +OBJ += physio.o physio/meta_bus.o physio/meta_intr.o +NAME = UDI + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Interfaces/UDI/Notes.txt b/KernelLand/Modules/Interfaces/UDI/Notes.txt new file mode 100644 index 00000000..1e2e9d45 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/Notes.txt @@ -0,0 +1,36 @@ + + logical volume metalanguage - for file system drivers to use, network protocol + metalanguage and audio support + madeofstaples expressed his interest in reviewing the OpenUSBDI specification to update + it for USB 3.0 + + +=== Initialisation === +udi_init_t + > udi_init_info + > This is the only defined symbol in a UDI driver + +udi_primary_init_t +- UDI_MAX_SCRATCH +- UDI_OP_LONG_EXEC + +udi_secondary_init_t + +udi_ops_init_t + +udi_cb_init_t + +udi_cb_select_t + +udi_gcb_init_t + +udi_init_context_t + +udi_limits_t +- UDI_MIN_ALLOC_LIMIT +- UDI_MIN_TRACE_LOG_LIMIT +- UDI_MIN_INSTANCE_ATTR_LIMIT + +udi_chan_context_t + +udi_child_chan_context_t diff --git a/KernelLand/Modules/Interfaces/UDI/buf.c b/KernelLand/Modules/Interfaces/UDI/buf.c new file mode 100644 index 00000000..5327cc8b --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/buf.c @@ -0,0 +1,70 @@ +/** + * \file buf.c + * \author John Hodge (thePowersGang) + * + * Buffer Manipulation + */ +#include +#include + +// === EXPORTS === +EXPORT(udi_buf_copy); +EXPORT(udi_buf_write); +EXPORT(udi_buf_read); +EXPORT(udi_buf_free); + +// === CODE === +void udi_buf_copy( + udi_buf_copy_call_t *callback, + udi_cb_t *gcb, + udi_buf_t *src_buf, + udi_size_t src_off, + udi_size_t src_len, + udi_buf_t *dst_buf, + udi_size_t dst_off, + udi_size_t dst_len, + udi_buf_path_t path_handle + ) +{ + UNIMPLEMENTED(); +} + +/** + * \brief Write to a buffer + * \param callback Function to call once the write has completed + * \param gcb Control Block + * \param src_mem Source Data + * \param src_len Length of source data + * \param dst_buf Destination buffer + * \param dst_off Destination offset in the buffer + * \param dst_len Length of destination area (What the?, Redundant + * Department of redundacny department) + * \param path_handle ??? + */ +void udi_buf_write( + udi_buf_write_call_t *callback, + udi_cb_t *gcb, + const void *src_mem, + udi_size_t src_len, + udi_buf_t *dst_buf, + udi_size_t dst_off, + udi_size_t dst_len, + udi_buf_path_t path_handle + ) +{ + UNIMPLEMENTED(); +} + +void udi_buf_read( + udi_buf_t *src_buf, + udi_size_t src_off, + udi_size_t src_len, + void *dst_mem ) +{ + UNIMPLEMENTED(); +} + +void udi_buf_free(udi_buf_t *buf) +{ + UNIMPLEMENTED(); +} diff --git a/KernelLand/Modules/Interfaces/UDI/cb.c b/KernelLand/Modules/Interfaces/UDI/cb.c new file mode 100644 index 00000000..b615dfeb --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/cb.c @@ -0,0 +1,60 @@ +/** + * \file cb.c + * \author John Hodge (thePowersGang) + * \brief Control block code + */ +#include +#include + +// === CODE === +void udi_cb_alloc ( + udi_cb_alloc_call_t *callback, //!< Function to be called when the CB is allocated + udi_cb_t *gcb, //!< Parent Control Block + udi_index_t cb_idx, + udi_channel_t default_channel + ) +{ + UNIMPLEMENTED(); +} + +void udi_cb_alloc_dynamic( + udi_cb_alloc_call_t *callback, + udi_cb_t *gcb, + udi_index_t cb_idx, + udi_channel_t default_channel, + udi_size_t inline_size, + udi_layout_t *inline_layout + ) +{ + UNIMPLEMENTED(); +} + +void udi_cb_alloc_batch( + udi_cb_alloc_batch_call_t *callback, //!< + udi_cb_t *gcb, //!< + udi_index_t cb_idx, + udi_index_t count, + udi_boolean_t with_buf, + udi_size_t buf_size, + udi_buf_path_t path_handle + ) +{ + UNIMPLEMENTED(); +} + +void udi_cb_free(udi_cb_t *cb) +{ + UNIMPLEMENTED(); +} + +void udi_cancel(udi_cancel_call_t *callback, udi_cb_t *gcb) +{ + UNIMPLEMENTED(); +} + +// === EXPORTS === +EXPORT(udi_cb_alloc); +EXPORT(udi_cb_alloc_dynamic); +EXPORT(udi_cb_alloc_batch); +EXPORT(udi_cb_free); +EXPORT(udi_cancel); diff --git a/KernelLand/Modules/Interfaces/UDI/imc.c b/KernelLand/Modules/Interfaces/UDI/imc.c new file mode 100644 index 00000000..cd15e070 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/imc.c @@ -0,0 +1,69 @@ +/** + * \file imc.c + * \author John Hodge (thePowersGang) + */ +#include +#include + +// === EXPORTS === +EXPORT(udi_channel_anchor); +EXPORT(udi_channel_spawn); +EXPORT(udi_channel_set_context); +EXPORT(udi_channel_op_abort); +EXPORT(udi_channel_close); +EXPORT(udi_channel_event_ind); +EXPORT(udi_channel_event_complete); + +// === CODE === +/** + */ +void udi_channel_anchor( + udi_channel_anchor_call_t *callback, udi_cb_t *gcb, + udi_channel_t channel, udi_index_t ops_idx, void *channel_context + ) +{ + Warning("%s Unimplemented", __func__); +} + +/** + */ +extern void udi_channel_spawn( + udi_channel_spawn_call_t *callback, udi_cb_t *gcb, + udi_channel_t channel, udi_index_t spawn_idx, + udi_index_t ops_idx, void *channel_context + ) +{ + Warning("%s Unimplemented", __func__); +} + +/** + * + */ +void udi_channel_set_context( + udi_channel_t target_channel, void *channel_context + ) +{ + Warning("%s Unimplemented", __func__); +} + +void udi_channel_op_abort( + udi_channel_t target_channel, udi_cb_t *orig_cb + ) +{ + Warning("%s Unimplemented", __func__); +} + +void udi_channel_close(udi_channel_t channel) +{ + Warning("%s Unimplemented", __func__); +} + +void udi_channel_event_ind(udi_channel_event_cb_t *cb) +{ + udi_channel_event_complete(cb, UDI_OK); +} + +void udi_channel_event_complete(udi_channel_event_cb_t *cb, udi_status_t status) +{ + Warning("%s Unimplemented", __func__); +} diff --git a/KernelLand/Modules/Interfaces/UDI/include/physio/meta_bus.h b/KernelLand/Modules/Interfaces/UDI/include/physio/meta_bus.h new file mode 100644 index 00000000..7b88ece8 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/physio/meta_bus.h @@ -0,0 +1,74 @@ +/** + * \file physio/meta_bus.h + */ +#ifndef _PHYSIO_META_BUS_H_ +#define _PHYSIO_META_BUS_H_ + +#include +#include + +typedef const struct udi_bus_device_ops_s udi_bus_device_ops_t; +typedef const struct udi_bus_bridge_ops_s udi_bus_bridge_ops_t; +typedef struct udi_bus_bind_cb_s udi_bus_bind_cb_t; +typedef void udi_bus_unbind_req_op_t(udi_bus_bind_cb_t *cb); +typedef void udi_bus_unbind_ack_op_t(udi_bus_bind_cb_t *cb); +typedef void udi_bus_bind_req_op_t(udi_bus_bind_cb_t *cb); +typedef void udi_bus_bind_ack_op_t( + udi_bus_bind_cb_t *cb, + udi_dma_constraints_t dma_constraints, + udi_ubit8_t preferred_endianness, + udi_status_t status + ); + + +struct udi_bus_device_ops_s +{ + udi_channel_event_ind_op_t *channel_event_ind_op; + udi_bus_bind_ack_op_t *bus_bind_ack_op; + udi_bus_unbind_ack_op_t *bus_unbind_ack_op; + udi_intr_attach_ack_op_t *intr_attach_ack_op; + udi_intr_detach_ack_op_t *intr_detach_ack_op; +}; +/* Bus Device Ops Vector Number */ +#define UDI_BUS_DEVICE_OPS_NUM 1 + +struct udi_bus_bridge_ops_s +{ + udi_channel_event_ind_op_t *channel_event_ind_op; + udi_bus_bind_req_op_t *bus_bind_req_op; + udi_bus_unbind_req_op_t *bus_unbind_req_op; + udi_intr_attach_req_op_t *intr_attach_req_op; + udi_intr_detach_req_op_t *intr_detach_req_op; +}; +/* Bus Bridge Ops Vector Number */ +#define UDI_BUS_BRIDGE_OPS_NUM + +struct udi_bus_bind_cb_s +{ + udi_cb_t gcb; +}; +/* Bus Bind Control Block Group Number */ +#define UDI_BUS_BIND_CB_NUM 1 + + +extern void udi_bus_bind_req(udi_bus_bind_cb_t *cb); + +extern void udi_bus_bind_ack( + udi_bus_bind_cb_t *cb, + udi_dma_constraints_t dma_constraints, + udi_ubit8_t preferred_endianness, + udi_status_t status + ); +/* Values for preferred_endianness */ +#define UDI_DMA_BIG_ENDIAN (1U<<5) +#define UDI_DMA_LITTLE_ENDIAN (1U<<6) +#define UDI_DMA_ANY_ENDIAN (1U<<0) + +extern void udi_bus_unbind_req(udi_bus_bind_cb_t *cb); +extern void udi_bus_unbind_ack(udi_bus_bind_cb_t *cb); + + + + + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/physio/meta_intr.h b/KernelLand/Modules/Interfaces/UDI/include/physio/meta_intr.h new file mode 100644 index 00000000..d5dd3941 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/physio/meta_intr.h @@ -0,0 +1,100 @@ +/** + * \file physio/meta_intr.h + */ +#ifndef _PHYSIO_META_INTR_H_ +#define _PHYSIO_META_INTR_H_ + +#include +#include +#include "pio.h" + +typedef struct udi_intr_attach_cb_s udi_intr_attach_cb_t; +typedef void udi_intr_attach_req_op_t(udi_intr_attach_cb_t *intr_attach_cb); +typedef void udi_intr_attach_ack_op_t( + udi_intr_attach_cb_t *intr_attach_cb, + udi_status_t status + ); +typedef struct udi_intr_detach_cb_s udi_intr_detach_cb_t; +typedef void udi_intr_detach_req_op_t(udi_intr_detach_cb_t *intr_detach_cb); +typedef void udi_intr_detach_ack_op_t(udi_intr_detach_cb_t *intr_detach_cb); +typedef const struct udi_intr_handler_ops_s udi_intr_handler_ops_t; +typedef const struct udi_intr_dispatcher_ops_s udi_intr_dispatcher_ops_t; +typedef struct udi_intr_event_cb_s udi_intr_event_cb_t; +typedef void udi_intr_event_ind_op_t(udi_intr_event_cb_t *intr_event_cb, udi_ubit8_t flags); +typedef void udi_intr_event_rdy_op_t(udi_intr_event_cb_t *intr_event_cb); + + +struct udi_intr_attach_cb_s +{ + udi_cb_t gcb; + udi_index_t interrupt_idx; + udi_ubit8_t min_event_pend; + udi_pio_handle_t preprocessing_handle; +}; +/* Bridge Attach Control Block Group Number */ +#define UDI_BUS_INTR_ATTACH_CB_NUM 2 + +struct udi_intr_detach_cb_s +{ + udi_cb_t gcb; + udi_index_t interrupt_idx; +}; +/* Bridge Detach Control Block Group Number */ +#define UDI_BUS_INTR_DETACH_CB_NUM 3 + +struct udi_intr_handler_ops_s +{ + udi_channel_event_ind_op_t *channel_event_ind_op; + udi_intr_event_ind_op_t *intr_event_ind_op; +}; +/* Interrupt Handler Ops Vector Number */ +#define UDI_BUS_INTR_HANDLER_OPS_NUM 3 + +struct udi_intr_dispatcher_ops_s +{ + udi_channel_event_ind_op_t *channel_event_ind_op; + udi_intr_event_rdy_op_t *intr_event_rdy_op; +}; +/* Interrupt Dispatcher Ops Vector Number */ +#define UDI_BUS_INTR_DISPATCH_OPS_NUM 4 + +struct udi_intr_event_cb_s +{ + udi_cb_t gcb; + udi_buf_t *event_buf; + udi_ubit16_t intr_result; +}; +/* Flag values for interrupt handling */ +#define UDI_INTR_UNCLAIMED (1U<<0) +#define UDI_INTR_NO_EVENT (1U<<1) +/* Bus Interrupt Event Control Block Group Number */ +#define UDI_BUS_INTR_EVENT_CB_NUM 4 + + + +extern void udi_intr_attach_req(udi_intr_attach_cb_t *intr_attach_cb); +extern void udi_intr_attach_ack(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status); +extern void udi_intr_attach_ack_unused(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status); + +extern void udi_intr_detach_req(udi_intr_detach_cb_t *intr_detach_cb); +extern void udi_intr_detach_ack(udi_intr_detach_cb_t *intr_detach_cb); +extern void udi_intr_detach_ack_unused(udi_intr_detach_cb_t *intr_detach_cb); + + +extern void udi_intr_event_ind(udi_intr_event_cb_t *intr_event_cb, udi_ubit8_t flags); +/** + * \brief Values for ::udi_intr_event_ind \a flags + * \{ + */ +#define UDI_INTR_MASKING_NOT_REQUIRED (1U<<0) +#define UDI_INTR_OVERRUN_OCCURRED (1U<<1) +#define UDI_INTR_PREPROCESSED (1U<<2) +/** + * \} + */ + +extern void udi_intr_event_rdy(udi_intr_event_cb_t *intr_event_cb); + + + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/physio/pio.h b/KernelLand/Modules/Interfaces/UDI/include/physio/pio.h new file mode 100644 index 00000000..1ce305f2 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/physio/pio.h @@ -0,0 +1,15 @@ +/** + * \file physio/pio.h + */ +#ifndef _PHYSIO_PIO_H_ +#define _PHYSIO_PIO_H_ + +#include +#include + + +typedef _udi_handle_t udi_pio_handle_t; +/* Null handle value for udi_pio_handle_t */ +#define UDI_NULL_PIO_HANDLE _NULL_HANDLE + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/udi.h b/KernelLand/Modules/Interfaces/UDI/include/udi.h new file mode 100644 index 00000000..53953c07 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/udi.h @@ -0,0 +1,94 @@ +/** + * \file udi.h + */ +#ifndef _UDI_H_ +#define _UDI_H_ + +// Use the core acess file to use the specific size types (plus va_arg) +#include + +#include "udi/arch/x86.h" + +/** + * \name Values and Flags for udi_status_t + * \{ + */ +#define UDI_STATUS_CODE_MASK 0x0000FFFF +#define UDI_STAT_META_SPECIFIC 0x00008000 +#define UDI_SPECIFIC_STATUS_MASK 0x00007FFF +#define UDI_CORRELATE_OFFSET 16 +#define UDI_CORRELATE_MASK 0xFFFF0000 +/* Common Status Values */ +#define UDI_OK 0 +#define UDI_STAT_NOT_SUPPORTED 1 +#define UDI_STAT_NOT_UNDERSTOOD 2 +#define UDI_STAT_INVALID_STATE 3 +#define UDI_STAT_MISTAKEN_IDENTITY 4 +#define UDI_STAT_ABORTED 5 +#define UDI_STAT_TIMEOUT 6 +#define UDI_STAT_BUSY 7 +#define UDI_STAT_RESOURCE_UNAVAIL 8 +#define UDI_STAT_HW_PROBLEM 9 +#define UDI_STAT_NOT_RESPONDING 10 +#define UDI_STAT_DATA_UNDERRUN 11 +#define UDI_STAT_DATA_OVERRUN 12 +#define UDI_STAT_DATA_ERROR 13 +#define UDI_STAT_PARENT_DRV_ERROR 14 +#define UDI_STAT_CANNOT_BIND 15 +#define UDI_STAT_CANNOT_BIND_EXCL 16 +#define UDI_STAT_TOO_MANY_PARENTS 17 +#define UDI_STAT_BAD_PARENT_TYPE 18 +#define UDI_STAT_TERMINATED 19 +#define UDI_STAT_ATTR_MISMATCH 20 +/** + * \} + */ + +/** + * \name Data Layout Specifiers + * \{ + */ +typedef const udi_ubit8_t udi_layout_t; +/* Specific-Length Layout Type Codes */ +#define UDI_DL_UBIT8_T 1 +#define UDI_DL_SBIT8_T 2 +#define UDI_DL_UBIT16_T 3 +#define UDI_DL_SBIT16_T 4 +#define UDI_DL_UBIT32_T 5 +#define UDI_DL_SBIT32_T 6 +#define UDI_DL_BOOLEAN_T 7 +#define UDI_DL_STATUS_T 8 +/* Abstract Element Layout Type Codes */ +#define UDI_DL_INDEX_T 20 +/* Opaque Handle Element Layout Type Codes */ +#define UDI_DL_CHANNEL_T 30 +#define UDI_DL_ORIGIN_T 32 +/* Indirect Element Layout Type Codes */ +#define UDI_DL_BUF 40 +#define UDI_DL_CB 41 +#define UDI_DL_INLINE_UNTYPED 42 +#define UDI_DL_INLINE_DRIVER_TYPED 43 +#define UDI_DL_MOVABLE_UNTYPED 44 +/* Nested Element Layout Type Codes */ +#define UDI_DL_INLINE_TYPED 50 +#define UDI_DL_MOVABLE_TYPED 51 +#define UDI_DL_ARRAY 52 +#define UDI_DL_END 0 +/** + * \} + */ + + +// === INCLUDE SUB-SECTIONS === +#include "udi/cb.h" // Control Blocks +#include "udi/log.h" // Logging +#include "udi/attr.h" // Attributes +#include "udi/strmem.h" // String/Memory +#include "udi/buf.h" // Buffers +#include "udi/mem.h" // Memory Management +#include "udi/imc.h" // Inter-module Communication +#include "udi/meta_mgmt.h" // Management Metalanguage +#include "udi/meta_gio.h" // General IO Metalanguage +#include "udi/init.h" // Init + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/udi/arch/x86.h b/KernelLand/Modules/Interfaces/UDI/include/udi/arch/x86.h new file mode 100644 index 00000000..9c505d33 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/udi/arch/x86.h @@ -0,0 +1,67 @@ + +#ifndef _UDI_ARCH_x86_H_ +#define _UDI_ARCH_x86_H_ + +typedef Sint8 udi_sbit8_t; /* signed 8-bit: -2^7..2^7-1 */ +typedef Sint16 udi_sbit16_t; /* signed 16-bit: -2^15..2^15-1 */ +typedef Sint32 udi_sbit32_t; /* signed 32-bit: -2^31..2^31-1 */ +typedef Uint8 udi_ubit8_t; /* unsigned 8-bit: 0..28-1 */ +typedef Uint16 udi_ubit16_t; /* unsigned 16-bit: 0..216-1 */ +typedef Uint32 udi_ubit32_t; /* unsigned 32-bit: 0..232-1 */ + +typedef udi_ubit8_t udi_boolean_t; /* 0=False; 1..28-1=True */ +#define FALSE 0 +#define TRUE 1 + +typedef size_t udi_size_t; /* buffer size */ +typedef size_t udi_index_t; /* zero-based index type */ + +typedef void *_udi_handle_t; +#define _NULL_HANDLE NULL + +/* Channel Handle */ +typedef _udi_handle_t *udi_channel_t; +#define UDI_NULL_CHANNEL _NULL_HANDLE + +/** + * \brief Buffer Path + */ +typedef _udi_handle_t udi_buf_path_t; +#define UDI_NULL_BUF_PATH _NULL_HANDLE + +typedef _udi_handle_t udi_origin_t; +#define UDI_NULL_ORIGIN _NULL_HANDLE + +typedef Sint64 udi_timestamp_t; + +#define UDI_HANDLE_IS_NULL(handle, handle_type) (handle == NULL) +#define UDI_HANDLE_ID(handle, handle_type) ((Uint32)handle) + +/** + * \name va_arg wrapper + * \{ + */ +#define UDI_VA_ARG(pvar, type, va_code) va_arg(pvar,type) +#define UDI_VA_UBIT8_T +#define UDI_VA_SBIT8_T +#define UDI_VA_UBIT16_T +#define UDI_VA_SBIT16_T +#define UDI_VA_UBIT32_T +#define UDI_VA_SBIT32_T +#define UDI_VA_BOOLEAN_T +#define UDI_VA_INDEX_T +#define UDI_VA_SIZE_T +#define UDI_VA_STATUS_T +#define UDI_VA_CHANNEL_T +#define UDI_VA_ORIGIN_T +#define UDI_VA_POINTER +/** + * \} + */ + +/** + * \brief Status Type + */ +typedef udi_ubit32_t udi_status_t; + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/udi/attr.h b/KernelLand/Modules/Interfaces/UDI/include/udi/attr.h new file mode 100644 index 00000000..a63ce8a7 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/udi/attr.h @@ -0,0 +1,41 @@ +/** + * \file udi_attr.h + */ +#ifndef _UDI_ATTR_H_ +#define _UDI_ATTR_H_ + +typedef struct udi_instance_attr_list_s udi_instance_attr_list_t; +typedef udi_ubit8_t udi_instance_attr_type_t; + +/* Instance attribute limits */ +#define UDI_MAX_ATTR_NAMELEN 32 +#define UDI_MAX_ATTR_SIZE 64 + +/** + * \brief Instance Attribute + */ +struct udi_instance_attr_list_s +{ + char attr_name[UDI_MAX_ATTR_NAMELEN]; + udi_ubit8_t attr_value[UDI_MAX_ATTR_SIZE]; + udi_ubit8_t attr_length; + udi_instance_attr_type_t attr_type; +}; + + +/** + * \brief Instance Attribute Types + * \see ::udi_instance_attr_type_t + */ +enum +{ + UDI_ATTR_NONE, + UDI_ATTR_STRING, + UDI_ATTR_ARRAY8, + UDI_ATTR_UBIT32, + UDI_ATTR_BOOLEAN, + UDI_ATTR_FILE +}; + + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/udi/buf.h b/KernelLand/Modules/Interfaces/UDI/include/udi/buf.h new file mode 100644 index 00000000..fa2428b7 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/udi/buf.h @@ -0,0 +1,105 @@ +/** + * \file udi_buf.h + */ +#ifndef _UDI_BUF_H_ +#define _UDI_BUF_H_ + + +typedef struct udi_buf_s udi_buf_t; +typedef struct udi_xfer_constraints_s udi_xfer_constraints_t; +typedef void udi_buf_copy_call_t(udi_cb_t *gcb, udi_buf_t *new_dst_buf); +typedef void udi_buf_write_call_t(udi_cb_t *gcb, udi_buf_t *new_dst_buf); + +/** + * \brief Describes a buffer + * \note Semi-Opaque + */ +struct udi_buf_s +{ + udi_size_t buf_size; + Uint8 Data[]; //!< ENVIRONMENT ONLY +}; + +/** + * \brief + */ +struct udi_xfer_constraints_s +{ + udi_ubit32_t udi_xfer_max; + udi_ubit32_t udi_xfer_typical; + udi_ubit32_t udi_xfer_granularity; + udi_boolean_t udi_xfer_one_piece; + udi_boolean_t udi_xfer_exact_size; + udi_boolean_t udi_xfer_no_reorder; +}; + +// --- MACROS --- +/** + * \brief Allocates a buffer + */ +#define UDI_BUF_ALLOC(callback, gcb, init_data, size, path_handle) \ + udi_buf_write(callback, gcb, init_data, size, NULL, 0, 0, path_handle) + +/** + * \brief Inserts data into a buffer + */ +#define UDI_BUF_INSERT(callback, gcb, new_data, size, dst_buf, dst_off) \ + udi_buf_write(callback, gcb, new_data, size, dst_buf, dst_off, 0, UDI_NULL_BUF_PATH) + +/** + * \brief Removes data from a buffer (data afterwards will be moved forewards) + */ +#define UDI_BUF_DELETE(callback, gcb, size, dst_buf, dst_off) \ + udi_buf_write(callback, gcb, NULL, 0, dst_buf, dst_off, size, UDI_NULL_BUF_PATH) + +/** + * \brief Duplicates \a src_buf + */ +#define UDI_BUF_DUP(callback, gcb, src_buf, path_handle) \ + udi_buf_copy(callback, gcb, src_buf, 0, (src_buf)->buf_size, NULL, 0, 0, path_handle) + + +/** + * \brief Copies data from one buffer to another + */ +extern void udi_buf_copy( + udi_buf_copy_call_t *callback, + udi_cb_t *gcb, + udi_buf_t *src_buf, + udi_size_t src_off, + udi_size_t src_len, + udi_buf_t *dst_buf, + udi_size_t dst_off, + udi_size_t dst_len, + udi_buf_path_t path_handle ); + +/** + * \brief Copies data from driver space to a buffer + */ +extern void udi_buf_write( + udi_buf_write_call_t *callback, + udi_cb_t *gcb, + const void *src_mem, + udi_size_t src_len, + udi_buf_t *dst_buf, + udi_size_t dst_off, + udi_size_t dst_len, + udi_buf_path_t path_handle + ); + +/** + * \brief Reads data from a buffer into driver space + */ +extern void udi_buf_read( + udi_buf_t *src_buf, + udi_size_t src_off, + udi_size_t src_len, + void *dst_mem ); + +/** + * \brief Frees a buffer + */ +extern void udi_buf_free(udi_buf_t *buf); + + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/udi/cb.h b/KernelLand/Modules/Interfaces/UDI/include/udi/cb.h new file mode 100644 index 00000000..76db5c6e --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/udi/cb.h @@ -0,0 +1,77 @@ +/** + * \file udi_cb.h + */ +#ifndef _UDI_CB_H_ +#define _UDI_CB_H_ + +typedef struct udi_cb_s udi_cb_t; +typedef void udi_cb_alloc_call_t(udi_cb_t *gcb, udi_cb_t *new_cb); +typedef void udi_cb_alloc_batch_call_t(udi_cb_t *gcb, udi_cb_t *first_new_cb); +typedef void udi_cancel_call_t(udi_cb_t *gcb); + +#define UDI_GCB(mcb) (&(mcb)->gcb) +#define UDI_MCB(gcb, cb_type) ((cb_type *)(gcb)) + +/** + * \brief Describes a generic control block + * \note Semi-opaque + */ +struct udi_cb_s +{ + /** + * \brief Channel associated with the control block + */ + udi_channel_t channel; + /** + * \brief Current state + * \note Driver changable + */ + void *context; + /** + * \brief CB's scratch area + */ + void *scratch; + /** + * \brief ??? + */ + void *initiator_context; + /** + * \brief Request Handle? + */ + udi_origin_t origin; +}; + +extern void udi_cb_alloc ( + udi_cb_alloc_call_t *callback, + udi_cb_t *gcb, + udi_index_t cb_idx, + udi_channel_t default_channel + ); + +extern void udi_cb_alloc_dynamic( + udi_cb_alloc_call_t *callback, + udi_cb_t *gcb, + udi_index_t cb_idx, + udi_channel_t default_channel, + udi_size_t inline_size, + udi_layout_t *inline_layout + ); + +extern void udi_cb_alloc_batch( + udi_cb_alloc_batch_call_t *callback, + udi_cb_t *gcb, + udi_index_t cb_idx, + udi_index_t count, + udi_boolean_t with_buf, + udi_size_t buf_size, + udi_buf_path_t path_handle + ); + +extern void udi_cb_free(udi_cb_t *cb); + +extern void udi_cancel(udi_cancel_call_t *callback, udi_cb_t *gcb); + + + + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/udi/imc.h b/KernelLand/Modules/Interfaces/UDI/include/udi/imc.h new file mode 100644 index 00000000..e5b3f3bb --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/udi/imc.h @@ -0,0 +1,92 @@ +/** + * \file udi_imc.h + * \brief Inter-Module Communication + */ +#ifndef _UDI_IMC_H_ +#define _UDI_IMC_H_ + +typedef void udi_channel_anchor_call_t(udi_cb_t *gcb, udi_channel_t anchored_channel); +typedef void udi_channel_spawn_call_t(udi_cb_t *gcb, udi_channel_t new_channel); + +typedef struct udi_channel_event_cb_s udi_channel_event_cb_t; + +typedef void udi_channel_event_ind_op_t(udi_channel_event_cb_t *cb); + +/** + * \brief Anchors a channel end to the current region + */ +extern void udi_channel_anchor( + udi_channel_anchor_call_t *callback, udi_cb_t *gcb, + udi_channel_t channel, udi_index_t ops_idx, void *channel_context + ); + +/** + * \brief Created a new channel between two regions + */ +extern void udi_channel_spawn( + udi_channel_spawn_call_t *callback, + udi_cb_t *gcb, + udi_channel_t channel, + udi_index_t spawn_idx, + udi_index_t ops_idx, + void *channel_context + ); + +/** + * \brief Attaches a new context pointer to the current channel + */ +extern void udi_channel_set_context( + udi_channel_t target_channel, + void *channel_context + ); +/** + * \brief + */ +extern void udi_channel_op_abort( + udi_channel_t target_channel, + udi_cb_t *orig_cb + ); + +/** + * \brief Closes an open channel + */ +extern void udi_channel_close(udi_channel_t channel); + +/** + * \brief Describes a channel event + */ +struct udi_channel_event_cb_s +{ + udi_cb_t gcb; + udi_ubit8_t event; + union { + struct { + udi_cb_t *bind_cb; + } internal_bound; + struct { + udi_cb_t *bind_cb; + udi_ubit8_t parent_ID; + udi_buf_path_t *path_handles; + } parent_bound; + udi_cb_t *orig_cb; + } params; +}; +/* Channel event types */ +#define UDI_CHANNEL_CLOSED 0 +#define UDI_CHANNEL_BOUND 1 +#define UDI_CHANNEL_OP_ABORTED 2 + +/** + * \brief Proxy function + */ +extern void udi_channel_event_ind(udi_channel_event_cb_t *cb); + +/** + * \brief Called when channel event is completed + */ +extern void udi_channel_event_complete( + udi_channel_event_cb_t *cb, udi_status_t status + ); + + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/udi/init.h b/KernelLand/Modules/Interfaces/UDI/include/udi/init.h new file mode 100644 index 00000000..a6d5bb48 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/udi/init.h @@ -0,0 +1,315 @@ +/** + * \file udi_init.h + */ +#ifndef _UDI_INIT_H_ +#define _UDI_INIT_H_ + +typedef struct udi_init_s udi_init_t; +typedef struct udi_primary_init_s udi_primary_init_t; +typedef struct udi_secondary_init_s udi_secondary_init_t; +typedef struct udi_ops_init_s udi_ops_init_t; +typedef struct udi_cb_init_s udi_cb_init_t; +typedef struct udi_cb_select_s udi_cb_select_t; +typedef struct udi_gcb_init_s udi_gcb_init_t; + +typedef struct udi_init_context_s udi_init_context_t; +typedef struct udi_limits_s udi_limits_t; +typedef struct udi_chan_context_s udi_chan_context_t; +typedef struct udi_child_chan_context_s udi_child_chan_context_t; + +typedef void udi_op_t(void); +typedef udi_op_t * const udi_ops_vector_t; + +/** + * \brief UDI Initialisation Structure + * + * Defines how to initialise and use a UDI driver + */ +struct udi_init_s +{ + /** + * \brief Defines the primary region + * \note For secondary modules this must be NULL + */ + udi_primary_init_t *primary_init_info; + + /** + * \brief Defines all secondary regions + * Pointer to a list (so, essentially an array) of ::udi_secondary_init_t + * It is terminated by an entry with ::udi_secondary_init_t.region_idx + * set to zero. + * \note If NULL, it is to be treated as an empty list + */ + udi_secondary_init_t *secondary_init_list; + + /** + * \brief Channel operations + * Pointer to a ::udi_ops_init_t.ops_idx == 0 terminated list that + * defines the channel opterations usage for each ops vector implemented + * in this module. + * \note Must contain at least one entry for each metalanguage used + */ + udi_ops_init_t *ops_init_list; + + /** + * \brief Control Blocks + */ + udi_cb_init_t *cb_init_list; + + /** + * \brief Generic Control Blocks + */ + udi_gcb_init_t *gcb_init_list; + + /** + * \brief Overrides for control blocks + * Allows a control block to override the ammount of scratch space it + * gets for a specific ops vector. + */ + udi_cb_select_t *cb_select_list; +}; + + +/** + * \name Flags for ::udi_primary_init_t.mgmt_op_flags + * \{ + */ + +/** + * \brief Tells the environment that this operation may take some time + * Used as a hint in scheduling tasks + */ +#define UDI_OP_LONG_EXEC 0x01 + +/** + * \} + */ + +/** + * \brief Describes the Primary Region + * Tells the environment how to set up the driver's primary region. + */ +struct udi_primary_init_s +{ + /** + * \brief Management Ops Vector + * Pointer to a list of functions for the Management Metalanguage + */ + udi_mgmt_ops_t *mgmt_ops; + + /** + * \brief Flags for \a mgmt_ops + * Each entry in \a mgmt_ops is acommanied by an entry in this array. + * Each entry contains the flags that apply to the specified ops vector. + * \see UDI_OP_LONG_EXEC + */ + const udi_ubit8_t *mgmt_op_flags; + + /** + * \brief Scratch space size + * Specifies the number of bytes to allocate for each control block + * passed by the environment. + * \note must not exceed ::UDI_MAX_SCRATCH + */ + udi_size_t mgmt_scratch_requirement; + + /** + * \todo What is this? + */ + udi_ubit8_t enumeration_attr_list_length; + + /** + * \brief Size in bytes to allocate to each instance of the primary + * region + * Essentially the size of the driver's instance state + * \note Must be at least sizeof(udi_init_context_t) and not more + * than UDI_MIN_ALLOC_LIMIT + */ + udi_size_t rdata_size; + + /** + * \brief Size in bytes to allocate for each call to ::udi_enumerate_req + * \note Must not exceed UDI_MIN_ALLOC_LIMIT + */ + udi_size_t child_data_size; + + /** + * \brief Number of path handles for each parent bound to this driver + * \todo What the hell are path handles? + */ + udi_ubit8_t per_parent_paths; +}; + +/** + * \brief Tells the environment how to create a secondary region + */ +struct udi_secondary_init_s +{ + /** + * \brief Region Index + * Non-zero driver-dependent index value that identifies the region + * \note This corresponds to a "region" declaration in the udiprops.txt + * file. + */ + udi_index_t region_idx; + /** + * \brief Number of bytes to allocate + * + * \note Again, must be between sizeof(udi_init_context_t) and + * UDI_MIN_ALLOC_LIMIT + */ + udi_size_t rdata_size; +}; + +/** + * \brief Defines channel endpoints (ways of communicating with the driver) + * + */ +struct udi_ops_init_s +{ + /** + * \brief ops index number + * Used to uniquely this entry + * \note If this is zero, it marks the end of the list + */ + udi_index_t ops_idx; + /** + * \brief Metalanguage Index + * Defines what metalanguage is used + */ + udi_index_t meta_idx; + /** + * \brief Metalanguage Operation + * Defines what metalanguage operation is used + */ + udi_index_t meta_ops_num; + /** + * \brief Size of the context area + * \note If non-zero, must be at least + */ + udi_size_t chan_context_size; + /** + * \brief Pointer to the operations + * Pointer to a <>_<>_ops_t structure + */ + udi_ops_vector_t *ops_vector; + /** + * \brief Flags for each entry in \a ops_vector + */ + //const udi_ubit8_t *op_flags; +}; + +/** + * \brief Defines control blocks + * Much the same as ::udi_ops_init_t + */ +struct udi_cb_init_s +{ + udi_index_t cb_idx; + udi_index_t meta_idx; + udi_index_t meta_cb_num; + udi_size_t scratch_requirement; + /** + * \brief Size of inline memory + */ + udi_size_t inline_size; + /** + * \brief Layout of inline memory + */ + udi_layout_t *inline_layout; +}; + +/** + * \brief Overrides the scratch size for an operation + */ +struct udi_cb_select_s +{ + udi_index_t ops_idx; + udi_index_t cb_idx; +}; + +/** + * \brief General Control Blocks + * These control blocks can only be used as general data storage, not + * for any channel operations. + */ +struct udi_gcb_init_s +{ + udi_index_t cb_idx; + udi_size_t scratch_requirement; +}; + + +// === +// === +/** + * \brief Environement Imposed Limits + */ +struct udi_limits_s +{ + /** + * \brief Maximum legal ammount of memory that can be allocated + */ + udi_size_t max_legal_alloc; + + /** + * \brief Maximum ammount of guaranteed memory + */ + udi_size_t max_safe_alloc; + /** + * \brief Maximum size of the final string from ::udi_trace_write + * or ::udi_log_write + */ + udi_size_t max_trace_log_formatted_len; + /** + * \brief Maximum legal size of an instanct attribute value + */ + udi_size_t max_instance_attr_len; + /** + * \brief Minumum time difference (in nanoseconds between unique values + * returned by ::udi_time_current + */ + udi_ubit32_t min_curtime_res; + /** + * \brief Minimum resolution of timers + * \see ::udi_timer_start_repeating, ::udi_timer_start + */ + udi_ubit32_t min_timer_res; +} PACKED; + +/** + * \brief Primary Region Context data + */ +struct udi_init_context_s +{ + udi_index_t region_idx; + udi_limits_t limits; +}; + +/** + * \brief Channel context data + */ +struct udi_chan_context_s +{ + /** + * \brief Pointer to the driver instance's initial region data + */ + void *rdata; +} PACKED; + +/** + * \brief Child Channel context + */ +struct udi_child_chan_context_s +{ + /** + * \brief Pointer to the driver instance's initial region data + */ + void *rdata; + /** + * \brief Some sort of unique ID number + */ + udi_ubit32_t child_ID; +}; + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/udi/log.h b/KernelLand/Modules/Interfaces/UDI/include/udi/log.h new file mode 100644 index 00000000..dccb1246 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/udi/log.h @@ -0,0 +1,31 @@ +/** + * \file udi_log.h + */ +#ifndef _UDI_LOG_H_ +#define _UDI_LOG_H_ + +/** + * \brief Trace Event + */ +typedef udi_ubit32_t udi_trevent_t; + +/** + * \brief Log Callback + */ +typedef void udi_log_write_call_t(udi_cb_t *gcb, udi_status_t correlated_status); + +/** + * \name Log Severities + * \brief Values for severity + * \{ + */ +#define UDI_LOG_DISASTER 1 +#define UDI_LOG_ERROR 2 +#define UDI_LOG_WARNING 3 +#define UDI_LOG_INFORMATION 4 +/** + * \} + */ + + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/udi/mem.h b/KernelLand/Modules/Interfaces/UDI/include/udi/mem.h new file mode 100644 index 00000000..d29f25ed --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/udi/mem.h @@ -0,0 +1,38 @@ +/** + * \file udi_mem.h + */ +#ifndef _UDI_MEM_H_ +#define _UDI_MEM_H_ + +/** + * \brief Callback type for ::udi_mem_alloc + */ +typedef void udi_mem_alloc_call_t(udi_cb_t *gcb, void *new_mem); + +/** + * \brief Allocate memory + */ +extern void udi_mem_alloc( + udi_mem_alloc_call_t *callback, + udi_cb_t *gcb, + udi_size_t size, + udi_ubit8_t flags + ); + +/** + * \brief Values for ::udi_mem_alloc \a flags + * \{ + */ +#define UDI_MEM_NOZERO (1U<<0) //!< No need to zero the memory +#define UDI_MEM_MOVABLE (1U<<1) //!< Globally accessable memory? +/** + * \} + */ + +/** + * \brief Free allocated memory + */ +extern void udi_mem_free(void *target_mem); + + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/udi/meta_gio.h b/KernelLand/Modules/Interfaces/UDI/include/udi/meta_gio.h new file mode 100644 index 00000000..d1cf86fd --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/udi/meta_gio.h @@ -0,0 +1,120 @@ +/** + * \file udi_meta_gio.h + */ +#ifndef _UDI_META_GIO_H_ +#define _UDI_META_GIO_H_ + +typedef const struct udi_gio_provider_ops_s udi_gio_provider_ops_t; +typedef const struct udi_gio_client_ops_s udi_gio_client_ops_t; +typedef struct udi_gio_bind_cb_s udi_gio_bind_cb_t; +typedef struct udi_gio_xfer_cb_s udi_gio_xfer_cb_t; +typedef struct udi_gio_rw_params_s udi_gio_rw_params_t; +typedef struct udi_gio_event_cb_s udi_gio_event_cb_t; + +typedef void udi_gio_bind_req_op_t(udi_gio_bind_cb_t *cb); +typedef void udi_gio_unbind_req_op_t(udi_gio_bind_cb_t *cb); +typedef void udi_gio_xfer_req_op_t(udi_gio_bind_cb_t *cb); +typedef void udi_gio_event_res_op_t(udi_gio_bind_cb_t *cb); + +typedef void udi_gio_bind_ack_op_t( + udi_gio_bind_cb_t *cb, + udi_ubit32_t device_size_lo, + udi_ubit32_t device_size_hi, + udi_status_t status + ); +typedef void udi_gio_unbind_ack_op_t(udi_gio_bind_cb_t *cb); +typedef void udi_gio_xfer_ack_op_t(udi_gio_bind_cb_t *cb); +typedef void udi_gio_xfer_nak_op_t(udi_gio_bind_cb_t *cb, udi_status_t status); +typedef void udi_gio_event_ind_op_t(udi_gio_bind_cb_t *cb); + +typedef udi_ubit8_t udi_gio_op_t; +/* Limit values for udi_gio_op_t */ +#define UDI_GIO_OP_CUSTOM 16 +#define UDI_GIO_OP_MAX 64 +/* Direction flag values for op */ +#define UDI_GIO_DIR_READ (1U<<6) +#define UDI_GIO_DIR_WRITE (1U<<7) +/* Standard Operation Codes */ +#define UDI_GIO_OP_READ UDI_GIO_DIR_READ +#define UDI_GIO_OP_WRITE UDI_GIO_DIR_WRITE + + + +struct udi_gio_provider_ops_s +{ + udi_channel_event_ind_op_t *channel_event_ind_op; + udi_gio_bind_req_op_t *gio_bind_req_op; + udi_gio_unbind_req_op_t *gio_unbind_req_op; + udi_gio_xfer_req_op_t *gio_xfer_req_op; + udi_gio_event_res_op_t *gio_event_res_op; +}; +/* Ops Vector Number */ +#define UDI_GIO_PROVIDER_OPS_NUM 1 + +struct udi_gio_client_ops_s +{ + udi_channel_event_ind_op_t *channel_event_ind_op; + udi_gio_bind_ack_op_t *gio_bind_ack_op; + udi_gio_unbind_ack_op_t *gio_unbind_ack_op; + udi_gio_xfer_ack_op_t *gio_xfer_ack_op; + udi_gio_xfer_nak_op_t *gio_xfer_nak_op; + udi_gio_event_ind_op_t *gio_event_ind_op; +}; +/* Ops Vector Number */ +#define UDI_GIO_CLIENT_OPS_NUM 2 + +struct udi_gio_bind_cb_s +{ + udi_cb_t gcb; + udi_xfer_constraints_t xfer_constraints; +}; +/* Control Block Group Number */ +#define UDI_GIO_BIND_CB_NUM 1 + + +struct udi_gio_xfer_cb_s +{ + udi_cb_t gcb; + udi_gio_op_t op; + void *tr_params; + udi_buf_t *data_buf; +}; +/* Control Block Group Number */ +#define UDI_GIO_XFER_CB_NUM 2 + +struct udi_gio_rw_params_s +{ + udi_ubit32_t offset_lo; + udi_ubit32_t offset_hi; +}; + +struct udi_gio_event_cb_s +{ + udi_cb_t gcb; + udi_ubit8_t event_code; + void *event_params; +}; +/* Control Block Group Number */ +#define UDI_GIO_EVENT_CB_NUM 3 + + +extern void udi_gio_bind_req(udi_gio_bind_cb_t *cb); +extern void udi_gio_bind_ack( + udi_gio_bind_cb_t *cb, + udi_ubit32_t device_size_lo, + udi_ubit32_t device_size_hi, + udi_status_t status + ); + +extern void udi_gio_unbind_req(udi_gio_bind_cb_t *cb); +extern void udi_gio_unbind_ack(udi_gio_bind_cb_t *cb); + +extern void udi_gio_xfer_req(udi_gio_xfer_cb_t *cb); +extern void udi_gio_xfer_ack(udi_gio_xfer_cb_t *cb); +extern void udi_gio_xfer_nak(udi_gio_xfer_cb_t *cb, udi_status_t status); + +extern void udi_gio_event_res(udi_gio_event_cb_t *cb); +extern void udi_gio_event_ind(udi_gio_event_cb_t *cb); +extern void udi_gio_event_res_unused(udi_gio_event_cb_t *cb); + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/udi/meta_mgmt.h b/KernelLand/Modules/Interfaces/UDI/include/udi/meta_mgmt.h new file mode 100644 index 00000000..97eccf2d --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/udi/meta_mgmt.h @@ -0,0 +1,156 @@ +/** + * \file udi_meta_mgmt.h + */ +#ifndef _UDI_META_MGMT_H_ +#define _UDI_META_MGMT_H_ + +typedef struct udi_mgmt_ops_s udi_mgmt_ops_t; +typedef struct udi_mgmt_cb_s udi_mgmt_cb_t; +typedef struct udi_usage_cb_s udi_usage_cb_t; +typedef struct udi_filter_element_s udi_filter_element_t; +typedef struct udi_enumerate_cb_s udi_enumerate_cb_t; + +/** + * \name Specify Usage + * \{ + */ +typedef void udi_usage_ind_op_t(udi_usage_cb_t *cb, udi_ubit8_t resource_level); +/* Values for resource_level */ +#define UDI_RESOURCES_CRITICAL 1 +#define UDI_RESOURCES_LOW 2 +#define UDI_RESOURCES_NORMAL 3 +#define UDI_RESOURCES_PLENTIFUL 4 +/* Proxy */ +extern void udi_static_usage(udi_usage_cb_t *cb, udi_ubit8_t resource_level); +/** + * \} + */ + +typedef void udi_usage_res_op_t(udi_usage_cb_t *cb); + +/** + * \name Enumerate this driver + * \{ + */ +typedef void udi_enumerate_req_op_t(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level); +/* Values for enumeration_level */ +#define UDI_ENUMERATE_START 1 +#define UDI_ENUMERATE_START_RESCAN 2 +#define UDI_ENUMERATE_NEXT 3 +#define UDI_ENUMERATE_NEW 4 +#define UDI_ENUMERATE_DIRECTED 5 +#define UDI_ENUMERATE_RELEASE 6 +/* Proxy */ +extern void udi_enumerate_no_children(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level); +/** + * \} + */ + +/** + * \name Enumeration Acknowlagement + * \{ + */ +typedef void udi_enumerate_ack_op_t(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_result, udi_index_t ops_idx); +/* Values for enumeration_result */ +#define UDI_ENUMERATE_OK 0 +#define UDI_ENUMERATE_LEAF 1 +#define UDI_ENUMERATE_DONE 2 +#define UDI_ENUMERATE_RESCAN 3 +#define UDI_ENUMERATE_REMOVED 4 +#define UDI_ENUMERATE_REMOVED_SELF 5 +#define UDI_ENUMERATE_RELEASED 6 +#define UDI_ENUMERATE_FAILED 255 +/** + * \} + */ + +/** + * \name + * \{ + */ +typedef void udi_devmgmt_req_op_t(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID); + +typedef void udi_devmgmt_ack_op_t(udi_mgmt_cb_t *cb, udi_ubit8_t flags, udi_status_t status); +/** + * \} + */ +typedef void udi_final_cleanup_req_op_t(udi_mgmt_cb_t *cb); +typedef void udi_final_cleanup_ack_op_t(udi_mgmt_cb_t *cb); + + + + + +struct udi_mgmt_ops_s +{ + udi_usage_ind_op_t *usage_ind_op; + udi_enumerate_req_op_t *enumerate_req_op; + udi_devmgmt_req_op_t *devmgmt_req_op; + udi_final_cleanup_req_op_t *final_cleanup_req_op; +}; + +struct udi_mgmt_cb_s +{ + udi_cb_t gcb; +}; + +struct udi_usage_cb_s +{ + udi_cb_t gcb; + udi_trevent_t trace_mask; + udi_index_t meta_idx; +}; + + +struct udi_filter_element_s +{ + char attr_name[UDI_MAX_ATTR_NAMELEN]; + udi_ubit8_t attr_min[UDI_MAX_ATTR_SIZE]; + udi_ubit8_t attr_min_len; + udi_ubit8_t attr_max[UDI_MAX_ATTR_SIZE]; + udi_ubit8_t attr_max_len; + udi_instance_attr_type_t attr_type; + udi_ubit32_t attr_stride; +}; +struct udi_enumerate_cb_s +{ + udi_cb_t gcb; + udi_ubit32_t child_ID; + void *child_data; + udi_instance_attr_list_t *attr_list; + udi_ubit8_t attr_valid_length; + const udi_filter_element_t *filter_list; + udi_ubit8_t filter_list_length; + udi_ubit8_t parent_ID; +}; +/* Special parent_ID filter values */ +#define UDI_ANY_PARENT_ID 0 + +/** + * \brief + */ +extern void udi_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID ); +/** + * \brief Values for ::udi_devmgmt_req \a mgmt_op + */ +enum eDMGMT +{ + UDI_DMGMT_PREPARE_TO_SUSPEND = 1, + UDI_DMGMT_SUSPEND, + UDI_DMGMT_SHUTDOWN, + UDI_DMGMT_PARENT_SUSPENDED, + UDI_DMGMT_RESUME, + UDI_DMGMT_UNBIND +}; + +extern void udi_devmgmt_ack(udi_mgmt_cb_t *cb, udi_ubit8_t flags, udi_status_t status); +//!\brief Values for flags +#define UDI_DMGMT_NONTRANSPARENT (1U<<0) +//!\brief Meta-Specific Status Codes +#define UDI_DMGMT_STAT_ROUTING_CHANGE (UDI_STAT_META_SPECIFIC|1) + +extern void udi_final_cleanup_req(udi_mgmt_cb_t *cb); +extern void udi_final_cleanup_ack(udi_mgmt_cb_t *cb); + + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/udi/strmem.h b/KernelLand/Modules/Interfaces/UDI/include/udi/strmem.h new file mode 100644 index 00000000..f540a3ea --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/udi/strmem.h @@ -0,0 +1,44 @@ +/** + * \file udi_strmem.h + */ +#ifndef _UDI_STRMEM_H_ +#define _UDI_STRMEM_H_ + +/** + * \brief Gets the length of a C style string + */ +extern udi_size_t udi_strlen(const char *s); + +/** + * \brief Appends to a string + */ +extern char *udi_strcat(char *s1, const char *s2); +extern char *udi_strncat(char *s1, const char *s2, udi_size_t n); + +/** + * \brief Compares Strings/Memory + */ +extern udi_sbit8_t udi_strcmp(const char *s1, const char *s2); +extern udi_sbit8_t udi_strncmp(const char *s1, const char *s2, udi_size_t n); +extern udi_sbit8_t udi_memcmp(const void *s1, const void *s2, udi_size_t n); + +extern char *udi_strcpy(char *s1, const char *s2); +extern char *udi_strncpy(char *s1, const char *s2, udi_size_t n); +extern void *udi_memcpy(void *s1, const void *s2, udi_size_t n); +extern void *udi_memmove(void *s1, const void *s2, udi_size_t n); + +extern char *udi_strncpy_rtrim(char *s1, const char *s2, udi_size_t n); + +extern char *udi_strchr(const char *s, char c); +extern char *udi_strrchr(const char *s, char c); +extern void *udi_memchr (const void *s, udi_ubit8_t c, udi_size_t n); + +extern void *udi_memset(void *s, udi_ubit8_t c, udi_size_t n); +extern udi_ubit32_t udi_strtou32(const char *s, char **endptr, int base); + + +extern udi_size_t udi_snprintf(char *s, udi_size_t max_bytes, const char *format, ...); + + + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/include/udi_physio.h b/KernelLand/Modules/Interfaces/UDI/include/udi_physio.h new file mode 100644 index 00000000..1397b1c0 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/include/udi_physio.h @@ -0,0 +1,144 @@ +/** + * \file udi_physio.h + */ +#ifndef _UDI_PHYSIO_H_ +#define _UDI_PHYSIO_H_ + +#include + +// === TYPEDEFS === +// DMA Core +typedef _udi_handle_t udi_dma_handle_t; +#define UDI_NULL_DMA_HANDLE _NULL_HANDLE +typedef Uint64 udi_busaddr64_t; //!< \note Opaque +typedef struct udi_scgth_element_32_s udi_scgth_element_32_t; +typedef struct udi_scgth_element_64_s udi_scgth_element_64_t; +typedef struct udi_scgth_s udi_scgth_t; +typedef _udi_handle_t udi_dma_constraints_t; +#define UDI_NULL_DMA_CONSTRAINTS _NULL_HANDLE +/** + * \name DMA constraints attributes + * \{ + */ +typedef udi_ubit8_t udi_dma_constraints_attr_t; +/* DMA Convenience Attribute Codes */ +#define UDI_DMA_ADDRESSABLE_BITS 100 +#define UDI_DMA_ALIGNMENT_BITS 101 +/* DMA Constraints on the Entire Transfer */ +#define UDI_DMA_DATA_ADDRESSABLE_BITS 110 +#define UDI_DMA_NO_PARTIAL 111 +/* DMA Constraints on the Scatter/Gather List */ +#define UDI_DMA_SCGTH_MAX_ELEMENTS 120 +#define UDI_DMA_SCGTH_FORMAT 121 +#define UDI_DMA_SCGTH_ENDIANNESS 122 +#define UDI_DMA_SCGTH_ADDRESSABLE_BITS 123 +#define UDI_DMA_SCGTH_MAX_SEGMENTS 124 +/* DMA Constraints on Scatter/Gather Segments */ +#define UDI_DMA_SCGTH_ALIGNMENT_BITS 130 +#define UDI_DMA_SCGTH_MAX_EL_PER_SEG 131 +#define UDI_DMA_SCGTH_PREFIX_BYTES 132 +/* DMA Constraints on Scatter/Gather Elements */ +#define UDI_DMA_ELEMENT_ALIGNMENT_BITS 140 +#define UDI_DMA_ELEMENT_LENGTH_BITS 141 +#define UDI_DMA_ELEMENT_GRANULARITY_BITS 142 +/* DMA Constraints for Special Addressing */ +#define UDI_DMA_ADDR_FIXED_BITS 150 +#define UDI_DMA_ADDR_FIXED_TYPE 151 +#define UDI_DMA_ADDR_FIXED_VALUE_LO 152 +#define UDI_DMA_ADDR_FIXED_VALUE_HI 153 +/* DMA Constraints on DMA Access Behavior */ +#define UDI_DMA_SEQUENTIAL 160 +#define UDI_DMA_SLOP_IN_BITS 161 +#define UDI_DMA_SLOP_OUT_BITS 162 +#define UDI_DMA_SLOP_OUT_EXTRA 163 +#define UDI_DMA_SLOP_BARRIER_BITS 164 +/* Values for UDI_DMA_SCGTH_ENDIANNESS */ +#define UDI_DMA_LITTLE_ENDIAN (1U<<6) +#define UDI_DMA_BIG_ENDIAN (1U<<5) +/* Values for UDI_DMA_ADDR_FIXED_TYPE */ +#define UDI_DMA_FIXED_ELEMENT 1 +/** + * \} + */ +// DMA Constraints Management +typedef struct udi_dma_constraints_attr_spec_s udi_dma_constraints_attr_spec_t; +typedef void udi_dma_constraints_attr_set_call_t( + udi_cb_t *gcb, udi_dma_constraints_t new_constraints, udi_status_t status + ); +typedef struct udi_dma_limits_s udi_dma_limits_t; + + +// === STRUCTURES === +// --- DMA Constraints Management --- +struct udi_dma_constraints_attr_spec_s +{ + udi_dma_constraints_attr_t attr_type; + udi_ubit32_t attr_value; +}; +// --- DMA Core --- +struct udi_dma_limits_s +{ + udi_size_t max_legal_contig_alloc; + udi_size_t max_safe_contig_alloc; + udi_size_t cache_line_size; +}; +struct udi_scgth_element_32_s +{ + udi_ubit32_t block_busaddr; + udi_ubit32_t block_length; +}; +struct udi_scgth_element_64_s +{ + udi_busaddr64_t block_busaddr; + udi_ubit32_t block_length; + udi_ubit32_t el_reserved; +}; +/* Extension Flag */ +#define UDI_SCGTH_EXT 0x80000000 +struct udi_scgth_s +{ + udi_ubit16_t scgth_num_elements; + udi_ubit8_t scgth_format; + udi_boolean_t scgth_must_swap; + union { + udi_scgth_element_32_t *el32p; + udi_scgth_element_64_t *el64p; + } scgth_elements; + union { + udi_scgth_element_32_t el32; + udi_scgth_element_64_t el64; + } scgth_first_segment; +}; +/* Values for scgth_format */ +#define UDI_SCGTH_32 (1U<<0) +#define UDI_SCGTH_64 (1U<<1) +#define UDI_SCGTH_DMA_MAPPED (1U<<6) +#define UDI_SCGTH_DRIVER_MAPPED (1U<<7) + + + +// === FUNCTIONS === +// --- DMA Constraints Management --- +extern void udi_dma_constraints_attr_set( + udi_dma_constraints_attr_set_call_t *callback, + udi_cb_t *gcb, + udi_dma_constraints_t src_constraints, + const udi_dma_constraints_attr_spec_t *attr_list, + udi_ubit16_t list_length, + udi_ubit8_t flags + ); +/* Constraints Flags */ +#define UDI_DMA_CONSTRAINTS_COPY (1U<<0) + +extern void udi_dma_constraints_attr_reset( + udi_dma_constraints_t constraints, + udi_dma_constraints_attr_t attr_type + ); + +extern void udi_dma_constraints_free(udi_dma_constraints_t constraints); + +#include +#include + + +#endif diff --git a/KernelLand/Modules/Interfaces/UDI/logging.c b/KernelLand/Modules/Interfaces/UDI/logging.c new file mode 100644 index 00000000..2e58e373 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/logging.c @@ -0,0 +1,18 @@ +/** + * \file logging.c + * \author John Hodge (thePowersGang) + */ +#include +#include + +// === PROTOTYPES === + +// === CODE === +void udi_log_write( udi_log_write_call_t *callback, udi_cb_t *gcb, + udi_trevent_t trace_event, udi_ubit8_t severity, udi_index_t meta_idx, + udi_status_t original_status, udi_ubit32_t msgnum, ... ) +{ + Log("UDI Log"); +} + +EXPORT(udi_log_write); diff --git a/KernelLand/Modules/Interfaces/UDI/main.c b/KernelLand/Modules/Interfaces/UDI/main.c new file mode 100644 index 00000000..a4a1504a --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/main.c @@ -0,0 +1,145 @@ +/* + * Acess2 UDI Layer + */ +#define DEBUG 0 +#define VERSION ((0<<8)|1) +#include +#include +#include + +// === PROTOTYPES === + int UDI_Install(char **Arguments); + int UDI_DetectDriver(void *Base); + int UDI_LoadDriver(void *Base); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, UDI, UDI_Install, NULL, NULL); +tModuleLoader gUDI_Loader = { + NULL, "UDI", UDI_DetectDriver, UDI_LoadDriver, NULL +}; + +// === CODE === +/** + * \fn int UDI_Install(char **Arguments) + * \brief Stub intialisation routine + */ +int UDI_Install(char **Arguments) +{ + Module_RegisterLoader( &gUDI_Loader ); + return MODULE_ERR_OK; +} + +/** + * \brief Detects if a driver should be loaded by the UDI subsystem + */ +int UDI_DetectDriver(void *Base) +{ + if( Binary_FindSymbol(Base, "udi_init_info", NULL) == 0) { + return 0; + } + + return 1; +} + +/** + * \fn int UDI_LoadDriver(void *Base) + */ +int UDI_LoadDriver(void *Base) +{ + udi_init_t *info; + char *udiprops = NULL; + int udiprops_size = 0; + int i; + // int j; + + Log_Debug("UDI", "UDI_LoadDriver: (Base=%p)", Base); + + if( Binary_FindSymbol(Base, "udi_init_info", (Uint*)&info) == 0) { + Binary_Unload(Base); + return 0; + } + + if( Binary_FindSymbol(Base, "_udiprops", (Uint*)&udiprops) == 0 ) { + Log_Warning("UDI", "_udiprops is not defined, this is usually bad"); + } + else { + Uint udiprops_end = 0; + int i, j, nLines; + char **udipropsptrs; + + if( Binary_FindSymbol(Base, "_udiprops_end", (Uint*)&udiprops_end) == 0) + Log_Warning("UDI", "_udiprops_end is not defined"); + Log_Debug("UDI", "udiprops_end = %p", udiprops_end); + udiprops_size = udiprops_end - (Uint)udiprops; + Log_Debug("UDI", "udiprops = %p, udiprops_size = 0x%x", udiprops, udiprops_size); + + Debug_HexDump("UDI_LoadDriver", udiprops, udiprops_size); + + nLines = 1; + for( i = 0; i < udiprops_size; i++ ) + { + if( udiprops[i] == '\0' ) + nLines ++; + } + + Log_Debug("UDI", "nLines = %i", nLines); + + udipropsptrs = malloc( sizeof(char*)*nLines ); + udipropsptrs[0] = udiprops; + j = 0; + for( i = 0; i < udiprops_size; i++ ) + { + if( udiprops[i] == '\0' ) { + //Log_Debug("UDI", "udipropsptrs[%i] = '%s'", j, udipropsptrs[j]); + udipropsptrs[j++] = &udiprops[i+1]; + } + } + Log_Debug("UDI", "udipropsptrs[%i] = '%s'", j, udipropsptrs[j]); + Log_Debug("UDI", "udiprops = \"%s\"", udiprops); + } + + + Log("primary_init_info = %p = {", info->primary_init_info); + { + Log(" .mgmt_ops = %p = {", info->primary_init_info->mgmt_ops); + Log(" .usage_ind_op: %p() - 0x%02x", + info->primary_init_info->mgmt_ops->usage_ind_op, + info->primary_init_info->mgmt_op_flags[0] + ); + Log(" .enumerate_req_op: %p() - 0x%02x", + info->primary_init_info->mgmt_ops->enumerate_req_op, + info->primary_init_info->mgmt_op_flags[1] + ); + Log(" .devmgmt_req_op: %p() - 0x%02x", + info->primary_init_info->mgmt_ops->devmgmt_req_op, + info->primary_init_info->mgmt_op_flags[2] + ); + Log(" .final_cleanup_req_op: %p() - 0x%02x", + info->primary_init_info->mgmt_ops->final_cleanup_req_op, + info->primary_init_info->mgmt_op_flags[3] + ); + Log(" }"); + Log(" .mgmt_scratch_requirement = 0x%x", info->primary_init_info->mgmt_scratch_requirement); + Log(" .enumeration_attr_list_length = 0x%x", info->primary_init_info->enumeration_attr_list_length); + Log(" .rdata_size = 0x%x", info->primary_init_info->rdata_size); + Log(" .child_data_size = 0x%x", info->primary_init_info->child_data_size); + Log(" .per_parent_paths = 0x%x", info->primary_init_info->per_parent_paths); + } + Log("}"); + Log("secondary_init_list = %p", info->secondary_init_list); + Log("ops_init_list = %p", info->ops_init_list); + + for( i = 0; info->ops_init_list[i].ops_idx; i++ ) + { + Log("info->ops_init_list[%i] = {", i); + Log(" .ops_idx = 0x%x", info->ops_init_list[i].ops_idx); + Log(" .meta_idx = 0x%x", info->ops_init_list[i].meta_idx); + Log(" .meta_ops_num = 0x%x", info->ops_init_list[i].meta_ops_num); + Log(" .chan_context_size = 0x%x", info->ops_init_list[i].chan_context_size); + Log(" .ops_vector = %p", info->ops_init_list[i].ops_vector); +// Log(" .op_flags = %p", info->ops_init_list[i].op_flags); + Log("}"); + } + + return 0; +} diff --git a/KernelLand/Modules/Interfaces/UDI/mem.c b/KernelLand/Modules/Interfaces/UDI/mem.c new file mode 100644 index 00000000..a09d54c5 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/mem.c @@ -0,0 +1,32 @@ +/** + * \file mem.c + * \author John Hodge (thePowersGang) + */ +#include +#include + +// === EXPORTS === +EXPORT(udi_mem_alloc); +EXPORT(udi_mem_free); + +// === CODE === +void udi_mem_alloc( + udi_mem_alloc_call_t *callback, + udi_cb_t *gcb, + udi_size_t size, + udi_ubit8_t flags + ) +{ + void *buf = malloc(size); + if(buf) + { + if( !(flags & UDI_MEM_NOZERO) ) + memset(buf, 0, size); + } + callback(gcb, buf); +} + +void udi_mem_free(void *target_mem) +{ + free(target_mem); +} diff --git a/KernelLand/Modules/Interfaces/UDI/meta_gio.c b/KernelLand/Modules/Interfaces/UDI/meta_gio.c new file mode 100644 index 00000000..1618c27f --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/meta_gio.c @@ -0,0 +1,68 @@ +/** + * \file meta_gio.c + * \author John Hodge (thePowersGang) + */ +#include +#include + +// === EXPORTS === +EXPORT(udi_gio_bind_req); +EXPORT(udi_gio_bind_ack); +EXPORT(udi_gio_unbind_req); +EXPORT(udi_gio_unbind_ack); +EXPORT(udi_gio_xfer_req); +EXPORT(udi_gio_xfer_ack); +EXPORT(udi_gio_xfer_nak); +EXPORT(udi_gio_event_res); +EXPORT(udi_gio_event_ind); +EXPORT(udi_gio_event_res_unused); + +// === CODE === +void udi_gio_bind_req(udi_gio_bind_cb_t *cb) +{ + UNIMPLEMENTED(); +} +void udi_gio_bind_ack( + udi_gio_bind_cb_t *cb, + udi_ubit32_t device_size_lo, + udi_ubit32_t device_size_hi, + udi_status_t status + ) +{ + UNIMPLEMENTED(); +} + +void udi_gio_unbind_req(udi_gio_bind_cb_t *cb) +{ + UNIMPLEMENTED(); +} +void udi_gio_unbind_ack(udi_gio_bind_cb_t *cb) +{ + UNIMPLEMENTED(); +} + +void udi_gio_xfer_req(udi_gio_xfer_cb_t *cb) +{ + UNIMPLEMENTED(); +} +void udi_gio_xfer_ack(udi_gio_xfer_cb_t *cb) +{ + UNIMPLEMENTED(); +} +void udi_gio_xfer_nak(udi_gio_xfer_cb_t *cb, udi_status_t status) +{ + UNIMPLEMENTED(); +} + +void udi_gio_event_res(udi_gio_event_cb_t *cb) +{ + UNIMPLEMENTED(); +} +void udi_gio_event_ind(udi_gio_event_cb_t *cb) +{ + UNIMPLEMENTED(); +} +void udi_gio_event_res_unused(udi_gio_event_cb_t *cb) +{ + UNIMPLEMENTED(); +} diff --git a/KernelLand/Modules/Interfaces/UDI/meta_mgmt.c b/KernelLand/Modules/Interfaces/UDI/meta_mgmt.c new file mode 100644 index 00000000..45a02c46 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/meta_mgmt.c @@ -0,0 +1,49 @@ +/** + * \file meta_mgmt.c + * \author John Hodge (thePowersGang) + */ +#include +#include + +// === EXPORTS === +EXPORT(udi_devmgmt_req); +EXPORT(udi_devmgmt_ack); +EXPORT(udi_final_cleanup_req); +EXPORT(udi_final_cleanup_ack); +EXPORT(udi_static_usage); +EXPORT(udi_enumerate_no_children); + +// === CODE === +void udi_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID ) +{ + ENTER("pcb imgmt_op iparent_ID", cb, mgmt_op, parent_ID); + LEAVE('-'); +} + +void udi_devmgmt_ack(udi_mgmt_cb_t *cb, udi_ubit8_t flags, udi_status_t status) +{ + ENTER("pcb xflags istatus", cb, flags, status); + LEAVE('-'); +} + +void udi_final_cleanup_req(udi_mgmt_cb_t *cb) +{ + ENTER("pcb", cb); + LEAVE('-'); +} + +void udi_final_cleanup_ack(udi_mgmt_cb_t *cb) +{ + ENTER("pcb", cb); + LEAVE('-'); +} + +void udi_static_usage(udi_usage_cb_t *cb, udi_ubit8_t resource_level) +{ + UNIMPLEMENTED(); +} + +void udi_enumerate_no_children(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level) +{ + UNIMPLEMENTED(); +} diff --git a/KernelLand/Modules/Interfaces/UDI/physio.c b/KernelLand/Modules/Interfaces/UDI/physio.c new file mode 100644 index 00000000..881c9f40 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/physio.c @@ -0,0 +1,25 @@ +/** + * \file physio.c + * \author John Hodge (thePowersGang) + */ +#include +#include +#include + +// === EXPORTS === +EXPORT(udi_dma_constraints_attr_reset); +EXPORT(udi_dma_constraints_free); + +// === CODE === +void udi_dma_constraints_attr_reset( + udi_dma_constraints_t constraints, + udi_dma_constraints_attr_t attr_type + ) +{ + UNIMPLEMENTED(); +} + +void udi_dma_constraints_free(udi_dma_constraints_t constraints) +{ + UNIMPLEMENTED(); +} diff --git a/KernelLand/Modules/Interfaces/UDI/physio/meta_bus.c b/KernelLand/Modules/Interfaces/UDI/physio/meta_bus.c new file mode 100644 index 00000000..e1e6a47d --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/physio/meta_bus.c @@ -0,0 +1,38 @@ +/** + * \file physio/meta_bus.c + * \author John Hodge (thePowersGang) + */ +#include +#include +#include + +// === EXPORTS === +EXPORT(udi_bus_unbind_req); +EXPORT(udi_bus_unbind_ack); +EXPORT(udi_bus_bind_req); +EXPORT(udi_bus_bind_ack); + +// === CODE === +void udi_bus_unbind_req(udi_bus_bind_cb_t *cb) +{ + UNIMPLEMENTED(); +} +void udi_bus_unbind_ack(udi_bus_bind_cb_t *cb) +{ + UNIMPLEMENTED(); +} + +void udi_bus_bind_req(udi_bus_bind_cb_t *cb) +{ + UNIMPLEMENTED(); +} + +void udi_bus_bind_ack( + udi_bus_bind_cb_t *cb, + udi_dma_constraints_t dma_constraints, + udi_ubit8_t preferred_endianness, + udi_status_t status + ) +{ + UNIMPLEMENTED(); +} diff --git a/KernelLand/Modules/Interfaces/UDI/physio/meta_intr.c b/KernelLand/Modules/Interfaces/UDI/physio/meta_intr.c new file mode 100644 index 00000000..f4f50961 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/physio/meta_intr.c @@ -0,0 +1,48 @@ +/** + * \file physio/meta_intr.c + * \author John Hodge (thePowersGang) + */ +#include +#include +#include + +// === EXPORTS === +EXPORT(udi_intr_attach_req); +EXPORT(udi_intr_attach_ack); +EXPORT(udi_intr_attach_ack_unused); +EXPORT(udi_intr_detach_req); +EXPORT(udi_intr_detach_ack); +EXPORT(udi_intr_detach_ack_unused); +EXPORT(udi_intr_event_ind); + +// === CODE === +void udi_intr_attach_req(udi_intr_attach_cb_t *intr_attach_cb) +{ + UNIMPLEMENTED(); +} +void udi_intr_attach_ack(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status) +{ + UNIMPLEMENTED(); +} +void udi_intr_attach_ack_unused(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status) +{ + UNIMPLEMENTED(); +} + +void udi_intr_detach_req(udi_intr_detach_cb_t *intr_detach_cb) +{ + UNIMPLEMENTED(); +} +void udi_intr_detach_ack(udi_intr_detach_cb_t *intr_detach_cb) +{ + UNIMPLEMENTED(); +} +void udi_intr_detach_ack_unused(udi_intr_detach_cb_t *intr_detach_cb) +{ + UNIMPLEMENTED(); +} + +void udi_intr_event_ind(udi_intr_event_cb_t *intr_event_cb, udi_ubit8_t flags) +{ + UNIMPLEMENTED(); +} diff --git a/KernelLand/Modules/Interfaces/UDI/physio_main.c b/KernelLand/Modules/Interfaces/UDI/physio_main.c new file mode 100644 index 00000000..f5e7aa04 --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/physio_main.c @@ -0,0 +1,16 @@ +/** + * \file logging.c + * \author John Hodge (thePowersGang) + */ +#include +#include +#include + +// === CODE === +void udi_bus_bind_req(udi_bus_bind_cb_t *cb) +{ +} + +void udi_bus_unbind_req(udi_bus_bind_cb_t *cb) +{ +} diff --git a/KernelLand/Modules/Interfaces/UDI/strmem.c b/KernelLand/Modules/Interfaces/UDI/strmem.c new file mode 100644 index 00000000..53cc933f --- /dev/null +++ b/KernelLand/Modules/Interfaces/UDI/strmem.c @@ -0,0 +1,22 @@ +/** + * \file strmem.c + * \author John Hodge (thePowersGang) + */ +#include +#include + +// === EXPORTS === +EXPORT(udi_snprintf); + +// === CODE === +udi_size_t udi_snprintf(char *s, udi_size_t max_bytes, const char *format, ...) +{ + udi_size_t ret; + va_list args; + va_start(args, format); + + ret = vsnprintf(s, max_bytes, format, args); + + va_end(args); + return ret; +} diff --git a/KernelLand/Modules/Libraries/SunRPC/proto.h b/KernelLand/Modules/Libraries/SunRPC/proto.h new file mode 100644 index 00000000..150fc426 --- /dev/null +++ b/KernelLand/Modules/Libraries/SunRPC/proto.h @@ -0,0 +1,28 @@ +/* + * Acess2 Kernel + * - Sun RPC (RFC 1057) implementation + * proto.h + * - Protocol definition + */ +#ifndef _SUNRPC_PROTO_H_ +#define _SUNRPC_PROTO_H_ + +struct sRPC_CallBody +{ + Uint32 RPCVers; //!< Version (2) + Uint32 Program; //!< Program Identifier + Uint32 Version; //!< Program Version + +}; + +struct sRPC_Message +{ + tNet32 XID; //!< Transaction Identifier + union + { + struct sRPC_CallBody Call; + }; +}; + +#endif + diff --git a/KernelLand/Modules/Makefile.tpl b/KernelLand/Modules/Makefile.tpl new file mode 100644 index 00000000..f1c31a57 --- /dev/null +++ b/KernelLand/Modules/Makefile.tpl @@ -0,0 +1,75 @@ + +# Acess2 Module/Driver Templater Makefile +# Makefile.tpl + +_CPPFLAGS := $(CPPFLAGS) + +-include $(dir $(lastword $(MAKEFILE_LIST)))../Makefile.cfg + +LIBINCLUDES := $(addprefix -I$(ACESSDIR)/Modules/,$(DEPS)) +LIBINCLUDES := $(addsuffix /include,$(LIBINCLUDES)) + +CPPFLAGS := -I$(ACESSDIR)/Kernel/include -I$(ACESSDIR)/Kernel/arch/$(ARCHDIR)/include +CPPFLAGS += -DARCH=$(ARCH) -DARCH_is_$(ARCH) -DARCHDIR_is_$(ARCHDIR) +CPPFLAGS += $(_CPPFLAGS) +CPPFLAGS += $(LIBINCLUDES) +CFLAGS := -std=gnu99 -Wall -fno-stack-protector -g -O3 + +ifneq ($(CATEGORY),) + FULLNAME := $(CATEGORY)_$(NAME) +else + FULLNAME := $(NAME) +endif + +CPPFLAGS += -D_MODULE_NAME_=\"$(FULLNAME)\" + +ifneq ($(BUILDTYPE),static) + _SUFFIX := dyn_$(ARCH) + BIN := ../$(FULLNAME).kmd.$(ARCH) + CFLAGS += $(DYNMOD_CFLAGS) -fPIC +else + _SUFFIX := st_$(ARCH) + CFLAGS += $(KERNEL_CFLAGS) + BIN := ../$(NAME).xo.$(ARCH) +endif + +OBJ := $(addprefix obj-$(_SUFFIX)/,$(OBJ)) +#OBJ := $(addsuffix .$(_SUFFIX),$(OBJ)) + +DEPFILES := $(filter %.o,$(OBJ)) +DEPFILES := $(DEPFILES:%.o=%.d) + +.PHONY: all clean + +all: $(BIN) + +clean: + $(RM) $(BIN) $(BIN).dsm $(KOBJ) $(OBJ) $(DEPFILES) $(EXTRA) + $(RM) -r obj-$(_SUFFIX) + +install: $(BIN) +ifneq ($(BUILDTYPE),static) + @$(xMKDIR) $(DISTROOT)/Modules/$(ARCH); true + $(xCP) $(BIN) $(DISTROOT)/Modules/$(ARCH)/$(NAME).kmd +else +endif + + +ifneq ($(BUILDTYPE),static) +$(BIN): %.kmd.$(ARCH): $(OBJ) + @echo --- $(LD) -o $@ + @$(LD) --allow-shlib-undefined -shared -nostdlib -o $@ $(OBJ) -defsym=DriverInfo=_DriverInfo_$(FULLNAME) $(LDFLAGS) + @$(DISASM) $(BIN) > $(BIN).dsm +else +$(BIN): %.xo.$(ARCH): $(OBJ) + @echo --- $(LD) -o $@ + @$(LD) -r -o $@ $(OBJ) $(LDFLAGS) +endif + +obj-$(_SUFFIX)/%.o: %.c Makefile $(CFGFILES) + @echo --- $(CC) -o $@ + @mkdir -p $(dir $@) + @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + @$(CC) -M $(CPPFLAGS) -MT $@ -o obj-$(_SUFFIX)/$*.d $< + +-include $(DEPFILES) diff --git a/KernelLand/Modules/Network/Makefile.tpl b/KernelLand/Modules/Network/Makefile.tpl new file mode 100644 index 00000000..d5c0b3cc --- /dev/null +++ b/KernelLand/Modules/Network/Makefile.tpl @@ -0,0 +1,3 @@ +CATEGORY = Network + +-include ../../Makefile.tpl diff --git a/KernelLand/Modules/Network/NE2000/Makefile b/KernelLand/Modules/Network/NE2000/Makefile new file mode 100644 index 00000000..7e740227 --- /dev/null +++ b/KernelLand/Modules/Network/NE2000/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = ne2000.o +NAME = NE2000 + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Network/NE2000/ne2000.c b/KernelLand/Modules/Network/NE2000/ne2000.c new file mode 100644 index 00000000..d05b2a33 --- /dev/null +++ b/KernelLand/Modules/Network/NE2000/ne2000.c @@ -0,0 +1,543 @@ +/* Acess2 + * NE2000 Driver + * + * See: ~/Sources/bochs/bochs.../iodev/ne2k.cc + */ +#define DEBUG 1 +#define VERSION ((0<<8)|50) +#include +#include +#include +#include +#include +#include + +// === CONSTANTS === +#define MEM_START 0x40 +#define MEM_END 0xC0 +#define RX_FIRST (MEM_START) +#define RX_LAST (MEM_START+RX_BUF_SIZE-1) +#define RX_BUF_SIZE 0x40 +#define TX_FIRST (MEM_START+RX_BUF_SIZE) +#define TX_LAST (MEM_END) +#define TX_BUF_SIZE 0x40 +#define MAX_PACKET_QUEUE 10 + +static const struct { + Uint16 Vendor; + Uint16 Device; +} csaCOMPAT_DEVICES[] = { + {0x10EC, 0x8029}, // Realtek 8029 + {0x10EC, 0x8129} // Realtek 8129 +}; +#define NUM_COMPAT_DEVICES ((int)(sizeof(csaCOMPAT_DEVICES)/sizeof(csaCOMPAT_DEVICES[0]))) + +enum eNe2k_Page0Read { + CMD = 0, //!< the master command register + CLDA0, //!< Current Local DMA Address 0 + CLDA1, //!< Current Local DMA Address 1 + BNRY, //!< Boundary Pointer (for ringbuffer) + TSR, //!< Transmit Status Register + NCR, //!< collisions counter + FIFO, //!< (for what purpose ??) + ISR, //!< Interrupt Status Register + CRDA0, //!< Current Remote DMA Address 0 + CRDA1, //!< Current Remote DMA Address 1 + RSR = 0xC //!< Receive Status Register +}; + +enum eNe2k_Page0Write { + PSTART = 1, //!< page start (init only) + PSTOP, //!< page stop (init only) + TPSR = 4, //!< transmit page start address + TBCR0, //!< transmit byte count (low) + TBCR1, //!< transmit byte count (high) + RSAR0 = 8, //!< remote start address (lo) + RSAR1, //!< remote start address (hi) + RBCR0, //!< remote byte count (lo) + RBCR1, //!< remote byte count (hi) + RCR, //!< receive config register + TCR, //!< transmit config register + DCR, //!< data config register (init) + IMR //!< interrupt mask register (init) +}; + +enum eNe2k_Page1Read { + CURR = 7 //!< current page +}; + +// === TYPES === +typedef struct sNe2k_Card { + Uint16 IOBase; //!< IO Port Address from PCI + Uint8 IRQ; //!< IRQ Assigned from PCI + + tSemaphore Semaphore; //!< Semaphore for incoming packets + int NextRXPage; //!< Next expected RX page + + int NextMemPage; //!< Next Card Memory page to use + + char Name[2]; // "0" + tVFS_Node Node; //!< VFS Node + Uint8 MacAddr[6]; //!< Cached MAC address +} tCard; + +// === PROTOTYPES === + int Ne2k_Install(char **Arguments); +char *Ne2k_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *Ne2k_FindDir(tVFS_Node *Node, const char *Name); + int Ne2k_IOCtl(tVFS_Node *Node, int ID, void *Data); +Uint64 Ne2k_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); +Uint64 Ne2k_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + + int Ne2k_int_ReadDMA(tCard *Card, int FirstPage, int NumPages, void *Buffer); +Uint8 Ne2k_int_GetWritePage(tCard *Card, Uint16 Length); +void Ne2k_IRQHandler(int IntNum, void *Ptr); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, Ne2k, Ne2k_Install, NULL, NULL); +tVFS_NodeType gNe2K_RootNodeType = { + .ReadDir = Ne2k_ReadDir, + .FindDir = Ne2k_FindDir, + .IOCtl = Ne2k_IOCtl + }; +tVFS_NodeType gNe2K_DevNodeType = { + .Write = Ne2k_Write, + .Read = Ne2k_Read, + .IOCtl = Ne2k_IOCtl + }; +tDevFS_Driver gNe2k_DriverInfo = { + NULL, "ne2k", + { + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Flags = VFS_FFLAG_DIRECTORY, + .Type = &gNe2K_RootNodeType + } +}; +Uint16 gNe2k_BaseAddress; + int giNe2k_CardCount = 0; +tCard *gpNe2k_Cards = NULL; + +// === CODE === +/** + * \fn int Ne2k_Install(char **Options) + * \brief Installs the NE2000 Driver + */ +int Ne2k_Install(char **Options) +{ + int i, j, k; + int count, base; + tPCIDev id; + + // --- Scan PCI Bus --- + // Count Cards + giNe2k_CardCount = 0; + for( i = 0; i < NUM_COMPAT_DEVICES; i ++ ) + { + giNe2k_CardCount += PCI_CountDevices( csaCOMPAT_DEVICES[i].Vendor, csaCOMPAT_DEVICES[i].Device ); + } + + if( giNe2k_CardCount == 0 ) return MODULE_ERR_NOTNEEDED; + + // Enumerate Cards + k = 0; + gpNe2k_Cards = calloc( giNe2k_CardCount, sizeof(tCard) ); + + for( i = 0; i < NUM_COMPAT_DEVICES; i ++ ) + { + count = PCI_CountDevices( csaCOMPAT_DEVICES[i].Vendor, csaCOMPAT_DEVICES[i].Device ); + for( j = 0; j < count; j ++,k ++ ) + { + id = PCI_GetDevice( csaCOMPAT_DEVICES[i].Vendor, csaCOMPAT_DEVICES[i].Device, j ); + // Create Structure + base = PCI_GetBAR( id, 0 ); + gpNe2k_Cards[ k ].IOBase = base; + gpNe2k_Cards[ k ].IRQ = PCI_GetIRQ( id ); + gpNe2k_Cards[ k ].NextMemPage = 64; + gpNe2k_Cards[ k ].NextRXPage = RX_FIRST; + + // Install IRQ Handler + IRQ_AddHandler(gpNe2k_Cards[ k ].IRQ, Ne2k_IRQHandler, &gpNe2k_Cards[k]); + + // Reset Card + outb( base + 0x1F, inb(base + 0x1F) ); + while( (inb( base+ISR ) & 0x80) == 0 ); + outb( base + ISR, 0x80 ); + + // Initialise Card + outb( base + CMD, 0x40|0x21 ); // Page 1, No DMA, Stop + outb( base + CURR, RX_FIRST ); // Current RX page + outb( base + CMD, 0x21 ); // No DMA and Stop + outb( base + DCR, 0x49 ); // Set WORD mode + outb( base + IMR, 0x00 ); // Interrupt Mask Register + outb( base + ISR, 0xFF ); + outb( base + RCR, 0x20 ); // Reciever to Monitor + outb( base + TCR, 0x02 ); // Transmitter OFF (TCR.LB = 1, Internal Loopback) + + // Read MAC Address + outb( base + RBCR0, 6*4 ); // Remote Byte Count + outb( base + RBCR1, 0 ); + outb( base + RSAR0, 0 ); // Clear Source Address + outb( base + RSAR1, 0 ); + outb( base + CMD, 0x0A ); // Remote Read, Start + gpNe2k_Cards[ k ].MacAddr[0] = inb(base+0x10);// inb(base+0x10); + gpNe2k_Cards[ k ].MacAddr[1] = inb(base+0x10);// inb(base+0x10); + gpNe2k_Cards[ k ].MacAddr[2] = inb(base+0x10);// inb(base+0x10); + gpNe2k_Cards[ k ].MacAddr[3] = inb(base+0x10);// inb(base+0x10); + gpNe2k_Cards[ k ].MacAddr[4] = inb(base+0x10);// inb(base+0x10); + gpNe2k_Cards[ k ].MacAddr[5] = inb(base+0x10);// inb(base+0x10); + + outb( base+PSTART, RX_FIRST); // Set Receive Start + outb( base+BNRY, RX_LAST-1); // Set Boundary Page + outb( base+PSTOP, RX_LAST); // Set Stop Page + outb( base+ISR, 0xFF ); // Clear all ints + outb( base+CMD, 0x22 ); // No DMA, Start + outb( base+IMR, 0x3F ); // Set Interupt Mask + outb( base+RCR, 0x0F ); // Set WRAP and allow all packet matches + outb( base+TCR, 0x00 ); // Set Normal Transmitter mode + outb( base+TPSR, 0x40); // Set Transmit Start + + Log_Log("Ne2k", "Card %i 0x%04x IRQ%i %02x:%02x:%02x:%02x:%02x:%02x", + k, base, gpNe2k_Cards[ k ].IRQ, + gpNe2k_Cards[k].MacAddr[0], gpNe2k_Cards[k].MacAddr[1], + gpNe2k_Cards[k].MacAddr[2], gpNe2k_Cards[k].MacAddr[3], + gpNe2k_Cards[k].MacAddr[4], gpNe2k_Cards[k].MacAddr[5] + ); + + // Set VFS Node + gpNe2k_Cards[ k ].Name[0] = '0'+k; + gpNe2k_Cards[ k ].Name[1] = '\0'; + gpNe2k_Cards[ k ].Node.ImplPtr = &gpNe2k_Cards[ k ]; + gpNe2k_Cards[ k ].Node.NumACLs = 0; // Root Only + gpNe2k_Cards[ k ].Node.CTime = now(); + gpNe2k_Cards[ k ].Node.Type = &gNe2K_DevNodeType; + + // Initialise packet semaphore + // - Start at zero, no max + Semaphore_Init( &gpNe2k_Cards[k].Semaphore, 0, 0, "NE2000", gpNe2k_Cards[ k ].Name ); + } + } + + gNe2k_DriverInfo.RootNode.Size = giNe2k_CardCount; + DevFS_AddDevice( &gNe2k_DriverInfo ); + return MODULE_ERR_OK; +} + +/** + * \fn char *Ne2k_ReadDir(tVFS_Node *Node, int Pos) + */ +char *Ne2k_ReadDir(tVFS_Node *Node, int Pos) +{ + char ret[2]; + if(Pos < 0 || Pos >= giNe2k_CardCount) return NULL; + ret[0] = '0'+Pos; + ret[1] = '\0'; + return strdup(ret); +} + +/** + * \fn tVFS_Node *Ne2k_FindDir(tVFS_Node *Node, const char *Name) + */ +tVFS_Node *Ne2k_FindDir(tVFS_Node *Node, const char *Name) +{ + if(Name[0] == '\0' || Name[1] != '\0') return NULL; + + return &gpNe2k_Cards[ Name[0]-'0' ].Node; +} + +static const char *casIOCtls[] = { DRV_IOCTLNAMES, DRV_NETWORK_IOCTLNAMES, NULL }; +/** + * \fn int Ne2k_IOCtl(tVFS_Node *Node, int ID, void *Data) + * \brief IOCtl calls for a network device + */ +int Ne2k_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + ENTER("pNode iID pData", Node, ID, Data); + switch( ID ) + { + BASE_IOCTLS(DRV_TYPE_NETWORK, "NE2000", VERSION, casIOCtls); + } + + // If this is the root, return + if( Node == &gNe2k_DriverInfo.RootNode ) { + LEAVE('i', 0); + return 0; + } + + // Device specific settings + switch( ID ) + { + case NET_IOCTL_GETMAC: + if( !CheckMem(Data, 6) ) { + LEAVE('i', -1); + return -1; + } + memcpy( Data, ((tCard*)Node->ImplPtr)->MacAddr, 6 ); + LEAVE('i', 1); + return 1; + } + LEAVE('i', 0); + return 0; +} + +/** + * \fn Uint64 Ne2k_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) + * \brief Send a packet from the network card + */ +Uint64 Ne2k_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + tCard *Card = (tCard*)Node->ImplPtr; + const Uint16 *buf = Buffer; + int rem = Length; + int page; + + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + + // TODO: Lock + + // Sanity Check Length + if(Length > TX_BUF_SIZE*256) { + Log_Warning( + "Ne2k", + "Ne2k_Write - Attempting to send over TX_BUF_SIZE*256 (%i) bytes (%i)", + TX_BUF_SIZE*256, Length + ); + LEAVE('i', 0); + return 0; + } + + // Make sure that the card is in page 0 + outb(Card->IOBase + CMD, 0|0x22); // Page 0, Start, NoDMA + + // Clear Remote DMA Flag + outb(Card->IOBase + ISR, 0x40); // Bit 6 + + // Send Size - Transfer Byte Count Register + outb(Card->IOBase + TBCR0, Length & 0xFF); + outb(Card->IOBase + TBCR1, Length >> 8); + + // Send Size - Remote Byte Count Register + outb(Card->IOBase + RBCR0, Length & 0xFF); + outb(Card->IOBase + RBCR1, Length >> 8); + + // Set up transfer + outb(Card->IOBase + RSAR0, 0x00); // Page Offset + page = Ne2k_int_GetWritePage(Card, Length); + outb(Card->IOBase + RSAR1, page); // Page Offset + // Start + outb(Card->IOBase + CMD, 0|0x10|0x2); // Page 0, Remote Write, Start + + // Send Data + for(rem = Length; rem > 0; rem -= 2) { + outw(Card->IOBase + 0x10, *buf++); + } + + while( !(inb(Card->IOBase + ISR) & 0x40) ) // Wait for Remote DMA Complete + ; //Proc_Yield(); + + outb( Card->IOBase + ISR, 0x40 ); // ACK Interrupt + + // Send Packet + outb(Card->IOBase + TPSR, page); + outb(Card->IOBase + CMD, 0|0x10|0x4|0x2); + + // Complete DMA + //outb(Card->IOBase + CMD, 0|0x20); + + LEAVE('i', Length); + return Length; +} + +/** + * \fn Uint64 Ne2k_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Wait for and read a packet from the network card + */ +Uint64 Ne2k_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tCard *Card = (tCard*)Node->ImplPtr; + Uint8 page; + Uint8 data[256]; + struct { + Uint8 Status; + Uint8 NextPacketPage; + Uint16 Length; // Little Endian + } *pktHdr; + + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + + // Wait for packets + if( Semaphore_Wait( &Card->Semaphore, 1 ) != 1 ) + { + // Error or interrupted + LEAVE_RET('i', 0); + } + + outb(Card->IOBase, 0x22 | (1 << 6)); // Page 6 + LOG("CURR : 0x%02x", inb(Card->IOBase + CURR)); + + // Get current read page + page = Card->NextRXPage; + LOG("page = %i", page); + + Ne2k_int_ReadDMA(Card, page, 1, data); + + pktHdr = (void*)data; + + LOG("pktHdr->Status = 0x%x", pktHdr->Status); + LOG("pktHdr->NextPacketPage = %i", pktHdr->NextPacketPage); + LOG("pktHdr->Length = 0x%03x", pktHdr->Length); + + // Have we read all the required bytes yet? + if(pktHdr->Length < 256 - 4) + { + if(Length > pktHdr->Length) + Length = pktHdr->Length; + memcpy(Buffer, &data[4], Length); + } + // No? oh damn, now we need to allocate a buffer + else + { + int pages = pktHdr->NextPacketPage - page; + char *buf = malloc( pages*256 ); + + LOG("pktHdr->Length (%i) > 256 - 4, allocated buffer %p", pktHdr->Length, buf); + + if(!buf) LEAVE_RET('i', -1); + + // Copy the already read data + memcpy(buf, data, 256); + + // Read all the needed pages + page ++; + if(page == RX_LAST+1) page = RX_FIRST; + + if( page + pages > RX_LAST ) + { + int first_count = RX_LAST+1 - page; + int tmp = 0; + tmp += Ne2k_int_ReadDMA(Card, page, first_count, buf+256); + tmp += Ne2k_int_ReadDMA(Card, RX_FIRST, pages-1-first_count, buf+(first_count+1)*256); + LOG("composite return count = %i", tmp); + } + else + Ne2k_int_ReadDMA(Card, page, pages-1, buf+256); + + // Wrap length to the packet length + if(Length > pktHdr->Length) + Length = pktHdr->Length; + else if( Length < pktHdr->Length ) { + Log_Warning("NE2000", "Packet truncated! (%i bytes truncated to %i)", + pktHdr->Length, Length); + } + memcpy(Buffer, &buf[4], Length); + + free(buf); + } + + // Write BNRY (maximum page for incoming packets) + if(pktHdr->NextPacketPage == RX_FIRST) + outb( Card->IOBase + BNRY, RX_LAST-1 ); + else + outb( Card->IOBase + BNRY, pktHdr->NextPacketPage-1 ); + // Set next RX Page and decrement the waiting list + Card->NextRXPage = pktHdr->NextPacketPage; + + LEAVE('i', Length); + return Length; +} + +int Ne2k_int_ReadDMA(tCard *Card, int FirstPage, int NumPages, void *Buffer) +{ + int i; + + // Sanity check + if( !(0 <= FirstPage && FirstPage < 256) ) { + Log_Warning("NE2000", "Ne2k_int_ReadDMA: BUG - FirstPage(%i) not 8-bit", FirstPage); + return -1; + } + if( !(0 <= NumPages && NumPages < 256) ) { + Log_Warning("NE2000", "Ne2k_int_ReadDMA: BUG - NumPages(%i) not 8-bit", NumPages); + return -1; + } + + ENTER("pCard iFirstPage iNumPages pBuffer", Card, FirstPage, NumPages, Buffer); + + // Make sure that the card is in bank 0 + outb(Card->IOBase + CMD, 0|0x22); // Bank 0, Start, NoDMA + outb(Card->IOBase + ISR, 0x40); // Clear Remote DMA Flag + + // Set up transfer + outb(Card->IOBase + RBCR0, 0); + outb(Card->IOBase + RBCR1, NumPages); // page count + outb(Card->IOBase + RSAR0, 0x00); // Page Offset + outb(Card->IOBase + RSAR1, FirstPage); // Page Number + outb(Card->IOBase + CMD, 0|0x08|0x2); // Bank 0, Remote Read, Start + + // TODO: Less expensive + //while( !(inb(Card->IOBase + ISR) & 0x40) ) { + // HALT(); + // LOG("inb(ISR) = 0x%02x", inb(Card->IOBase + ISR)); + //} + HALT(); // Small delay? + + // Read data + for(i = 0; i < 128*NumPages; i ++) + ((Uint16*)Buffer)[i] = inw(Card->IOBase + 0x10); + + + outb(Card->IOBase + ISR, 0x40); // Clear Remote DMA Flag + + LEAVE('i', NumPages); + return NumPages; +} + +/** + * \fn Uint8 Ne2k_int_GetWritePage(tCard *Card, Uint16 Length) + */ +Uint8 Ne2k_int_GetWritePage(tCard *Card, Uint16 Length) +{ + Uint8 ret = Card->NextMemPage; + + Card->NextMemPage += (Length + 0xFF) >> 8; + if(Card->NextMemPage >= TX_LAST) { + Card->NextMemPage -= TX_BUF_SIZE; + } + + return ret; +} + +/** + * \fn void Ne2k_IRQHandler(int IntNum) + */ +void Ne2k_IRQHandler(int IntNum, void *Ptr) +{ + Uint8 byte; + tCard *card = Ptr; + + if(card->IRQ != IntNum) return; + + byte = inb( card->IOBase + ISR ); + + LOG("byte = 0x%02x", byte); + + + // Reset All (save for RDMA), that's polled + outb( card->IOBase + ISR, 0xFF&(~0x40) ); + + // 0: Packet recieved (no error) + if( byte & 1 ) + { + //if( card->NumWaitingPackets > MAX_PACKET_QUEUE ) + // card->NumWaitingPackets = MAX_PACKET_QUEUE; + if( Semaphore_Signal( &card->Semaphore, 1 ) != 1 ) { + // Oops? + } + } + // 1: Packet sent (no error) + // 2: Recieved with error + // 3: Transmission Halted (Excessive Collisions) + // 4: Recieve Buffer Exhausted + // 5: + // 6: Remote DMA Complete + // 7: Reset +} diff --git a/KernelLand/Modules/Network/PCnet-FASTIII/Makefile b/KernelLand/Modules/Network/PCnet-FASTIII/Makefile new file mode 100644 index 00000000..1c7f3e94 --- /dev/null +++ b/KernelLand/Modules/Network/PCnet-FASTIII/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = rtl8139.o +NAME = RTL8139 + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Network/PCnet-FASTIII/pcnet-fast3.c b/KernelLand/Modules/Network/PCnet-FASTIII/pcnet-fast3.c new file mode 100644 index 00000000..bb4f83a8 --- /dev/null +++ b/KernelLand/Modules/Network/PCnet-FASTIII/pcnet-fast3.c @@ -0,0 +1,386 @@ +/* + * Acess2 PCnet-FAST III Driver + * - By John Hodge (thePowersGang) + */ +#define DEBUG 0 +#define VERSION ((0<<8)|10) +#include +#include +#include +#include +#include +#include + +// === CONSTANTS === +#define VENDOR_ID 0x1022 +#define DEVICE_ID 0x2000 + +enum eRegs +{ + REG_APROM0 = 0x00, + REG_APROM4 = 0x04, + REG_APROM8 = 0x08, + REG_APROMC = 0x0C, + REG_RDP = 0x10, // 16 bit + REG_RAP = 0x14, // 8-bit + REG_RESET = 0x18, // 16-bit + REG_BDP = 0x1C, // 16-bit +}; + +enum eCSR_Regs +{ + CSR_STATUS, // CSR0 - Am79C973/Am79C975 Controller Status + CSR_IBA0, // CSR1 - Initialization Block Address[15:0] + CSR_IBA1, // CSR2 - Initialization Block Address[31:16] + CSR_INTMASK, // CSR3 - Interrupt Masks and Deferral Control + + CSR_MAC0 = 12, // CSR12 - Physical Address[15:0] + CSR_MAC1 = 13, // CSR13 - Physical Address[31:16] + CSR_MAC2 = 14, // CSR14 - Physical Address[47:32] + CSR_MODE = 15, // CSR15 - Mode + + CSR_RXBASE0 = 24, // CSR24 - Base Address of Receive Ring Lower + CSR_RXBASE1 = 25, // CSR25 - Base Address of Receive Ring Upper + CSR_TXBASE0 = 30, // CSR26 - Base Address of Transmit Ring Lower + CSR_TXBASE1 = 31, // CSR27 - Base Address of Transmit Ring Upper + + CSR_RXLENGTH = 76, // CSR76 - Receive Ring Length + CSR_TXLENGTH = 78, // CSR78 - Transmit Ring Length +}; + +enum eBCR_Regs +{ + BCR_PHYCS = 32, // BCR32 - Internal PHY Control and Status + BCR_PHYADDR, // BCR33 - Internal PHY Address + BCR_PHYMGMT, // BCR34 - Internal PHY Management Data +}; + +// === TYPES === +typedef struct sCard +{ + Uint16 IOBase; + Uint8 IRQ; + + int NumWaitingPackets; + + char Name[2]; + tVFS_Node Node; + Uint8 MacAddr[6]; +} tCard; + +// === PROTOTYPES === + int PCnet3_Install(char **Options); +char *PCnet3_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *PCnet3_FindDir(tVFS_Node *Node, const char *Filename); + int PCnet3_RootIOCtl(tVFS_Node *Node, int ID, void *Arg); +Uint64 PCnet3_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 PCnet3_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + int PCnet3_IOCtl(tVFS_Node *Node, int ID, void *Arg); +void PCnet3_IRQHandler(int Num); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, PCnet3, PCnet3_Install, NULL, NULL); +tDevFS_Driver gPCnet3_DriverInfo = { + NULL, "PCnet3", + { + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Flags = VFS_FFLAG_DIRECTORY, + .ReadDir = PCnet3_ReadDir, + .FindDir = PCnet3_FindDir, + .IOCtl = PCnet3_RootIOCtl + } +}; + int giPCnet3_CardCount; +tCard *gaPCnet3_Cards; + +// === CODE === +/** + * \brief Installs the PCnet3 Driver + */ +int PCnet3_Install(char **Options) +{ + int id = -1; + int i = 0; + Uint16 base; + tCard *card; + + giPCnet3_CardCount = PCI_CountDevices(VENDOR_ID, DEVICE_ID); + Log_Debug("PCnet3", "%i cards", giPCnet3_CardCount); + + if( giPCnet3_CardCount == 0 ) return MODULE_ERR_NOTNEEDED; + + gaPCnet3_Cards = calloc( giPCnet3_CardCount, sizeof(tCard) ); + + while( (id = PCI_GetDevice(VENDOR_ID, DEVICE_ID, i)) != -1 ) + { + card = &gaPCnet3_Cards[i]; + base = PCI_GetBAR( id, 0 ); + if( !(base & 1) ) { + Log_Warning("PCnet3", "Driver does not support MMIO, skipping card"); + card->IOBase = 0; + card->IRQ = 0; + continue ; + } + base &= ~1; + card->IOBase = base; + card->IRQ = PCI_GetIRQ( id ); + + // Install IRQ Handler + IRQ_AddHandler(card->IRQ, PCnet3_IRQHandler); + + + + Log_Log("PCnet3", "Card %i 0x%04x, IRQ %i %02x:%02x:%02x:%02x:%02x:%02x", + i, card->IOBase, card->IRQ, + card->MacAddr[0], card->MacAddr[1], card->MacAddr[2], + card->MacAddr[3], card->MacAddr[4], card->MacAddr[5] + ); + + i ++; + } + + gPCnet3_DriverInfo.RootNode.Size = giPCnet3_CardCount; + DevFS_AddDevice( &gPCnet3_DriverInfo ); + + return MODULE_ERR_OK; +} + +// --- Root Functions --- +char *PCnet3_ReadDir(tVFS_Node *Node, int Pos) +{ + if( Pos < 0 || Pos >= giPCnet3_CardCount ) return NULL; + + return strdup( gaPCnet3_Cards[Pos].Name ); +} + +tVFS_Node *PCnet3_FindDir(tVFS_Node *Node, const char *Filename) +{ + //TODO: It might be an idea to supprt >10 cards + if(Filename[0] == '\0' || Filename[1] != '\0') return NULL; + if(Filename[0] < '0' || Filename[0] > '9') return NULL; + return &gaPCnet3_Cards[ Filename[0]-'0' ].Node; +} + +const char *csaPCnet3_RootIOCtls[] = {DRV_IOCTLNAMES, NULL}; +int PCnet3_RootIOCtl(tVFS_Node *Node, int ID, void *Data) +{ + ENTER("pNode iID pData", Node, ID, Data); + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_NETWORK, "PCnet3", VERSION, csaPCnet3_RootIOCtls); + } + LEAVE('i', 0); + return 0; +} + +// --- File Functions --- +Uint64 PCnet3_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tCard *card = Node->ImplPtr; + Uint16 read_ofs, pkt_length; + int new_read_ofs; + + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + +retry: + if( Semaphore_Wait( &card->ReadSemaphore, 1 ) != 1 ) + { + LEAVE_RET('i', 0); + } + + Mutex_Acquire( &card->ReadMutex ); + + read_ofs = inw( card->IOBase + CAPR ); + LOG("raw read_ofs = %i", read_ofs); + read_ofs = (read_ofs + 0x10) & 0xFFFF; + LOG("read_ofs = %i", read_ofs); + + pkt_length = *(Uint16*)&card->ReceiveBuffer[read_ofs+2]; + + // Calculate new read offset + new_read_ofs = read_ofs + pkt_length + 4; + new_read_ofs = (new_read_ofs + 3) & ~3; // Align + if(new_read_ofs > card->ReceiveBufferLength) { + LOG("wrapping read_ofs"); + new_read_ofs -= card->ReceiveBufferLength; + } + new_read_ofs -= 0x10; // I dunno + LOG("new_read_ofs = %i", new_read_ofs); + + // Check for errors + if( *(Uint16*)&card->ReceiveBuffer[read_ofs] & 0x1E ) { + // Update CAPR + outw(card->IOBase + CAPR, new_read_ofs); + Mutex_Release( &card->ReadMutex ); + goto retry; // I feel evil + } + + // Get packet + if( Length > pkt_length ) Length = pkt_length; + memcpy(Buffer, &card->ReceiveBuffer[read_ofs+4], Length); + + // Update CAPR + outw(card->IOBase + CAPR, new_read_ofs); + + Mutex_Release( &card->ReadMutex ); + + LEAVE('i', Length); + + return Length; +} + +Uint64 PCnet3_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + int td; + Uint32 status; + tCard *card = Node->ImplPtr; + + if( Length > 1500 ) return 0; // MTU exceeded + + ENTER("pNode XLength pBuffer", Node, Length, Buffer); + + // TODO: Implement a semaphore for avaliable transmit buffers + + // Find an avaliable descriptor + Mutex_Acquire(&card->CurTXProtector); + td = card->CurTXDescriptor; + card->CurTXDescriptor ++; + card->CurTXDescriptor %= 4; + Mutex_Release(&card->CurTXProtector); + // - Lock it + Mutex_Acquire( &card->TransmitInUse[td] ); + + LOG("td = %i", td); + + // Transmit using descriptor `td` + LOG("card->PhysTransmitBuffers[td] = %P", card->PhysTransmitBuffers[td]); + outd(card->IOBase + TSAD0 + td*4, card->PhysTransmitBuffers[td]); + LOG("card->TransmitBuffers[td] = %p", card->TransmitBuffers[td]); + // Copy to buffer + memcpy(card->TransmitBuffers[td], Buffer, Length); + // Start + status = 0; + status |= Length & 0x1FFF; // 0-12: Length + status |= 0 << 13; // 13: OWN bit + status |= (0 & 0x3F) << 16; // 16-21: Early TX threshold (zero atm, TODO: check) + LOG("status = 0x%08x", status); + outd(card->IOBase + TSD0 + td*4, status); + + LEAVE('i', (int)Length); + + return Length; +} + +const char *csaPCnet3_NodeIOCtls[] = {DRV_IOCTLNAMES, NULL}; +int PCnet3_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + tCard *card = Node->ImplPtr; + ENTER("pNode iID pData", Node, ID, Data); + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_NETWORK, "PCnet3", VERSION, csaPCnet3_NodeIOCtls); + case NET_IOCTL_GETMAC: + if( !CheckMem(Data, 6) ) { + LEAVE('i', -1); + return -1; + } + memcpy( Data, card->MacAddr, 6 ); + LEAVE('i', 1); + return 1; + } + LEAVE('i', 0); + return 0; +} + +void PCnet3_IRQHandler(int Num) +{ + int i, j; + tCard *card; + Uint16 status; + + LOG("Num = %i", Num); + + for( i = 0; i < giPCnet3_CardCount; i ++ ) + { + card = &gaPCnet3_Cards[i]; + if( Num != card->IRQ ) break; + + status = inw(card->IOBase + ISR); + LOG("status = 0x%02x", status); + + // Transmit OK, a transmit descriptor is now free + if( status & FLAG_ISR_TOK ) + { + for( j = 0; j < 4; j ++ ) + { + if( ind(card->IOBase + TSD0 + j*4) & 0x8000 ) { // TSD TOK + Mutex_Release( &card->TransmitInUse[j] ); + // TODO: Update semaphore once implemented + } + } + outw(card->IOBase + ISR, FLAG_ISR_TOK); + } + + // Recieve OK, inform read + if( status & FLAG_ISR_ROK ) + { + int read_ofs, end_ofs; + int packet_count = 0; + int len; + + // Scan recieve buffer for packets + end_ofs = inw(card->IOBase + CBA); + read_ofs = card->SeenOfs; + LOG("read_ofs = %i, end_ofs = %i", read_ofs, end_ofs); + if( read_ofs > end_ofs ) + { + while( read_ofs < card->ReceiveBufferLength ) + { + packet_count ++; + len = *(Uint16*)&card->ReceiveBuffer[read_ofs+2]; + LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x", + packet_count, read_ofs, + *(Uint16*)&card->ReceiveBuffer[read_ofs], + len + ); + if(len > 2000) { + Log_Warning("PCnet3", "IRQ: Packet in buffer exceeds sanity (%i>2000)", len); + } + read_ofs += len + 4; + read_ofs = (read_ofs + 3) & ~3; // Align + } + read_ofs -= card->ReceiveBufferLength; + LOG("wrapped read_ofs"); + } + while( read_ofs < end_ofs ) + { + packet_count ++; + LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x", + packet_count, read_ofs, + *(Uint16*)&card->ReceiveBuffer[read_ofs], + *(Uint16*)&card->ReceiveBuffer[read_ofs+2] + ); + read_ofs += *(Uint16*)&card->ReceiveBuffer[read_ofs+2] + 4; + read_ofs = (read_ofs + 3) & ~3; // Align + } + if( read_ofs != end_ofs ) { + Log_Warning("PCnet3", "IRQ: read_ofs (%i) != end_ofs(%i)", read_ofs, end_ofs); + read_ofs = end_ofs; + } + card->SeenOfs = read_ofs; + + LOG("packet_count = %i, read_ofs = 0x%x", packet_count, read_ofs); + + if( packet_count ) + { + if( Semaphore_Signal( &card->ReadSemaphore, packet_count ) != packet_count ) { + // Oops? + } + VFS_MarkAvaliable( &card->Node, 1 ); + } + + outw(card->IOBase + ISR, FLAG_ISR_ROK); + } + } +} diff --git a/KernelLand/Modules/Network/RTL8139/Makefile b/KernelLand/Modules/Network/RTL8139/Makefile new file mode 100644 index 00000000..1c7f3e94 --- /dev/null +++ b/KernelLand/Modules/Network/RTL8139/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = rtl8139.o +NAME = RTL8139 + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Network/RTL8139/rtl8139.c b/KernelLand/Modules/Network/RTL8139/rtl8139.c new file mode 100644 index 00000000..1e9ac899 --- /dev/null +++ b/KernelLand/Modules/Network/RTL8139/rtl8139.c @@ -0,0 +1,465 @@ +/* + * Acess2 RTL8139 Driver + * - By John Hodge (thePowersGang) + */ +#define DEBUG 0 +#define VERSION ((0<<8)|20) +#include +#include +#include +#include +#include +#include + +// === CONSTANTS === +#define VENDOR_ID 0x10EC +#define DEVICE_ID 0x8139 + +enum eRTL8139_Regs +{ + // MAC Address + MAC0, MAC1, MAC2, + MAC3, MAC4, MAC5, + + // Multicast Registers + MAR0 = 0x08, MAR1, MAR2, MAR3, + MAR4, MAR5, MAR6, MAR7, + + // Transmit status of descriptors 0 - 3 + TSD0 = 0x10, TSD1 = 0x14, + TSD2 = 0x18, TSD3 = 0x1C, + // Transmit start addresses + TSAD0 = 0x20, TSAD1 = 0x24, + TSAD2 = 0x28, TSAD3 = 0x2C, + + RBSTART = 0x30, //!< Recieve Buffer Start (DWord) + // Early Recieve Byte Count + ERBCR = 0x34, // 16-bits + // Early RX Status Register + ERSR = 0x36, + + // ??, ??, ??, RST, RE, TE, ??, ?? + CMD = 0x37, + + CAPR = 0x38, // Current address of packet read + CBA = 0x3A, // Current Buffer Address - Total byte count in RX buffer + + IMR = 0x3C, // Interrupt mask register + ISR = 0x3E, // Interrupt status register + + TCR = 0x40, // Transmit Configuration Register + RCR = 0x44, // Recieve Configuration Register + TCTR = 0x48, // 32-bit timer (count) + MPC = 0x4C, // Missed packet count (due to RX overflow) + + CR_9346 = 0x50, + CONFIG0 = 0x51, + CONFIG1 = 0x52, + // 0x53 resvd + TIMERINT = 0x54, // Fires a timeout when TCTR equals this value + +}; + +#define FLAG_ISR_TOK 0x04 +#define FLAG_ISR_ROK 0x01 + +// === TYPES === +typedef struct sCard +{ + Uint16 IOBase; + Uint8 IRQ; + + int NumWaitingPackets; + + char *ReceiveBuffer; + tPAddr PhysReceiveBuffer; + int ReceiveBufferLength; + int SeenOfs; //!< End of the most recently seen packet (by IRQ) + tMutex ReadMutex; + tSemaphore ReadSemaphore; + + char *TransmitBuffers[4]; + tPAddr PhysTransmitBuffers[4]; + tMutex TransmitInUse[4]; + tMutex CurTXProtector; //!< Protects \a .CurTXDescriptor + int CurTXDescriptor; + + char Name[2]; + tVFS_Node Node; + Uint8 MacAddr[6]; +} tCard; + +// === PROTOTYPES === + int RTL8139_Install(char **Options); +char *RTL8139_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *RTL8139_FindDir(tVFS_Node *Node, const char *Filename); + int RTL8139_RootIOCtl(tVFS_Node *Node, int ID, void *Arg); +Uint64 RTL8139_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 RTL8139_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); + int RTL8139_IOCtl(tVFS_Node *Node, int ID, void *Arg); +void RTL8139_IRQHandler(int Num, void *Ptr); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, RTL8139, RTL8139_Install, NULL, NULL); +tVFS_NodeType gRTL8139_RootNodeType = { + .ReadDir = RTL8139_ReadDir, + .FindDir = RTL8139_FindDir, + .IOCtl = RTL8139_IOCtl + }; +tVFS_NodeType gRTL8139_DevNodeType = { + .Write = RTL8139_Write, + .Read = RTL8139_Read, + .IOCtl = RTL8139_IOCtl + }; +tDevFS_Driver gRTL8139_DriverInfo = { + NULL, "RTL8139", + { + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Flags = VFS_FFLAG_DIRECTORY, + .Type = &gRTL8139_RootNodeType + } +}; + int giRTL8139_CardCount; +tCard *gaRTL8139_Cards; + +// === CODE === +/** + * \brief Installs the RTL8139 Driver + */ +int RTL8139_Install(char **Options) +{ + int id = -1; + int i = 0; + Uint16 base; + tCard *card; + + giRTL8139_CardCount = PCI_CountDevices(VENDOR_ID, DEVICE_ID); + + if( giRTL8139_CardCount == 0 ) return MODULE_ERR_NOTNEEDED; + + Log_Debug("RTL8139", "%i cards", giRTL8139_CardCount); + gaRTL8139_Cards = calloc( giRTL8139_CardCount, sizeof(tCard) ); + + for( i = 0 ; (id = PCI_GetDevice(VENDOR_ID, DEVICE_ID, i)) != -1; i ++ ) + { + card = &gaRTL8139_Cards[i]; + base = PCI_GetBAR( id, 0 ); + if( !(base & 1) ) { + Log_Warning("RTL8139", "Driver does not support MMIO, skipping card (addr %x)", + base); + card->IOBase = 0; + card->IRQ = 0; + continue ; + } + base &= ~1; + card->IOBase = base; + card->IRQ = PCI_GetIRQ( id ); + + // Install IRQ Handler + IRQ_AddHandler(card->IRQ, RTL8139_IRQHandler, card); + + // Power on + outb( base + CONFIG1, 0x00 ); + + // Reset (0x10 to CMD) + outb( base + CMD, 0x10 ); + while( inb(base + CMD) & 0x10 ) ; + + // Set up recieve buffer + // - Allocate 3 pages below 4GiB for the recieve buffer (Allows 8k+16+1500) + card->ReceiveBuffer = (void*)MM_AllocDMA( 3, 32, &card->PhysReceiveBuffer ); + card->ReceiveBufferLength = 8*1024; + outd(base + RBSTART, (Uint32)card->PhysReceiveBuffer); + outd(base + CBA, 0); + outd(base + CAPR, 0); + // Set IMR to Transmit OK and Receive OK + outw(base + IMR, 0x5); + + // Set up transmit buffers + // - 2 non-contiguous pages (each page can fit 2 1500 byte packets) + card->TransmitBuffers[0] = (void*)MM_AllocDMA( 1, 32, &card->PhysTransmitBuffers[0] ); + card->TransmitBuffers[1] = card->TransmitBuffers[0] + 0x800; + card->PhysTransmitBuffers[1] = card->PhysTransmitBuffers[0] + 0x800; + + card->TransmitBuffers[2] = (void*)MM_AllocDMA( 1, 32, &card->PhysTransmitBuffers[2] ); + card->TransmitBuffers[3] = card->TransmitBuffers[2] + 0x800; + card->PhysTransmitBuffers[3] = card->PhysTransmitBuffers[2] + 0x800; + + outd(base + TSAD0, card->PhysTransmitBuffers[0]); + outd(base + TSAD1, card->PhysTransmitBuffers[1]); + outd(base + TSAD2, card->PhysTransmitBuffers[2]); + outd(base + TSAD3, card->PhysTransmitBuffers[3]); + + // Set recieve buffer size and recieve mask + // - Bit 7 being set tells the card to overflow the recieve buffer if needed + // (i.e. when the packet starts at the end of the bufffer, it overflows up + // to 1500 bytes) + outd(base + RCR, 0x8F); + + // Recive Enable and Transmit Enable + outb(base + CMD, 0x0C); + + // Get the card's MAC address + card->MacAddr[0] = inb(base+MAC0); + card->MacAddr[1] = inb(base+MAC1); + card->MacAddr[2] = inb(base+MAC2); + card->MacAddr[3] = inb(base+MAC3); + card->MacAddr[4] = inb(base+MAC4); + card->MacAddr[5] = inb(base+MAC5); + + // Set VFS Node + card->Name[0] = '0'+i; + card->Name[1] = '\0'; + card->Node.ImplPtr = card; + card->Node.NumACLs = 0; + card->Node.CTime = now(); + card->Node.Type = &gRTL8139_DevNodeType; + + Log_Log("RTL8139", "Card %i 0x%04x, IRQ %i %02x:%02x:%02x:%02x:%02x:%02x", + i, card->IOBase, card->IRQ, + card->MacAddr[0], card->MacAddr[1], card->MacAddr[2], + card->MacAddr[3], card->MacAddr[4], card->MacAddr[5] + ); + } + + gRTL8139_DriverInfo.RootNode.Size = giRTL8139_CardCount; + DevFS_AddDevice( &gRTL8139_DriverInfo ); + + return MODULE_ERR_OK; +} + +// --- Root Functions --- +char *RTL8139_ReadDir(tVFS_Node *Node, int Pos) +{ + if( Pos < 0 || Pos >= giRTL8139_CardCount ) return NULL; + + return strdup( gaRTL8139_Cards[Pos].Name ); +} + +tVFS_Node *RTL8139_FindDir(tVFS_Node *Node, const char *Filename) +{ + //TODO: It might be an idea to supprt >10 cards + if(Filename[0] == '\0' || Filename[1] != '\0') return NULL; + if(Filename[0] < '0' || Filename[0] > '9') return NULL; + return &gaRTL8139_Cards[ Filename[0]-'0' ].Node; +} + +const char *csaRTL8139_RootIOCtls[] = {DRV_IOCTLNAMES, NULL}; +int RTL8139_RootIOCtl(tVFS_Node *Node, int ID, void *Data) +{ + ENTER("pNode iID pData", Node, ID, Data); + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_NETWORK, "RTL8139", VERSION, csaRTL8139_RootIOCtls); + } + LEAVE('i', 0); + return 0; +} + +// --- File Functions --- +Uint64 RTL8139_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tCard *card = Node->ImplPtr; + Uint16 read_ofs, pkt_length; + int new_read_ofs; + + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + +retry: + if( Semaphore_Wait( &card->ReadSemaphore, 1 ) != 1 ) + { + LEAVE_RET('i', 0); + } + + Mutex_Acquire( &card->ReadMutex ); + + read_ofs = inw( card->IOBase + CAPR ); + LOG("raw read_ofs = %i", read_ofs); + read_ofs = (read_ofs + 0x10) & 0xFFFF; + LOG("read_ofs = %i", read_ofs); + + pkt_length = *(Uint16*)&card->ReceiveBuffer[read_ofs+2]; + + // Calculate new read offset + new_read_ofs = read_ofs + pkt_length + 4; + new_read_ofs = (new_read_ofs + 3) & ~3; // Align + if(new_read_ofs > card->ReceiveBufferLength) { + LOG("wrapping read_ofs"); + new_read_ofs -= card->ReceiveBufferLength; + } + new_read_ofs -= 0x10; // I dunno + LOG("new_read_ofs = %i", new_read_ofs); + + // Check for errors + if( *(Uint16*)&card->ReceiveBuffer[read_ofs] & 0x1E ) { + // Update CAPR + outw(card->IOBase + CAPR, new_read_ofs); + Mutex_Release( &card->ReadMutex ); + goto retry; // I feel evil + } + + // Get packet + if( Length > pkt_length ) Length = pkt_length; + memcpy(Buffer, &card->ReceiveBuffer[read_ofs+4], Length); + + // Update CAPR + outw(card->IOBase + CAPR, new_read_ofs); + + Mutex_Release( &card->ReadMutex ); + + LEAVE('i', Length); + + return Length; +} + +Uint64 RTL8139_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + int td; + Uint32 status; + tCard *card = Node->ImplPtr; + + if( Length > 1500 ) return 0; // MTU exceeded + + ENTER("pNode XLength pBuffer", Node, Length, Buffer); + + // TODO: Implement a semaphore for avaliable transmit buffers + + // Find an avaliable descriptor + Mutex_Acquire(&card->CurTXProtector); + td = card->CurTXDescriptor; + card->CurTXDescriptor ++; + card->CurTXDescriptor %= 4; + Mutex_Release(&card->CurTXProtector); + // - Lock it + Mutex_Acquire( &card->TransmitInUse[td] ); + + LOG("td = %i", td); + + // Transmit using descriptor `td` + LOG("card->PhysTransmitBuffers[td] = %P", card->PhysTransmitBuffers[td]); + outd(card->IOBase + TSAD0 + td*4, card->PhysTransmitBuffers[td]); + LOG("card->TransmitBuffers[td] = %p", card->TransmitBuffers[td]); + // Copy to buffer + memcpy(card->TransmitBuffers[td], Buffer, Length); + // Start + status = 0; + status |= Length & 0x1FFF; // 0-12: Length + status |= 0 << 13; // 13: OWN bit + status |= (0 & 0x3F) << 16; // 16-21: Early TX threshold (zero atm, TODO: check) + LOG("status = 0x%08x", status); + outd(card->IOBase + TSD0 + td*4, status); + + LEAVE('i', (int)Length); + + return Length; +} + +const char *csaRTL8139_NodeIOCtls[] = {DRV_IOCTLNAMES, NULL}; +int RTL8139_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + tCard *card = Node->ImplPtr; + ENTER("pNode iID pData", Node, ID, Data); + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_NETWORK, "RTL8139", VERSION, csaRTL8139_NodeIOCtls); + case NET_IOCTL_GETMAC: + if( !CheckMem(Data, 6) ) { + LEAVE('i', -1); + return -1; + } + memcpy( Data, card->MacAddr, 6 ); + LEAVE('i', 1); + return 1; + } + LEAVE('i', 0); + return 0; +} + +void RTL8139_IRQHandler(int Num, void *Ptr) +{ + int j; + tCard *card = Ptr; + Uint16 status; + + LOG("Num = %i", Num); + + if( Num != card->IRQ ) return; + + status = inw(card->IOBase + ISR); + LOG("status = 0x%02x", status); + + // Transmit OK, a transmit descriptor is now free + if( status & FLAG_ISR_TOK ) + { + for( j = 0; j < 4; j ++ ) + { + if( ind(card->IOBase + TSD0 + j*4) & 0x8000 ) { // TSD TOK + Mutex_Release( &card->TransmitInUse[j] ); + // TODO: Update semaphore once implemented + } + } + outw(card->IOBase + ISR, FLAG_ISR_TOK); + } + + // Recieve OK, inform read + if( status & FLAG_ISR_ROK ) + { + int read_ofs, end_ofs; + int packet_count = 0; + int len; + + // Scan recieve buffer for packets + end_ofs = inw(card->IOBase + CBA); + read_ofs = card->SeenOfs; + LOG("read_ofs = %i, end_ofs = %i", read_ofs, end_ofs); + if( read_ofs > end_ofs ) + { + while( read_ofs < card->ReceiveBufferLength ) + { + packet_count ++; + len = *(Uint16*)&card->ReceiveBuffer[read_ofs+2]; + LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x", + packet_count, read_ofs, + *(Uint16*)&card->ReceiveBuffer[read_ofs], + len + ); + if(len > 2000) { + Log_Warning("RTL8139", "IRQ: Packet in buffer exceeds sanity (%i>2000)", len); + } + read_ofs += len + 4; + read_ofs = (read_ofs + 3) & ~3; // Align + } + read_ofs -= card->ReceiveBufferLength; + LOG("wrapped read_ofs"); + } + while( read_ofs < end_ofs ) + { + packet_count ++; + LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x", + packet_count, read_ofs, + *(Uint16*)&card->ReceiveBuffer[read_ofs], + *(Uint16*)&card->ReceiveBuffer[read_ofs+2] + ); + read_ofs += *(Uint16*)&card->ReceiveBuffer[read_ofs+2] + 4; + read_ofs = (read_ofs + 3) & ~3; // Align + } + if( read_ofs != end_ofs ) { + Log_Warning("RTL8139", "IRQ: read_ofs (%i) != end_ofs(%i)", read_ofs, end_ofs); + read_ofs = end_ofs; + } + card->SeenOfs = read_ofs; + + LOG("packet_count = %i, read_ofs = 0x%x", packet_count, read_ofs); + + if( packet_count ) + { + if( Semaphore_Signal( &card->ReadSemaphore, packet_count ) != packet_count ) { + // Oops? + } + VFS_MarkAvaliable( &card->Node, 1 ); + } + + outw(card->IOBase + ISR, FLAG_ISR_ROK); + } +} diff --git a/KernelLand/Modules/Sound/SoundBlaster16/Makefile b/KernelLand/Modules/Sound/SoundBlaster16/Makefile new file mode 100644 index 00000000..9384859c --- /dev/null +++ b/KernelLand/Modules/Sound/SoundBlaster16/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = main.o +NAME = SoundBlaster16 + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Sound/SoundBlaster16/main.c b/KernelLand/Modules/Sound/SoundBlaster16/main.c new file mode 100644 index 00000000..f2d056c6 --- /dev/null +++ b/KernelLand/Modules/Sound/SoundBlaster16/main.c @@ -0,0 +1,116 @@ +/* + * Acess2 SoundBlaster16 Driver + */ +#define DEBUG 0 +#include +#include +#include +#include +#include +#include +#include + +#define INT + +// === TYPES === +typedef struct sSB16 +{ + Uint16 Base; +} tSB16; + +// === CONSTANTS === +enum { + SB16_PORT_RESET = 0x6, + SB16_PORT_READ = 0xA, + SB16_PORT_WRITE = 0xC, + SB16_PORT_AVAIL = 0xE +}; +#define SB16_BASE_PORT 0x200 +enum { + SB16_CMD_RAW8 = 0x10, // 8-bit DAC value follows + SB16_CMD_DMAFREQ = 0x40, // followed by TIME_CONSTANT = 256 - 1000000 / frequency + SB16_CMD_DMASTOP = 0xD0, + SB16_CMD_SPKRON = 0xD1, + SB16_CMD_SPKROFF = 0xD3, + SB16_CMD_DMACONT = 0xD4, + + // DMA Types (uses channel 1) + // - Followed by 16-bit length (well, length - 1) + SB16_CMD_DMA_8BIT = 0x14, +}; + + +// === PROTOTYPES === +// Driver + int SB16_Install(char **Arguments); +void SB16_Uninstall(); +// Filesystem +Uint64 SB16_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + int SB16_IOCtl(tVFS_Node *Node, int ID, void *Data); + +// === GLOBALS === +MODULE_DEFINE(0, 0x0032, SoundBlaster16, SB16_Install, SB16_Uninstall, "PCI", NULL); +tDevFS_Driver gBGA_DriverStruct = { + NULL, "SoundBlaster16", + { + .Write = SB16_Write, + .IOCtl = SB16_IOCtl + } +}; + +// === CODE === +int SB16_Install(char **Arguments) +{ + int jumper_port_setting = 1; // 1-6 incl + + // Reset + outb(card->Base+SB16_PORT_RESET, 1); + // - Wait 3us + outb(card->Base+SB16_PORT_RESET, 0); + + SB16_ReadDSP(card); + + return MODULE_ERR_OK; +} + +void SB16_Uninstall() +{ +} + +/** + * \fn Uint64 SB16_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Write to the framebuffer + */ +Uint64 SB16_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + return 0; +} + +/** + * \fn int SB16_IOCtl(tVFS_Node *Node, int ID, void *Data) + * \brief Handle messages to the device + */ +int SB16_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + return -1; +} + +// +int SB16_WriteDSP(tSB16 *Card, Uint8 Value) +{ + // Wait for the card to be ready + while( inb(Card->Base+SB16_PORT_WRITE) & 0x80 ) + ; + + outb(Card->Base+SB16_PORT_WRITE, Value); + + return 0; +} + +Uint8 SB16_ReadDSP(tSB16 *Card) +{ + // Wait for bit 7 of AVAIL + while( !(inb(card->Base+SB16_PORT_AVAIL) & 0x80) ) + ; + return inb(card->Base+SB16_PORT_READ); +} diff --git a/KernelLand/Modules/Sound/SoundBlaster16/sbdsp.txt b/KernelLand/Modules/Sound/SoundBlaster16/sbdsp.txt new file mode 100644 index 00000000..94364cd3 --- /dev/null +++ b/KernelLand/Modules/Sound/SoundBlaster16/sbdsp.txt @@ -0,0 +1,442 @@ + + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Programming the SoundBlaster DSP ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + + Written for the PC-GPE by Mark Feldman + e-mail address : u914097@student.canberra.edu.au + myndale@cairo.anu.edu.au + + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ THIS FILE MAY NOT BE DISTRIBUTED ³ + ³ SEPARATE TO THE ENTIRE PC-GPE COLLECTION. ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ Disclaimer ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ + +I assume no responsibility whatsoever for any effect that this file, the +information contained therein or the use thereof has on you, your sanity, +computer, spouse, children, pets or anything else related to you or your +existance. No warranty is provided nor implied with this information. + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ Introduction ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +The SoundBlaster is capable of both FM and digitised sounds. The FM wave +is fully Adlib compatible, so check the ADLIB.TXT file for info +on how to program it. This file will concentrate on recording and playback +of digital samples through the SoundBlaster CT-DSP 1321 chip. + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ The SoundBlaster DSP I/O Ports ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +The DSP (Digital Sound Processor) chip is programmed through 4 ports which +are determined by the SoundBlaster base address jumper setting: + + RESET 2x6h + + READ DATA 2xAh + +WRITE COMMAND/DATA output +WRITE BUFFER STATUS input 2xCh + + + DATA AVAILABLE 2xEh + +where x = 1 for base address jumper setting 210h + x = 2 for base address jumper setting 220h + . + . + x = 6 for base address jumper setting 260h + + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ Resetting the DSP ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +You have to reset the DSP before you program it. This is done with the +following procedure : + +1) Write a 1 to the SoundBlaster RESET port (2x6h) +2) Wait for 3 micro-seconds +3) Write a 0 to the SoundBlaster RESET port (2x6h) +4) Read the byte from the DATA AVAILABLE (2xEh) port until bit 7 = 1 +5) Poll for a ready byte (AAh) from the READ DATA port (2xAh). Before + reading the READ DATA port it is avdvisable. + +The DSP usually takes somewhere around 100 micro-seconds to reset itself. +If it fails to do within a reasonable time (say 200 micro-seconds) then +an error has occurred, possibly an incorrect I/O address is being used. + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ Writing to the DSP ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +A value can be written to the DSP with the following procedure : + +1) Read the DSP's WRITE BUFFER STATUS port (2xCh) until bit 7 = 0 +2) Write the value to the WRITE COMMAND/DATA port (2xCh) + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ Reading the DSP ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +A value can be read from the DSP with the following procedure : + +1) Read the DSP's DATA AVAILABLE port (2xEh) until bit 7 = 1 +2) Read the data from the READ DATA port (2xAh) + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ Turning the speaker on and controlling DMA ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +Speaker and DMA control are handled by writing one of the following bytes +to the DSP: + + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ Value Description ³ + ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ + ³ D0h DMA Stop ³ + ³ D1h Turn speaker on ³ + ³ D3h Turn speaker off ³ + ³ D4h DMA Continue ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +DMA is discussed below. The DMA commands shown here can be used to pause +the sample during DMA playback playback. + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ Writing to the DAC ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +The DAC (Digital to Analog Converter) is the part of the card which converts +a sample number (ie 0 -> 255) to a sound level. To generate a square sound +wave at maximum volume (for example) you could alternate writing 0's and +255's to the DAC. + +Programming the DAC in direct mode involves the main program setting the +DAC to a desired value. Only 8 bit DAC is available in direct mode. To set +the DAC level you write the value 10h to the DSP followed by the sample +number (0 -> 255). Note that no sound will be heard unless the speaker has +been turned on. In direct mode the main program is responsible for the +timing between samples, the DAC can output sound samples as fast as the +calling program can change it. Typically the timer interrupt is reprogrammed +and used to generate the timing required for a sample playback. Info on +programming the PIT chip can be found in the PIT.TXT file. + +The DAC can also be programmed to accept values sent to it via the DMA +chip. Draeden has written an excellent article on programming the DMA chip +(see DMA_VLA.TXT) so only a brief example of it's use will be given here. +The important thing to remember is that the DMA chip cannot transfer data +which crosses between page breaks. If the data does cross page breaks then +it will have to be split up into several transfers, with one page per +transfer. + +Setting the playback frequency for the DMA transfer is done by writing +the value 40h to the DSP followed by TIME_CONSTANT, where +TIME_CONSTANT = 256 - 1000000 / frequency + +There are several types of DMA transfers available. The following table +lists them: + + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³DMA_TYPE_VALUE Description Frequency Range ³ + ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ + ³ 14h 8 bit 4KHz -> 23 KHz ³ + ³ 74h 4 bit ADPCM 4KHz -> 12 KHz ³ + ³ 75h 4 bit ADPCM with 4KHz -> 12 KHz ³ + ³ reference byte ³ + ³ 76h 2.6 bit ADPCM 4KHz -> 13 KHz ³ + ³ 77h 2.6 bit ADPCM with 4KHz -> 13 KHz ³ + ³ reference byte ³ + ³ 16h 2 bit ADPCM 4KHz -> 11 KHz ³ + ³ 17h 2 bit ADPCM with 4KHz -> 11 KHz ³ + ³ reference byte ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +ADPCM stands for Adaptive Pulse Code Modulation, a sound compression +technique where the difference between successive samples is stored rather +than their actual values. In the modes with reference bytes, the first +byte is the actual starting value. Having modes with and without reference +bytes means you can output successive blocks without the need for a +reference byte at the start of each one. + +The procedure for doing a DMA transfer is as follows: + +1) Load the sound data into memory +2) Set up the DMA chip for the tranfer +3) Set the DSP TIME_CONSTANT to the sampling rate +4) Write DMA_TYPE_VALUE value to the DSP +5) Write DATA_LENGTH to the DSP (2 bytes, LSB first) where + DATA_LENGTH = number of bytes to send - 1 + +Note that the DMA chip must be programmed before the BSP. + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ Reading from the ADC ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +Reading samples from the ADC (Analog to Digital Converter) can also be +done in either direct or DMA mode. + +To read a sample in direct mode write the value 20h to the DSP and then +read the value from the DSP. Simple as that! + +To set up the DSP for a DMA transfer, follow this procedure : + +1) Get a memory buffer ready to hold the sample +2) Set up the DMA chip for the transfer +3) Set the DSP TIME_CONSTANT to the sampling rate +4) Write the value 24h to the DSP +5) Write DATA_LENGTH to the DSP (2 bytes, LSB first) where + DATA_LENGTH = number of bytes to read - 1 + +Note that the DMA chip must be programmed before the BSP. + +DMA reads only support 8 bit mode, compressed modes are done by software and +stored in the voc file. I haven't tried to figure out how the compression is +done. If someone does figure it out I'd like to know about it! + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ Programming the DMA Chip ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +As mentioned before, Draeden has written a very good article on the dma +chip, but here is a brief run down on what you would need to do to program +the DMA channel 1 for the DSP in real mode: + +1) Calculate the 20 bit address of the memory buffer you are using + where Base Address = Segment * 16 + Offset + eg 1234h:5678h = 179B8h +2) Send the value 05h to port 0Ah (mask off channel 1) +3) Send the value 00h to port 0Ch (clear the internal DMA flip/flop) +4) Send the value 49h to port 0Bh (for playback) or + 45h to port 0Bh (for recording) +5) Write the LSB (bits 0 -> 7) of the 20 bit memory address to port 02h +6) Write the MSB (bits 8 -> 15) of the 20 bit memory address to ort 02h +7) Write the Page (bits 16 -> 19) of the 20 bit memory address to port 83h +8) Send the LSB of DATA_LENGTH to port 03h +9) Send the MSB of DATA_LENGTH to port 03h +10) Send the value 01h to port 0Ah (enable channel 1) + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ End of DMA Interrupt ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +When a DMA transfer is complete an interrupt is generated. The actual +interrupt number depends on the SoundBlaster card's IRQ jumper setting: + + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ IRQ Jumper ³ + ³ Setting Interrupt ³ + ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ + ³ 2 0Ah ³ + ³ 3 0Bh ³ + ³ 5 0Dh ³ + ³ 7 0Fh ³ + ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +To service one of these interrupts you must perform these 3 tasks: + +1) Acknowledge the DSP interrupt by reading the DATA AVAILABLE port (2xEh) + once. +2) If there are more blocks to transfer then set them up +3) Output value 20h (EOI) to the interrupt controller port 20h + +Of course, as with any hardware interrupt you must also leave the +state of the system (registers etc..) the way it was when the interrupt +was called. + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ A Simple DSP Pascal Unit ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +{ + + DSP.PAS - A demo SoundBlaster DSP unit for real mode + + By Mark Feldman +} + +Unit DSP; + +Interface + +{ ResetDSP returns true if reset was successful + base should be 1 for base address 210h, 2 for 220h etc... } +function ResetDSP(base : word) : boolean; + +{ Write DAC sets the speaker output level } +procedure WriteDAC(level : byte); + +{ ReadDAC reads the microphone input level } +function ReadDAC : byte; + +{ SpeakerOn connects the DAC to the speaker } +function SpeakerOn: byte; + +{ SpeakerOff disconnects the DAC from the speaker, + but does not affect the DAC operation } +function SpeakerOff: byte; + +{ Functions to pause DMA playback } +procedure DMAStop; +procedure DMAContinue; + +{ Playback plays a sample of a given size back at a given frequency using + DMA channel 1. The sample must not cross a page boundry } +procedure Playback(sound : Pointer; size : word; frequency : word); + +Implementation + +Uses Crt; + +var DSP_RESET : word; + DSP_READ_DATA : word; + DSP_WRITE_DATA : word; + DSP_WRITE_STATUS : word; + DSP_DATA_AVAIL : word; + +function ResetDSP(base : word) : boolean; +begin + + base := base * $10; + + { Calculate the port addresses } + DSP_RESET := base + $206; + DSP_READ_DATA := base + $20A; + DSP_WRITE_DATA := base + $20C; + DSP_WRITE_STATUS := base + $20C; + DSP_DATA_AVAIL := base + $20E; + + { Reset the DSP, and give some nice long delays just to be safe } + Port[DSP_RESET] := 1; + Delay(10); + Port[DSP_RESET] := 0; + Delay(10); + if (Port[DSP_DATA_AVAIL] And $80 = $80) And + (Port[DSP_READ_DATA] = $AA) then + ResetDSP := true + else + ResetDSP := false; +end; + +procedure WriteDSP(value : byte); +begin + while Port[DSP_WRITE_STATUS] And $80 <> 0 do; + Port[DSP_WRITE_DATA] := value; +end; + +function ReadDSP : byte; +begin + while Port[DSP_DATA_AVAIL] and $80 = 0 do; + ReadDSP := Port[DSP_READ_DATA]; +end; + +procedure WriteDAC(level : byte); +begin + WriteDSP($10); + WriteDSP(level); +end; + +function ReadDAC : byte; +begin + WriteDSP($20); + ReadDAC := ReadDSP; +end; + +function SpeakerOn: byte; +begin + WriteDSP($D1); +end; + +function SpeakerOff: byte; +begin + WriteDSP($D3); +end; + +procedure DMAContinue; +begin + WriteDSP($D4); +end; + +procedure DMAStop; +begin + WriteDSP($D0); +end; + +procedure Playback(sound : Pointer; size : word; frequency : word); +var time_constant : word; + page, offset : word; +begin + + SpeakerOn; + + size := size - 1; + + { Set up the DMA chip } + offset := Seg(sound^) Shl 4 + Ofs(sound^); + page := (Seg(sound^) + Ofs(sound^) shr 4) shr 12; + Port[$0A] := 5; + Port[$0C] := 0; + Port[$0B] := $49; + Port[$02] := Lo(offset); + Port[$02] := Hi(offset); + Port[$83] := page; + Port[$03] := Lo(size); + Port[$03] := Hi(size); + Port[$0A] := 1; + + { Set the playback frequency } + time_constant := 256 - 1000000 div frequency; + WriteDSP($40); + WriteDSP(time_constant); + + { Set the playback type (8-bit) } + WriteDSP($14); + WriteDSP(Lo(size)); + WriteDSP(Hi(size)); +end; + +end. + + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ References ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ + +Title : The SoundBlaster Developpers Kit +Publishers : Creative Labs Inc + Creative Technology PTE LTD + +Title : Sound Blaster - The Official Book +Authors : Richard Heimlich, David M. Golden, Ivan Luk, Peter M. Ridge +Publishers : Osborne/McGraw Hill +ISBN : 0-07-881907-5 + +Some of the information in this file was either obtained from or verified +by the source code in a public domain library called SOUNDX by Peter +Sprenger. I haven't tried using his library yet (I don't have a C compiler +at the moment) but it looks very well done and contains numerous sound card +detection routines. Says Peter : "It would be nice, that when you make +something commercial with my routines, that you send me a copy of your +project or send me some bucks, just enough for pizza and coke to support my +night programming sessions. If you send me nothing, ok. But USE the stuff, +if you can need it!". Heh...a REAL programmer! + +ftpsite: ftp.uwp.edu +directory: /pub/msdos/demos/programming/game-dev/source +filename: soundx.zip + +ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +³ Sound Familiar? ³ +ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ + +What the...why is there a faint glimmer of sunlight outside? HOLY $#!^!! It's +5:30am! I'm goin' to bed! + diff --git a/KernelLand/Modules/Storage/ATA/Makefile b/KernelLand/Modules/Storage/ATA/Makefile new file mode 100644 index 00000000..22923422 --- /dev/null +++ b/KernelLand/Modules/Storage/ATA/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = main.o io.o mbr.o +NAME = ATA + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Storage/ATA/common.h b/KernelLand/Modules/Storage/ATA/common.h new file mode 100644 index 00000000..891d2bbe --- /dev/null +++ b/KernelLand/Modules/Storage/ATA/common.h @@ -0,0 +1,68 @@ +/* + * Acess2 IDE Harddisk Driver + * - main.c + */ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#include +#include + +// === CONSTANTS === +#define MAX_ATA_DISKS 4 +#define SECTOR_SIZE 512 +#define ATA_TIMEOUT 2000 // 2s timeout +// Needed out of io.c because it's the max for Read/WriteDMA +#define MAX_DMA_SECTORS (0x1000 / SECTOR_SIZE) + +// === STRUCTURES === +typedef struct +{ + Uint8 BootCode[0x1BE]; + struct { + Uint8 Boot; + Uint8 Unused1; // Also CHS Start + Uint16 StartHi; // Also CHS Start + Uint8 SystemID; + Uint8 Unused2; // Also CHS Length + Uint16 LengthHi; // Also CHS Length + Uint32 LBAStart; + Uint32 LBALength; + } __attribute__ ((packed)) Parts[4]; + Uint16 BootFlag; // = 0xAA 55 +} __attribute__ ((packed)) tMBR; + +typedef struct +{ + Uint64 Start; + Uint64 Length; + char Name[4]; + tVFS_Node Node; +} tATA_Partition; + +typedef struct +{ + Uint64 Sectors; + char Name[2]; + tVFS_Node Node; + int NumPartitions; + tATA_Partition *Partitions; +} tATA_Disk; + +// === GLOBALS === +extern tATA_Disk gATA_Disks[]; + +// === FUNCTIONS === +// --- Common --- +extern void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length); + +// --- MBR Parsing --- +extern void ATA_ParseMBR(int Disk, tMBR *MBR); + +// --- IO Functions --- +extern int ATA_SetupIO(void); +extern Uint64 ATA_GetDiskSize(int Disk); +extern int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer); +extern int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer); + +#endif diff --git a/KernelLand/Modules/Storage/ATA/io.c b/KernelLand/Modules/Storage/ATA/io.c new file mode 100644 index 00000000..b8a15d01 --- /dev/null +++ b/KernelLand/Modules/Storage/ATA/io.c @@ -0,0 +1,591 @@ +/* + * Acess2 IDE Harddisk Driver + * - io.c + * + * Disk Input/Output control + */ +#define DEBUG 0 +#include +#include // Needed for error codes +#include +#include "common.h" + +// === MACROS === +#define IO_DELAY() do{inb(0x80); inb(0x80); inb(0x80); inb(0x80);}while(0) + +// === Constants === +#define IDE_PRI_BASE 0x1F0 +#define IDE_PRI_CTRL 0x3F6 +#define IDE_SEC_BASE 0x170 +#define IDE_SEC_CTRL 0x376 + +#define IDE_PRDT_LAST 0x8000 +/** + \enum HddControls + \brief Commands to be sent to HDD_CMD +*/ +enum HddControls { + HDD_PIO_R28 = 0x20, + HDD_PIO_R48 = 0x24, + HDD_DMA_R48 = 0x25, + HDD_PIO_W28 = 0x30, + HDD_PIO_W48 = 0x34, + HDD_DMA_W48 = 0x35, + HDD_DMA_R28 = 0xC8, + HDD_DMA_W28 = 0xCA, + HDD_IDENTIFY = 0xEC +}; + +// === TYPES === +/** + * \brief PRDT Entry + */ +typedef struct +{ + Uint32 PBufAddr; // Physical Buffer Address + Uint16 Bytes; // Size of transfer entry + Uint16 Flags; // Flags +} __attribute__ ((packed)) tPRDT_Ent; + +/** + * \brief Structure returned by the ATA IDENTIFY command + */ +typedef struct +{ + Uint16 Flags; // 1 + Uint16 Usused1[9]; // 10 + char SerialNum[20]; // 20 + Uint16 Usused2[3]; // 23 + char FirmwareVer[8]; // 27 + char ModelNumber[40]; // 47 + Uint16 SectPerInt; // 48 - Low byte only + Uint16 Unused3; // 49 + Uint16 Capabilities[2]; // 51 + Uint16 Unused4[2]; // 53 + Uint16 ValidExtData; // 54 + Uint16 Unused5[5]; // 59 + Uint16 SizeOfRWMultiple; // 60 + Uint32 Sectors28; // LBA 28 Sector Count + Uint16 Unused6[100-62]; + Uint64 Sectors48; // LBA 48 Sector Count + Uint16 Unused7[256-104]; +} __attribute__ ((packed)) tIdentify; + +// === PROTOTYPES === + int ATA_SetupIO(void); +Uint64 ATA_GetDiskSize(int Disk); +Uint16 ATA_GetBasePort(int Disk); +// Read/Write DMA + int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer); + int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer); +// IRQs +void ATA_IRQHandlerPri(int UNUSED(IRQ), void *UNUSED(Ptr)); +void ATA_IRQHandlerSec(int UNUSED(IRQ), void *UNUSED(Ptr)); +// Controller IO +Uint8 ATA_int_BusMasterReadByte(int Ofs); +Uint32 ATA_int_BusMasterReadDWord(int Ofs); +void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value); +void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value); + +// === GLOBALS === +// - BusMaster IO Addresses +Uint32 gATA_BusMasterBase; //!< True Address (IO/MMIO) +Uint8 *gATA_BusMasterBasePtr; //!< Paging Mapped MMIO (If needed) +// - IRQs + int gATA_IRQPri = 14; + int gATA_IRQSec = 15; +volatile int gaATA_IRQs[2] = {0}; +// - Locks to avoid tripping +tMutex glaATA_ControllerLock[2]; +// - Buffers! +Uint8 gATA_Buffers[2][(MAX_DMA_SECTORS+0xFFF)&~0xFFF] __attribute__ ((section(".padata"))); +// - PRDTs +tPRDT_Ent gATA_PRDTs[2] = { + {0, 512, IDE_PRDT_LAST}, + {0, 512, IDE_PRDT_LAST} +}; +tPAddr gaATA_PRDT_PAddrs[2]; + +// === CODE === +/** + * \brief Sets up the ATA controller's DMA mode + */ +int ATA_SetupIO(void) +{ + int ent; + + ENTER(""); + + // Get IDE Controller's PCI Entry + ent = PCI_GetDeviceByClass(0x010100, 0xFFFF00, -1); + LOG("ent = %i", ent); + gATA_BusMasterBase = PCI_GetBAR(ent, 4); + if( gATA_BusMasterBase == 0 ) { + Log_Warning("ATA", "It seems that there is no Bus Master Controller on this machine. Get one"); + // TODO: Use PIO mode instead + LEAVE('i', MODULE_ERR_NOTNEEDED); + return MODULE_ERR_NOTNEEDED; + } + + LOG("BAR5 = 0x%x", PCI_GetBAR(ent, 5)); + LOG("IRQ = %i", PCI_GetIRQ(ent)); + + // Map memory + if( !(gATA_BusMasterBase & 1) ) + { + if( gATA_BusMasterBase < 0x100000 ) + gATA_BusMasterBasePtr = (void*)(KERNEL_BASE | (tVAddr)gATA_BusMasterBase); + else + gATA_BusMasterBasePtr = (void*)( MM_MapHWPages( gATA_BusMasterBase, 1 ) + (gATA_BusMasterBase&0xFFF) ); + LOG("gATA_BusMasterBasePtr = %p", gATA_BusMasterBasePtr); + } + else { + // Bit 0 is left set as a flag to other functions + LOG("gATA_BusMasterBase = IO 0x%x", gATA_BusMasterBase & ~1); + } + + // Register IRQs and get Buffers + IRQ_AddHandler( gATA_IRQPri, ATA_IRQHandlerPri, NULL ); + IRQ_AddHandler( gATA_IRQSec, ATA_IRQHandlerSec, NULL ); + + gATA_PRDTs[0].PBufAddr = MM_GetPhysAddr( (tVAddr)&gATA_Buffers[0] ); + gATA_PRDTs[1].PBufAddr = MM_GetPhysAddr( (tVAddr)&gATA_Buffers[1] ); + + LOG("gATA_PRDTs = {PBufAddr: 0x%x, PBufAddr: 0x%x}", gATA_PRDTs[0].PBufAddr, gATA_PRDTs[1].PBufAddr); + + gaATA_PRDT_PAddrs[0] = MM_GetPhysAddr( (tVAddr)&gATA_PRDTs[0] ); + LOG("gaATA_PRDT_PAddrs[0] = 0x%x", gaATA_PRDT_PAddrs[0]); + ATA_int_BusMasterWriteDWord(4, gaATA_PRDT_PAddrs[0]); + + gaATA_PRDT_PAddrs[1] = MM_GetPhysAddr( (tVAddr)&gATA_PRDTs[1] ); + LOG("gaATA_PRDT_PAddrs[1] = 0x%x", gaATA_PRDT_PAddrs[1]); + ATA_int_BusMasterWriteDWord(12, gaATA_PRDT_PAddrs[1]); + + // Enable controllers + outb(IDE_PRI_BASE+1, 1); + outb(IDE_SEC_BASE+1, 1); + outb(IDE_PRI_CTRL, 0); + outb(IDE_SEC_CTRL, 0); + + // Make sure interrupts are ACKed + ATA_int_BusMasterWriteByte(2, 0x4); + ATA_int_BusMasterWriteByte(10, 0x4); + + // return + LEAVE('i', MODULE_ERR_OK); + return MODULE_ERR_OK; +} + +/** + * \brief Get the size (in sectors) of a disk + * \param Disk Disk to get size of + * \return Number of sectors reported + * + * Does an ATA IDENTIFY + */ +Uint64 ATA_GetDiskSize(int Disk) +{ + union { + Uint16 buf[256]; + tIdentify identify; + } data; + Uint16 base; + Uint8 val; + int i; + ENTER("iDisk", Disk); + + base = ATA_GetBasePort( Disk ); + + // Send Disk Selector + if(Disk & 1) // Slave + outb(base+6, 0xB0); + else // Master + outb(base+6, 0xA0); + IO_DELAY(); + + // Check for a floating bus + if( 0xFF == inb(base+7) ) { + LOG("Floating bus"); + LEAVE('i', 0); + return 0; + } + + // Check for the controller + // - Write to two RW ports and attempt to read back + outb(base+0x02, 0x66); + outb(base+0x03, 0xFF); + if(inb(base+0x02) != 0x66 || inb(base+0x03) != 0xFF) { + LOG("No controller"); + LEAVE('i', 0); + return 0; + } + + // Send ATA IDENTIFY + outb(base+7, HDD_IDENTIFY); + IO_DELAY(); + val = inb(base+7); // Read status + LOG("val = 0x%02x", val); + if(val == 0) { + LEAVE('i', 0); + return 0; // Disk does not exist + } + + // Poll until BSY clears or ERR is set + // TODO: Timeout? + while( (val & 0x80) && !(val & 1) ) + val = inb(base+7); + LOG("BSY unset (0x%x)", val); + // and, wait for DRQ to set + while( !(val & 0x08) && !(val & 1)) + val = inb(base+7); + LOG("DRQ set (0x%x)", val); + + // Check for an error + if(val & 1) { + LEAVE('i', 0); + return 0; // Error occured, so return false + } + + // Read Data + for( i = 0; i < 256; i++ ) + data.buf[i] = inw(base); + + // Return the disk size + if(data.identify.Sectors48 != 0) { + LEAVE('X', data.identify.Sectors48); + return data.identify.Sectors48; + } + else { + LEAVE('x', data.identify.Sectors28); + return data.identify.Sectors28; + } +} + +/** + * \fn Uint16 ATA_GetPortBase(int Disk) + * \brief Returns the base port for a given disk + */ +Uint16 ATA_GetBasePort(int Disk) +{ + switch(Disk) + { + case 0: case 1: return IDE_PRI_BASE; + case 2: case 3: return IDE_SEC_BASE; + } + return 0; +} + +/** + * \fn int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer) + * \return Boolean Failure + */ +int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer) +{ + int cont = (Disk>>1)&1; // Controller ID + int disk = Disk & 1; + Uint16 base; + Sint64 timeoutTime; + + ENTER("iDisk XAddress iCount pBuffer", Disk, Address, Count, Buffer); + + // Check if the count is small enough + if(Count > MAX_DMA_SECTORS) { + Log_Warning("ATA", "Passed too many sectors for a bulk DMA read (%i > %i)", + Count, MAX_DMA_SECTORS); + LEAVE('i'); + return 1; + } + + // Hack to make debug hexdump noticable + #if 1 + memset(Buffer, 0xFF, Count*SECTOR_SIZE); + #endif + + // Get exclusive access to the disk controller + Mutex_Acquire( &glaATA_ControllerLock[ cont ] ); + + // Set Size + gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE; + + // Get Port Base + base = ATA_GetBasePort(Disk); + + // Reset IRQ Flag + gaATA_IRQs[cont] = 0; + + #if 1 + if( cont == 0 ) { + outb(IDE_PRI_CTRL, 4); + IO_DELAY(); + outb(IDE_PRI_CTRL, 0); + } + else { + outb(IDE_SEC_CTRL, 4); + IO_DELAY(); + outb(IDE_SEC_CTRL, 0); + } + #endif + + // Set up transfer + if( Address > 0x0FFFFFFF ) // Use LBA48 + { + outb(base+0x6, 0x40 | (disk << 4)); + IO_DELAY(); + outb(base+0x2, 0 >> 8); // Upper Sector Count + outb(base+0x3, Address >> 24); // Low 2 Addr + outb(base+0x4, Address >> 28); // Mid 2 Addr + outb(base+0x5, Address >> 32); // High 2 Addr + } + else + { + // Magic, Disk, High Address nibble + outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F)); + //outb(base+0x06, 0xA0 | (disk << 4) | ((Address >> 24) & 0x0F)); + IO_DELAY(); + } + + //outb(base+0x01, 0x01); //? + outb(base+0x02, Count & 0xFF); // Sector Count + outb(base+0x03, Address & 0xFF); // Low Addr + outb(base+0x04, (Address >> 8) & 0xFF); // Middle Addr + outb(base+0x05, (Address >> 16) & 0xFF); // High Addr + + LOG("Starting Transfer"); + + // HACK: Ensure the PRDT is reset + ATA_int_BusMasterWriteDWord(cont*8+4, gaATA_PRDT_PAddrs[cont]); + ATA_int_BusMasterWriteByte(cont*8, 4); // Reset IRQ + + LOG("gATA_PRDTs[%i].Bytes = %i", cont, gATA_PRDTs[cont].Bytes); + if( Address > 0x0FFFFFFF ) + outb(base+0x07, HDD_DMA_R48); // Read Command (LBA48) + else + outb(base+0x07, HDD_DMA_R28); // Read Command (LBA28) + + // Start transfer + ATA_int_BusMasterWriteByte( cont * 8, 9 ); // Read and start + + // Wait for transfer to complete + timeoutTime = now() + ATA_TIMEOUT; + while( gaATA_IRQs[cont] == 0 && now() < timeoutTime) + { + HALT(); + } + + // Complete Transfer + ATA_int_BusMasterWriteByte( cont * 8, 8 ); // Read and stop + + #if DEBUG + { + Uint8 val = inb(base+0x7); + LOG("Status byte = 0x%02x, Controller Status = 0x%02x", + val, ATA_int_BusMasterReadByte(cont * 8 + 2)); + } + #else + inb(base+0x7); + #endif + + if( gaATA_IRQs[cont] == 0 ) + { + if( ATA_int_BusMasterReadByte(cont * 8 + 2) & 0x4 ) { + Log_Error("ATA", "BM Status reports an interrupt, but none recieved"); + ATA_int_BusMasterWriteByte(cont*8 + 2, 4); // Clear interrupt + memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE ); + Mutex_Release( &glaATA_ControllerLock[ cont ] ); + LEAVE('i', 0); + return 0; + } + + #if 1 + Debug_HexDump("ATA", Buffer, 512); + #endif + + // Release controller lock + Mutex_Release( &glaATA_ControllerLock[ cont ] ); + Log_Warning("ATA", + "Read timeout on disk %i (Reading sector 0x%llx)", + Disk, Address); + // Return error + LEAVE('i', 1); + return 1; + } + else { + LOG("Transfer Completed & Acknowledged"); + // Copy to destination buffer + memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE ); + // Release controller lock + Mutex_Release( &glaATA_ControllerLock[ cont ] ); + + LEAVE('i', 0); + return 0; + } +} + +/** + * \fn int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer) + * \brief Write up to \a MAX_DMA_SECTORS to a disk + * \param Disk Disk ID to write to + * \param Address LBA of first sector + * \param Count Number of sectors to write (must be >= \a MAX_DMA_SECTORS) + * \param Buffer Source buffer for data + * \return Boolean Failure + */ +int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer) +{ + int cont = (Disk>>1)&1; // Controller ID + int disk = Disk & 1; + Uint16 base; + Sint64 timeoutTime; + + // Check if the count is small enough + if(Count > MAX_DMA_SECTORS) return 1; + + // Get exclusive access to the disk controller + Mutex_Acquire( &glaATA_ControllerLock[ cont ] ); + + // Set Size + gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE; + + // Get Port Base + base = ATA_GetBasePort(Disk); + + // Reset IRQ Flag + gaATA_IRQs[cont] = 0; + + // Set up transfer + outb(base+0x01, 0x00); + if( Address > 0x0FFFFFFF ) // Use LBA48 + { + outb(base+0x6, 0x40 | (disk << 4)); + outb(base+0x2, 0 >> 8); // Upper Sector Count + outb(base+0x3, Address >> 24); // Low 2 Addr + outb(base+0x3, Address >> 28); // Mid 2 Addr + outb(base+0x3, Address >> 32); // High 2 Addr + } + else + { + // Magic, Disk, High Address nibble + outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F)); + } + + outb(base+0x02, (Uint8) Count); // Sector Count + outb(base+0x03, (Uint8) Address); // Low Addr + outb(base+0x04, (Uint8) (Address >> 8)); // Middle Addr + outb(base+0x05, (Uint8) (Address >> 16)); // High Addr + if( Address > 0x0FFFFFFF ) + outb(base+0x07, HDD_DMA_W48); // Write Command (LBA48) + else + outb(base+0x07, HDD_DMA_W28); // Write Command (LBA28) + + // Copy to output buffer + memcpy( gATA_Buffers[cont], Buffer, Count*SECTOR_SIZE ); + + // Start transfer + ATA_int_BusMasterWriteByte( cont << 3, 1 ); // Write and start + + // Wait for transfer to complete + timeoutTime = now() + ATA_TIMEOUT; + while( gaATA_IRQs[cont] == 0 && now() < timeoutTime) + { + HALT(); + } + + // Complete Transfer + ATA_int_BusMasterWriteByte( cont << 3, 0 ); // Write and stop + + // If the IRQ is unset, return error + if( gaATA_IRQs[cont] == 0 ) { + // Release controller lock + Mutex_Release( &glaATA_ControllerLock[ cont ] ); + return 1; // Error + } + else { + Mutex_Release( &glaATA_ControllerLock[ cont ] ); + return 0; + } +} + +/** + * \brief Primary ATA Channel IRQ handler + */ +void ATA_IRQHandlerPri(int UNUSED(IRQ), void *UNUSED(Ptr)) +{ + Uint8 val; + + // IRQ bit set for Primary Controller + val = ATA_int_BusMasterReadByte( 0x2 ); + LOG("IRQ val = 0x%x", val); + if(val & 4) { + LOG("IRQ hit (val = 0x%x)", val); + ATA_int_BusMasterWriteByte( 0x2, 4 ); + gaATA_IRQs[0] = 1; + return ; + } +} + +/** + * \brief Second ATA Channel IRQ handler + */ +void ATA_IRQHandlerSec(int UNUSED(IRQ), void *UNUSED(Ptr)) +{ + Uint8 val; + // IRQ bit set for Secondary Controller + val = ATA_int_BusMasterReadByte( 0xA ); + LOG("IRQ val = 0x%x", val); + if(val & 4) { + LOG("IRQ hit (val = 0x%x)", val); + ATA_int_BusMasterWriteByte( 0xA, 4 ); + gaATA_IRQs[1] = 1; + return ; + } +} + +/** + * \brief Read an 8-bit value from a Bus Master register + * \param Ofs Register offset + */ +Uint8 ATA_int_BusMasterReadByte(int Ofs) +{ + if( gATA_BusMasterBase & 1 ) + return inb( (gATA_BusMasterBase & ~1) + Ofs ); + else + return *(Uint8*)(gATA_BusMasterBasePtr + Ofs); +} + +/** + * \brief Read an 32-bit value from a Bus Master register + * \param Ofs Register offset + */ +Uint32 ATA_int_BusMasterReadDWord(int Ofs) +{ + if( gATA_BusMasterBase & 1 ) + return ind( (gATA_BusMasterBase & ~1) + Ofs ); + else + return *(Uint32*)(gATA_BusMasterBasePtr + Ofs); +} + +/** + * \brief Writes a byte to a Bus Master Register + * \param Ofs Register Offset + * \param Value Value to write + */ +void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value) +{ + if( gATA_BusMasterBase & 1 ) + outb( (gATA_BusMasterBase & ~1) + Ofs, Value ); + else + *(Uint8*)(gATA_BusMasterBasePtr + Ofs) = Value; +} + +/** + * \brief Writes a 32-bit value to a Bus Master Register + * \param Ofs Register offset + * \param Value Value to write + */ +void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value) +{ + if( gATA_BusMasterBase & 1 ) + outd( (gATA_BusMasterBase & ~1) + Ofs, Value ); + else + *(Uint32*)(gATA_BusMasterBasePtr + Ofs) = Value; +} diff --git a/KernelLand/Modules/Storage/ATA/main.c b/KernelLand/Modules/Storage/ATA/main.c new file mode 100644 index 00000000..41228d88 --- /dev/null +++ b/KernelLand/Modules/Storage/ATA/main.c @@ -0,0 +1,457 @@ +/* + * Acess2 IDE Harddisk Driver + * - main.c + */ +#define DEBUG 0 +#define VERSION 0x0032 +#include +#include +#include +#include +#include +#include +#include "common.h" + +// === MACROS === +#define IO_DELAY() do{inb(0x80); inb(0x80); inb(0x80); inb(0x80);}while(0) + +// === PROTOTYPES === + int ATA_Install(char **Arguments); +void ATA_SetupPartitions(void); +void ATA_SetupVFS(void); + int ATA_ScanDisk(int Disk); +void ATA_ParseGPT(int Disk); +void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length); +Uint16 ATA_GetBasePort(int Disk); +// Filesystem Interface +char *ATA_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *ATA_FindDir(tVFS_Node *Node, const char *Name); +Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); + int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data); +// Read/Write Interface/Quantiser +Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk); +Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, i386ATA, ATA_Install, NULL, "PCI", NULL); +tVFS_NodeType gATA_RootNodeType = { + .TypeName = "ATA Root Node", + .ReadDir = ATA_ReadDir, + .FindDir = ATA_FindDir + }; +tVFS_NodeType gATA_DiskNodeType = { + .TypeName = "ATA Volume", + .Read = ATA_ReadFS, + .Write = ATA_WriteFS, + .IOCtl = ATA_IOCtl + }; +tDevFS_Driver gATA_DriverInfo = { + NULL, "ata", + { + .NumACLs = 1, + .Size = -1, + .Flags = VFS_FFLAG_DIRECTORY, + .ACLs = &gVFS_ACL_EveryoneRX, + .Type = &gATA_RootNodeType + } +}; +tATA_Disk gATA_Disks[MAX_ATA_DISKS]; + int giATA_NumNodes; +tVFS_Node **gATA_Nodes; + +// === CODE === +/** + * \brief Initialise the ATA driver + */ +int ATA_Install(char **Arguments) +{ + int ret; + + ret = ATA_SetupIO(); + if(ret) return ret; + + ATA_SetupPartitions(); + + ATA_SetupVFS(); + + if( DevFS_AddDevice( &gATA_DriverInfo ) == 0 ) + return MODULE_ERR_MISC; + + return MODULE_ERR_OK; +} + +/** + * \brief Scan all disks, looking for partitions + */ +void ATA_SetupPartitions(void) +{ + int i; + for( i = 0; i < MAX_ATA_DISKS; i ++ ) + { + if( !ATA_ScanDisk(i) ) { + gATA_Disks[i].Name[0] = '\0'; // Mark as unused + continue; + } + } +} + +/** + * \brief Sets up the ATA drivers VFS information and registers with DevFS + */ +void ATA_SetupVFS(void) +{ + int i, j, k; + + // Count number of nodes needed + giATA_NumNodes = 0; + for( i = 0; i < MAX_ATA_DISKS; i++ ) + { + if(gATA_Disks[i].Name[0] == '\0') continue; // Ignore + giATA_NumNodes ++; + giATA_NumNodes += gATA_Disks[i].NumPartitions; + } + + // Allocate Node space + gATA_Nodes = malloc( giATA_NumNodes * sizeof(void*) ); + + // Set nodes + k = 0; + for( i = 0; i < MAX_ATA_DISKS; i++ ) + { + if(gATA_Disks[i].Name[0] == '\0') continue; // Ignore + gATA_Nodes[ k++ ] = &gATA_Disks[i].Node; + for( j = 0; j < gATA_Disks[i].NumPartitions; j ++ ) + gATA_Nodes[ k++ ] = &gATA_Disks[i].Partitions[j].Node; + } + + gATA_DriverInfo.RootNode.Size = giATA_NumNodes; +} + +/** + * \brief Scan a disk, getting the size and any paritions + * \param Disk Disk ID to scan + */ +int ATA_ScanDisk(int Disk) +{ + tVFS_Node *node; + tMBR mbr; + + ENTER("iDisk", Disk); + + // Get the disk size + gATA_Disks[ Disk ].Sectors = ATA_GetDiskSize(Disk); + if(gATA_Disks[ Disk ].Sectors == 0) + { + LEAVE('i', 0); + return 0; + } + + LOG("gATA_Disks[ %i ].Sectors = 0x%x", Disk, gATA_Disks[ Disk ].Sectors); + + // Create Name + gATA_Disks[ Disk ].Name[0] = 'A'+Disk; + gATA_Disks[ Disk ].Name[1] = '\0'; + + #if 1 + { + Uint64 val = gATA_Disks[ Disk ].Sectors / 2; + char *units = "KiB"; + if( val > 4*1024 ) { + val /= 1024; + units = "MiB"; + } + else if( val > 4*1024 ) { + val /= 1024; + units = "GiB"; + } + else if( val > 4*1024 ) { + val /= 1024; + units = "TiB"; + } + Log_Notice("ATA", "Disk %s: 0x%llx Sectors (%lli %s)", + gATA_Disks[ Disk ].Name, gATA_Disks[ Disk ].Sectors, val, units); + } + #endif + + // Get pointer to vfs node and populate it + node = &gATA_Disks[ Disk ].Node; + node->Size = gATA_Disks[Disk].Sectors * SECTOR_SIZE; + node->NumACLs = 0; // Means Superuser only can access it + node->Inode = (Disk << 8) | 0xFF; + node->ImplPtr = gATA_Disks[ Disk ].Name; + + node->ATime = node->MTime = node->CTime = now(); + + node->Type = &gATA_DiskNodeType; + + // --- Scan Partitions --- + LOG("Reading MBR"); + // Read Boot Sector + if( ATA_ReadDMA( Disk, 0, 1, &mbr ) != 0 ) { + Log_Warning("ATA", "Error in reading MBR on %i", Disk); + LEAVE('i', 0); + return 0; + } + + // Check for a GPT table + if(mbr.Parts[0].SystemID == 0xEE) + ATA_ParseGPT(Disk); + else // No? Just parse the MBR + ATA_ParseMBR(Disk, &mbr); + + #if DEBUG >= 2 + ATA_ReadDMA( Disk, 1, 1, &mbr ); + Debug_HexDump("ATA_ScanDisk", &mbr, 512); + #endif + + LEAVE('i', 1); + return 1; +} + +/** + * \fn void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length) + * \brief Fills a parition's information structure + */ +void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length) +{ + ENTER("pPart iDisk iNum XStart XLength", Part, Disk, Num, Start, Length); + Part->Start = Start; + Part->Length = Length; + Part->Name[0] = 'A'+Disk; + if(Num >= 10) { + Part->Name[1] = '1'+Num/10; + Part->Name[2] = '1'+Num%10; + Part->Name[3] = '\0'; + } else { + Part->Name[1] = '1'+Num; + Part->Name[2] = '\0'; + } + Part->Node.NumACLs = 0; // Only root can read/write raw block devices + Part->Node.Inode = (Disk << 8) | Num; + Part->Node.ImplPtr = Part->Name; + + Part->Node.Type = &gATA_DiskNodeType; + Log_Notice("ATA", "Partition %s at 0x%llx+0x%llx", Part->Name, Part->Start, Part->Length); + LOG("Made '%s' (&Node=%p)", Part->Name, &Part->Node); + LEAVE('-'); +} + +/** + * \fn void ATA_ParseGPT(int Disk) + * \brief Parses the GUID Partition Table + */ +void ATA_ParseGPT(int Disk) +{ + ///\todo Support GPT Disks + Warning("GPT Disks are currently unsupported (Disk %i)", Disk); +} + +/** + * \fn char *ATA_ReadDir(tVFS_Node *Node, int Pos) + */ +char *ATA_ReadDir(tVFS_Node *UNUSED(Node), int Pos) +{ + if(Pos >= giATA_NumNodes || Pos < 0) return NULL; + return strdup( gATA_Nodes[Pos]->ImplPtr ); +} + +/** + * \fn tVFS_Node *ATA_FindDir(tVFS_Node *Node, const char *Name) + */ +tVFS_Node *ATA_FindDir(tVFS_Node *UNUSED(Node), const char *Name) +{ + int part; + tATA_Disk *disk; + + // Check first character + if(Name[0] < 'A' || Name[0] > 'A'+MAX_ATA_DISKS) + return NULL; + disk = &gATA_Disks[Name[0]-'A']; + // Raw Disk + if(Name[1] == '\0') { + if( disk->Sectors == 0 && disk->Name[0] == '\0') + return NULL; + return &disk->Node; + } + + // Partitions + if(Name[1] < '0' || '9' < Name[1]) return NULL; + if(Name[2] == '\0') { // <= 9 + part = Name[1] - '0'; + part --; + return &disk->Partitions[part].Node; + } + // > 9 + if('0' > Name[2] || '9' < Name[2]) return NULL; + if(Name[3] != '\0') return NULL; + + part = (Name[1] - '0') * 10; + part += Name[2] - '0'; + part --; + return &disk->Partitions[part].Node; + +} + +/** + * \fn Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + */ +Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + int disk = Node->Inode >> 8; + int part = Node->Inode & 0xFF; + + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + + // Raw Disk Access + if(part == 0xFF) + { + if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE ) { + LEAVE('i', 0); + return 0; + } + if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE ) + Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset; + } + // Partition + else + { + if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) { + LEAVE('i', 0); + return 0; + } + if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) + Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset; + Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE; + } + + { + int ret = DrvUtil_ReadBlock(Offset, Length, Buffer, ATA_ReadRaw, SECTOR_SIZE, disk); + //Log("ATA_ReadFS: disk=%i, Offset=%lli, Length=%lli", disk, Offset, Length); + //Debug_HexDump("ATA_ReadFS", Buffer, Length); + LEAVE('i', ret); + return ret; + } +} + +/** + * \fn Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) + */ +Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + int disk = Node->Inode >> 8; + int part = Node->Inode & 0xFF; + + // Raw Disk Access + if(part == 0xFF) + { + if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE ) + return 0; + if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE ) + Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset; + } + // Partition + else + { + if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) + return 0; + if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) + Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset; + Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE; + } + + Log("ATA_WriteFS: (Node=%p, Offset=0x%llx, Length=0x%llx, Buffer=%p)", Node, Offset, Length, Buffer); + Debug_HexDump("ATA_WriteFS", Buffer, Length); + return DrvUtil_WriteBlock(Offset, Length, Buffer, ATA_ReadRaw, ATA_WriteRaw, SECTOR_SIZE, disk); +} + +const char *csaATA_IOCtls[] = {DRV_IOCTLNAMES, DRV_DISK_IOCTLNAMES, NULL}; +/** + * \fn int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data) + * \brief IO Control Funtion + */ +int ATA_IOCtl(tVFS_Node *UNUSED(Node), int Id, void *Data) +{ + switch(Id) + { + BASE_IOCTLS(DRV_TYPE_DISK, "i386ATA", VERSION, csaATA_IOCtls); + + case DISK_IOCTL_GETBLOCKSIZE: + return 512; + + default: + return 0; + } + return 0; +} + +// --- Disk Access --- +/** + * \fn Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk) + */ +Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk) +{ + int ret; + Uint offset; + Uint done = 0; + + // Pass straight on to ATA_ReadDMAPage if we can + if(Count <= MAX_DMA_SECTORS) + { + ret = ATA_ReadDMA(Disk, Address, Count, Buffer); + if(ret == 0) return 0; + return Count; + } + + // Else we will have to break up the transfer + offset = 0; + while(Count > MAX_DMA_SECTORS) + { + ret = ATA_ReadDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset); + // Check for errors + if(ret != 1) return done; + // Change Position + done += MAX_DMA_SECTORS; + Count -= MAX_DMA_SECTORS; + offset += MAX_DMA_SECTORS*SECTOR_SIZE; + } + + ret = ATA_ReadDMA(Disk, Address+offset, Count, Buffer+offset); + if(ret != 1) return 0; + return done+Count; +} + +/** + * \fn Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk) + */ +Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk) +{ + int ret; + Uint offset; + Uint done = 0; + + // Pass straight on to ATA_WriteDMA, if we can + if(Count <= MAX_DMA_SECTORS) + { + ret = ATA_WriteDMA(Disk, Address, Count, Buffer); + if(ret == 0) return 0; + return Count; + } + + // Else we will have to break up the transfer + offset = 0; + while(Count > MAX_DMA_SECTORS) + { + ret = ATA_WriteDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset); + // Check for errors + if(ret != 1) return done; + // Change Position + done += MAX_DMA_SECTORS; + Count -= MAX_DMA_SECTORS; + offset += MAX_DMA_SECTORS*SECTOR_SIZE; + } + + ret = ATA_WriteDMA(Disk, Address+offset, Count, Buffer+offset); + if(ret != 1) return 0; + return done+Count; +} diff --git a/KernelLand/Modules/Storage/ATA/mbr.c b/KernelLand/Modules/Storage/ATA/mbr.c new file mode 100644 index 00000000..a294ca81 --- /dev/null +++ b/KernelLand/Modules/Storage/ATA/mbr.c @@ -0,0 +1,186 @@ +/* + * Acess2 IDE Harddisk Driver + * - MBR Parsing Code + * mbr.c + */ +#define DEBUG 0 +#include +#include "common.h" + +// === PROTOTYPES === +void ATA_ParseMBR(int Disk, tMBR *MBR); +Uint64 ATA_MBR_int_ReadExt(int Disk, Uint64 Addr, Uint64 *Base, Uint64 *Length); + +// === GLOBALS === + +// === CODE === +/** + * \fn void ATA_ParseMBR(int Disk, tMBR *MBR) + */ +void ATA_ParseMBR(int Disk, tMBR *MBR) +{ + int i, j = 0, k = 4; + Uint64 extendedLBA; + Uint64 base, len; + + ENTER("iDisk", Disk); + + // Count Partitions + gATA_Disks[Disk].NumPartitions = 0; + extendedLBA = 0; + for( i = 0; i < 4; i ++ ) + { + if( MBR->Parts[i].SystemID == 0 ) continue; + if( MBR->Parts[i].Boot == 0x0 || MBR->Parts[i].Boot == 0x80 // LBA 28 + || MBR->Parts[i].Boot == 0x1 || MBR->Parts[i].Boot == 0x81 // LBA 48 + ) + { + if( MBR->Parts[i].SystemID == 0xF || MBR->Parts[i].SystemID == 5 ) { + LOG("Extended Partition at 0x%llx", MBR->Parts[i].LBAStart); + if(extendedLBA != 0) { + Warning("Disk %i has multiple extended partitions, ignoring rest", Disk); + continue; + } + extendedLBA = MBR->Parts[i].LBAStart; + continue; + } + LOG("Primary Partition at 0x%llx", MBR->Parts[i].LBAStart); + + gATA_Disks[Disk].NumPartitions ++; + continue; + } + // Invalid Partition, so don't count it + } + while(extendedLBA != 0) + { + extendedLBA = ATA_MBR_int_ReadExt(Disk, extendedLBA, &base, &len); + if( extendedLBA == -1 ) break; + gATA_Disks[Disk].NumPartitions ++; + } + LOG("gATA_Disks[Disk].NumPartitions = %i", gATA_Disks[Disk].NumPartitions); + + // Create patition array + gATA_Disks[Disk].Partitions = malloc( gATA_Disks[Disk].NumPartitions * sizeof(tATA_Partition) ); + + // --- Fill Partition Info --- + extendedLBA = 0; + for( j = 0, i = 0; i < 4; i ++ ) + { + LOG("MBR->Parts[%i].SystemID = 0x%02x", i, MBR->Parts[i].SystemID); + if( MBR->Parts[i].SystemID == 0 ) continue; + if( MBR->Parts[i].Boot == 0x0 || MBR->Parts[i].Boot == 0x80 ) // LBA 28 + { + base = MBR->Parts[i].LBAStart; + len = MBR->Parts[i].LBALength; + } + else if( MBR->Parts[i].Boot == 0x1 || MBR->Parts[i].Boot == 0x81 ) // LBA 58 + { + base = (MBR->Parts[i].StartHi << 16) | MBR->Parts[i].LBAStart; + len = (MBR->Parts[i].LengthHi << 16) | MBR->Parts[i].LBALength; + } + else + continue; + + if( MBR->Parts[i].SystemID == 0xF || MBR->Parts[i].SystemID == 5 ) { + if(extendedLBA != 0) { + Log_Warning("ATA", "Disk %i has multiple extended partitions, ignoring rest", Disk); + continue; + } + extendedLBA = base; + continue; + } + // Create Partition + ATA_int_MakePartition( + &gATA_Disks[Disk].Partitions[j], Disk, j, + base, len + ); + j ++; + + } + // Scan extended partitions + while(extendedLBA != 0) + { + extendedLBA = ATA_MBR_int_ReadExt(Disk, extendedLBA, &base, &len); + if(extendedLBA == -1) break; + ATA_int_MakePartition( + &gATA_Disks[Disk].Partitions[j], Disk, k, base, len + ); + } + + LEAVE('-'); +} + +/** + * \brief Reads an extended partition + * \return LBA of next Extended, -1 on error, 0 for last + */ +Uint64 ATA_MBR_int_ReadExt(int Disk, Uint64 Addr, Uint64 *Base, Uint64 *Length) +{ + Uint64 link = 0; + int bFoundPart = 0;; + int i; + tMBR mbr; + Uint64 base, len; + + if( ATA_ReadDMA( Disk, Addr, 1, &mbr ) != 0 ) + return -1; // Stop on Errors + + for( i = 0; i < 4; i ++ ) + { + if( mbr.Parts[i].SystemID == 0 ) continue; + + // LBA 24 + if( mbr.Parts[i].Boot == 0x0 || mbr.Parts[i].Boot == 0x80 ) { + base = mbr.Parts[i].LBAStart; + len = mbr.Parts[i].LBALength; + } + // LBA 48 + else if( mbr.Parts[i].Boot == 0x1 || mbr.Parts[i].Boot == 0x81 ) { + base = (mbr.Parts[i].StartHi << 16) | mbr.Parts[i].LBAStart; + len = (mbr.Parts[i].LengthHi << 16) | mbr.Parts[i].LBALength; + } + else { + Log_Warning("ATA MBR", + "Unknown partition type 0x%x, Disk %i Ext 0x%llx Part %i", + mbr.Parts[i].Boot, Disk, Addr, i + ); + return -1; + } + + switch(mbr.Parts[i].SystemID) + { + case 0xF: + case 0x5: + if(link != 0) { + Log_Warning("ATA MBR", + "Disk %i has two forward links in the extended partition", + Disk + ); + return -1; + } + link = base; + break; + default: + if(bFoundPart) { + Warning("ATA MBR", + "Disk %i has more than one partition in the extended partition at 0x%llx", + Disk, Addr + ); + return -1; + } + bFoundPart = 1; + *Base = base; + *Length = len; + break; + } + } + + if(!bFoundPart) { + Log_Warning("ATA MBR", + "No partition in extended partiton, Disk %i 0x%llx", + Disk, Addr); + return -1; + } + + return link; +} diff --git a/KernelLand/Modules/Storage/FDD/Makefile b/KernelLand/Modules/Storage/FDD/Makefile new file mode 100644 index 00000000..15965531 --- /dev/null +++ b/KernelLand/Modules/Storage/FDD/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = fdd.o +NAME = FDD + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Storage/FDD/fdd.c b/KernelLand/Modules/Storage/FDD/fdd.c new file mode 100644 index 00000000..9cf808d2 --- /dev/null +++ b/KernelLand/Modules/Storage/FDD/fdd.c @@ -0,0 +1,957 @@ +/* + * AcessOS 0.1 + * Floppy Disk Access Code + */ +#define DEBUG 0 +#include +#include +#include +#include +#include +#include + +#define WARN 0 + +// === CONSTANTS === +// --- Current Version +#define FDD_VERSION ((0<<8)|(75)) + +// --- Options +#define FDD_SEEK_TIMEOUT 10 // Timeout for a seek operation +#define MOTOR_ON_DELAY 500 // Miliseconds +#define MOTOR_OFF_DELAY 2000 // Miliseconds +#define FDD_MAX_READWRITE_ATTEMPTS 16 + +// === TYPEDEFS === +/** + * \brief Representation of a floppy drive + */ +typedef struct sFloppyDrive +{ + int type; + volatile int motorState; //2 - On, 1 - Spinup, 0 - Off + int track[2]; + int timer; + tVFS_Node Node; + #if !USE_CACHE + tIOCache *CacheHandle; + #endif +} t_floppyDevice; + +/** + * \brief Cached Sector + */ +typedef struct { + Uint64 timestamp; + Uint16 disk; + Uint16 sector; // Allows 32Mb of addressable space (Plenty for FDD) + Uint8 data[512]; +} t_floppySector; + +// === CONSTANTS === +static const char *cFDD_TYPES[] = {"None", "360kB 5.25\"", "1.2MB 5.25\"", "720kB 3.5\"", "1.44MB 3.5\"", "2.88MB 3.5\"" }; +static const int cFDD_SIZES[] = { 0, 360*1024, 1200*1024, 720*1024, 1440*1024, 2880*1024 }; +static const short cPORTBASE[] = { 0x3F0, 0x370 }; +#if DEBUG +static const char *cFDD_STATUSES[] = {NULL, "Error", "Invalid command", "Drive not ready"}; +#endif + +enum FloppyPorts { + PORT_STATUSA = 0x0, + PORT_STATUSB = 0x1, + PORT_DIGOUTPUT = 0x2, + PORT_MAINSTATUS = 0x4, + PORT_DATARATE = 0x4, + PORT_DATA = 0x5, + PORT_DIGINPUT = 0x7, + PORT_CONFIGCTRL = 0x7 +}; + +#define CMD_FLAG_MULTI_TRACK 0x80 //!< Multitrack +#define CMD_FLAG_MFM_ENCODING 0x40 //!< MFM Encoding Mode (Always set for read/write/format/verify) +#define CMD_FLAG_SKIP_MODE 0x20 //!< Skip Mode (don't use) +enum FloppyCommands { + CMD_READ_TRACK = 0x02, + CMD_SPECIFY = 0x03, + CMD_SENSE_STATUS = 0x04, + CMD_WRITE_DATA = 0x05, + CMD_READ_DATA = 0x06, + CMD_RECALIBRATE = 0x07, + CMD_SENSE_INTERRUPT = 0x08, + CMD_WRITE_DEL_DATA = 0x09, + CMD_READ_SECTOR_ID = 0x0A, + // 0x0B - ? + CMD_READ_DEL_DATA = 0x0C, + CMD_FORMAT_TRACK = 0x0D, + // 0x0E - ? + CMD_SEEK_TRACK = 0x0F, + CMD_VERSION = 0x10, + + CMD_LOCK = 0x14, //!< Save controller parameters + + CMD_CONFIGURE = 0x13 +}; + +// === PROTOTYPES === +// --- Filesystem + int FDD_Install(char **Arguments); +void FDD_UnloadModule(); +// --- VFS Methods +char *FDD_ReadDir(tVFS_Node *Node, int pos); +tVFS_Node *FDD_FindDir(tVFS_Node *dirNode, const char *Name); + int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data); +Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); +// --- Functions for IOCache/DrvUtil +Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk); +// --- Raw Disk Access + int FDD_ReadSector(Uint32 disk, Uint64 lba, void *Buffer); + int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer); +// --- Helpers + int FDD_WaitIRQ(); +void FDD_IRQHandler(int Num, void *Ptr); +void FDD_SenseInt(int base, Uint8 *sr0, Uint8 *cyl); + + int FDD_Reset(int id); +void FDD_Recalibrate(int disk); + int FDD_Reconfigure(int ID); + + int FDD_int_SeekTrack(int disk, int head, int track); +void FDD_int_TimerCallback(void *Arg); +void FDD_int_StopMotor(int Disk); +void FDD_int_StopMotorCallback(void *Arg); +void FDD_int_StartMotor(int Disk); + int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt); + + int FDD_int_SendByte(int base, Uint8 Byte); + int FDD_int_GetByte(int base, Uint8 *Byte); + +// === GLOBALS === +MODULE_DEFINE(0, FDD_VERSION, FDD, FDD_Install, NULL, "x86_ISADMA", NULL); +t_floppyDevice gFDD_Devices[2]; +tMutex glFDD; +volatile int gbFDD_IrqFired = 0; +tDevFS_Driver gFDD_DriverInfo = { + NULL, "fdd", + { + .Size = -1, + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Flags = VFS_FFLAG_DIRECTORY, + .ReadDir = FDD_ReadDir, + .FindDir = FDD_FindDir, + .IOCtl = FDD_IOCtl + } +}; + +// === CODE === +/** + * \fn int FDD_Install(char **Arguments) + * \brief Installs floppy driver + */ +int FDD_Install(char **Arguments) +{ + Uint8 data; + char **args = Arguments; + + // Determine Floppy Types (From CMOS) + outb(0x70, 0x10); + data = inb(0x71); + gFDD_Devices[0].type = data >> 4; + gFDD_Devices[1].type = data & 0xF; + gFDD_Devices[0].track[0] = -1; + gFDD_Devices[1].track[1] = -1; + + Log_Log("FDD", "Detected Disk 0: %s and Disk 1: %s", cFDD_TYPES[data>>4], cFDD_TYPES[data&0xF]); + + if( data == 0 ) { + return MODULE_ERR_NOTNEEDED; + } + + // Handle arguments + if(args) { + for(;*args;args++) + { + if(strcmp(*args, "disable")==0) + return MODULE_ERR_NOTNEEDED; + } + } + + // Install IRQ6 Handler + IRQ_AddHandler(6, FDD_IRQHandler, NULL); + + // Ensure the FDD version is 0x90 + { + Uint8 tmp = 0; + FDD_int_SendByte(cPORTBASE[0], CMD_VERSION); + FDD_int_GetByte(cPORTBASE[0], &tmp); + if( tmp != 0x90 ) { + Log_Error("FDD", "Version(0x%2x) != 0x90", tmp); + return MODULE_ERR_NOTNEEDED; + } + } + + // Configure + FDD_Reconfigure(0); + + // Reset Primary FDD Controller + if( FDD_Reset(0) != 0 ) { + return MODULE_ERR_MISC; + } + + #if 0 + { + int retries; + // Recalibrate disks + LOG("Recalibrate disks (16x seek)"); + retries = 16; + while(FDD_int_SeekTrack(0, 0, 1) == 0 && retries --) + Threads_Yield(); // set track + if(retries < 0) LEAVE_RET('i', -1); + + retries = 16; + while(FDD_int_SeekTrack(0, 1, 1) == 0 && retries --) + Threads_Yield(); // set track + if(retries < 0) LEAVE_RET('i', -1); + } + #endif + + + // Initialise Root Node + gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime + = gFDD_DriverInfo.RootNode.ATime = now(); + + // Initialise Child Nodes + gFDD_Devices[0].Node.Inode = 0; + gFDD_Devices[0].Node.Flags = 0; + gFDD_Devices[0].Node.NumACLs = 0; + gFDD_Devices[0].Node.Read = FDD_ReadFS; + gFDD_Devices[0].Node.Write = NULL;//FDD_WriteFS; + memcpy(&gFDD_Devices[1].Node, &gFDD_Devices[0].Node, sizeof(tVFS_Node)); + + gFDD_Devices[1].Node.Inode = 1; + + // Set Lengths + gFDD_Devices[0].Node.Size = cFDD_SIZES[data >> 4]; + gFDD_Devices[1].Node.Size = cFDD_SIZES[data & 0xF]; + + // Create Sector Cache + if( cFDD_SIZES[data >> 4] ) + { + gFDD_Devices[0].CacheHandle = IOCache_Create( + FDD_WriteSector, 0, 512, + gFDD_Devices[0].Node.Size / (512*4) + ); // Cache is 1/4 the size of the disk + } + if( cFDD_SIZES[data & 15] ) + { + gFDD_Devices[1].CacheHandle = IOCache_Create( + FDD_WriteSector, 0, 512, + gFDD_Devices[1].Node.Size / (512*4) + ); // Cache is 1/4 the size of the disk + } + + // Register with devfs + DevFS_AddDevice(&gFDD_DriverInfo); + + return MODULE_ERR_OK; +} + +/** + * \brief Prepare the module for removal + */ +void FDD_UnloadModule() +{ + int i; + DevFS_DelDevice( &gFDD_DriverInfo ); + Mutex_Acquire(&glFDD); + for(i=0;i<4;i++) { + Time_RemoveTimer(gFDD_Devices[i].timer); + FDD_int_StopMotor(i); + } + Mutex_Release(&glFDD); + //IRQ_Clear(6); +} + +/** + * \fn char *FDD_ReadDir(tVFS_Node *Node, int pos) + * \brief Read Directory + */ +char *FDD_ReadDir(tVFS_Node *UNUSED(Node), int Pos) +{ + char name[2] = "0\0"; + + if(Pos >= 2 || Pos < 0) return NULL; + + if(gFDD_Devices[Pos].type == 0) return VFS_SKIP; + + name[0] += Pos; + + return strdup(name); +} + +/** + * \fn tVFS_Node *FDD_FindDir(tVFS_Node *Node, const char *filename); + * \brief Find File Routine (for vfs_node) + */ +tVFS_Node *FDD_FindDir(tVFS_Node *UNUSED(Node), const char *Filename) +{ + int i; + + ENTER("sFilename", Filename); + + // Sanity check string + if(Filename == NULL) { + LEAVE('n'); + return NULL; + } + + // Check string length (should be 1) + if(Filename[0] == '\0' || Filename[1] != '\0') { + LEAVE('n'); + return NULL; + } + + // Get First character + i = Filename[0] - '0'; + + // Check for 1st disk and if it is present return + if(i == 0 && gFDD_Devices[0].type != 0) { + LEAVE('p', &gFDD_Devices[0].Node); + return &gFDD_Devices[0].Node; + } + + // Check for 2nd disk and if it is present return + if(i == 1 && gFDD_Devices[1].type != 0) { + LEAVE('p', &gFDD_Devices[1].Node); + return &gFDD_Devices[1].Node; + } + + // Else return null + LEAVE('n'); + return NULL; +} + +static const char *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL}; +/** + * \fn int FDD_IOCtl(tVFS_Node *Node, int id, void *data) + * \brief Stub ioctl function + */ +int FDD_IOCtl(tVFS_Node *UNUSED(Node), int ID, void *Data) +{ + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_DISK, "FDD", FDD_VERSION, casIOCTLS); + + case DISK_IOCTL_GETBLOCKSIZE: return 512; + + default: + return 0; + } +} + +/** + * \fn Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Read Data from a disk +*/ +Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + int ret; + + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + + if(Node == NULL) { + LEAVE('i', -1); + return -1; + } + + if(Node->Inode != 0 && Node->Inode != 1) { + LEAVE('i', -1); + return -1; + } + + ret = DrvUtil_ReadBlock(Offset, Length, Buffer, FDD_ReadSectors, 512, Node->Inode); + LEAVE('i', ret); + return ret; +} + +/** + * \brief Reads \a Count contiguous sectors from a disk + * \param SectorAddr Address of the first sector + * \param Count Number of sectors to read + * \param Buffer Destination Buffer + * \param Disk Disk Number + * \return Number of sectors read + * \note Used as a ::DrvUtil_ReadBlock helper + */ +Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk) +{ + Uint ret = 0; + while(Count --) + { + if( FDD_ReadSector(Disk, SectorAddr, Buffer) != 1 ) + return ret; + + Buffer = (void*)( (tVAddr)Buffer + 512 ); + SectorAddr ++; + ret ++; + } + return ret; +} + +int FDD_int_ReadWriteSector(Uint32 Disk, Uint64 SectorAddr, int Write, void *Buffer) +{ + int cyl, head, sec; + int spt, base; + int i; + int lba = SectorAddr; + Uint8 st0=0, st1=0, st2=0, bps=0; // Status Values + + ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer); + + base = cPORTBASE[Disk >> 1]; + + LOG("Calculating Disk Dimensions"); + // Get CHS position + if(FDD_int_GetDims(gFDD_Devices[Disk].type, lba, &cyl, &head, &sec, &spt) != 1) + { + LEAVE('i', -1); + return -1; + } + LOG("Cyl=%i, Head=%i, Sector=%i", cyl, head, sec); + + // Start the motor + Mutex_Acquire(&glFDD); // Lock to stop the motor stopping on us + Time_RemoveTimer(gFDD_Devices[Disk].timer); // Remove Old Timer + // Start motor if needed + if(gFDD_Devices[Disk].motorState != 2) FDD_int_StartMotor(Disk); + Mutex_Release(&glFDD); + + // Wait for spinup + LOG("Wait for the motor to spin up"); + while(gFDD_Devices[Disk].motorState == 1) Threads_Yield(); + + LOG("Acquire Spinlock"); + Mutex_Acquire(&glFDD); + + // Read Data from DMA + LOG("Setting DMA for read"); + DMA_SetChannel(2, 512, !Write); // Read/Write 512 Bytes from channel 2 + + LOG("Sending command"); + + #define SENDB(__data) if(FDD_int_SendByte(base, __data)) { FDD_Reset(Disk >> 1); continue; } + + for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ ) + { + FDD_int_SeekTrack(Disk, head, cyl); + if( Write ) { + SENDB(CMD_WRITE_DATA|CMD_FLAG_MFM_ENCODING); + } + else { + SENDB(CMD_READ_DATA|CMD_FLAG_MFM_ENCODING); + } + SENDB( (head << 2) | (Disk&1) ); + SENDB(cyl & 0xFF); + SENDB(head & 0xFF); + SENDB(sec & 0xFF); + SENDB(0x02); // Bytes Per Sector (Real BPS=128*2^{val}) + SENDB(spt); // SPT + SENDB(0x1B); // Gap Length (27 is default) + SENDB(0xFF); // Data Length + + // Wait for IRQ + if( Write ) { + LOG("Writing Data"); + DMA_WriteData(2, 512, Buffer); + LOG("Waiting for Data to be written"); + if( FDD_WaitIRQ() ) { FDD_Reset(Disk>>1); continue; } + } + else { + LOG("Waiting for data to be read"); + if( FDD_WaitIRQ() ) { FDD_Reset(Disk>>1); continue; } + LOG("Reading Data"); + DMA_ReadData(2, 512, Buffer); + } + + // Clear Input Buffer + LOG("Clearing Input Buffer"); + // Status Values + FDD_int_GetByte(base, &st0); + FDD_int_GetByte(base, &st1); + FDD_int_GetByte(base, &st2); + + // Cylinder, Head and Sector (mutilated in some way) + FDD_int_GetByte(base, NULL); // Cylinder + FDD_int_GetByte(base, NULL); // Head + FDD_int_GetByte(base, NULL); // Sector + // Should be the BPS set above (0x02) + FDD_int_GetByte(base, &bps); + + // Check Status + // - Error Code + if(st0 & 0xC0) { + LOG("Error (st0 & 0xC0) \"%s\"", cFDD_STATUSES[st0 >> 6]); + continue; + } + // - Status Flags + if(st0 & 0x08) { LOG("Drive not ready"); continue; } + if(st1 & 0x80) { LOG("End of Cylinder"); continue; } + if(st1 & 0x20) { LOG("CRC Error"); continue; } + if(st1 & 0x10) { LOG("Controller Timeout"); continue; } + if(st1 & 0x04) { LOG("No Data Found"); continue; } + if(st1 & 0x01 || st2 & 0x01) { + LOG("No Address mark found"); + continue; + } + if(st2 & 0x40) { LOG("Deleted address mark"); continue; } + if(st2 & 0x20) { LOG("CRC error in data"); continue; } + if(st2 & 0x10) { LOG("Wrong Cylinder"); continue; } + if(st2 & 0x04) { LOG("uPD765 sector not found"); continue; } + if(st2 & 0x02) { LOG("Bad Cylinder"); continue; } + + if(bps != 0x2) { + LOG("Returned BPS = 0x%02x, not 0x02", bps); + continue; + } + + if(st1 & 0x02) { + LOG("Floppy not writable"); + // Return error without triggering the attempt count check + i = FDD_MAX_READWRITE_ATTEMPTS+1; + break; + } + + // Success! + break; + } + #undef SENDB + + // Release Spinlock + LOG("Realeasing Spinlock and setting motor to stop"); + Mutex_Release(&glFDD); + + if(i == FDD_MAX_READWRITE_ATTEMPTS) { + Log_Warning("FDD", "Exceeded %i attempts in %s the disk", + FDD_MAX_READWRITE_ATTEMPTS, + (Write ? "writing to" : "reading from") + ); + } + + // Don't turn the motor off now, wait for a while + FDD_int_StopMotor(Disk); + + // Error check + if( i < FDD_MAX_READWRITE_ATTEMPTS ) { + LEAVE('i', 0); + return 0; + } + else { + LEAVE('i', 1); + return 1; + } +} + +/** + * \fn int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer) + * \brief Read a sector from disk + * \todo Make real-hardware safe (account for read errors) + */ +int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer) +{ + int ret; + + ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer); + + if( IOCache_Read( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer ) == 1 ) { + LEAVE('i', 1); + return 1; + } + + // Pass to general function + ret = FDD_int_ReadWriteSector(Disk, SectorAddr, 0, Buffer); + + if( ret == 0 ) { + IOCache_Add( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer ); + LEAVE('i', 1); + return 1; + } + else { + LOG("Reading failed"); + LEAVE('i', 0); + return 0; + } +} + +/** + * \fn int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer) + * \brief Write a sector to the floppy disk + * \note Not Implemented + */ +int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer) +{ + Log_Warning("FDD", "Read Only at the moment"); + return -1; +} + +/** + * \brief Seek disk to selected track + */ +int FDD_int_SeekTrack(int disk, int head, int track) +{ + Uint8 sr0=0, cyl=0; + int base, i, bUnclean; + + base = cPORTBASE[disk>>1]; + + // Check if seeking is needed + if(gFDD_Devices[disk].track[head] == track) + return 1; + + // - Seek Head 0 + for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ ) + { + if(i && bUnclean) FDD_Reset(disk >> 1); + bUnclean = 1; + if(FDD_int_SendByte(base, CMD_SEEK_TRACK)) continue; + if(FDD_int_SendByte(base, (head<<2)|(disk&1))) continue; + if(FDD_int_SendByte(base, track)) continue; + FDD_WaitIRQ(); + FDD_SenseInt(base, &sr0, &cyl); // Wait for IRQ + + bUnclean = 0; + if( cyl != track ) + continue; // Try again + if( (sr0 & 0xF0) != 0x20 ) { + LOG("sr0 = 0x%x", sr0); + continue ; + } + + break; + } + + if( i == FDD_MAX_READWRITE_ATTEMPTS ) { + Log_Warning("FDD", "Unable to seek to track %i on disk %i", + track, disk); + return 0; + } + + // Set Track in structure + gFDD_Devices[disk].track[head] = track; + + LOG("Time_Delay(100)"); + // Wait for Head to settle + Time_Delay(100); + + return 1; +} + +/** + * \fn int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt) + * \brief Get Dimensions of a disk + */ +int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt) +{ + switch(type) { + case 0: + return 0; + + // 360Kb 5.25" + case 1: + *spt = 9; + *s = (lba % 9) + 1; + *c = lba / 18; + *h = (lba / 9) & 1; + break; + + // 1220Kb 5.25" + case 2: + *spt = 15; + *s = (lba % 15) + 1; + *c = lba / 30; + *h = (lba / 15) & 1; + break; + + // 720Kb 3.5" + case 3: + *spt = 9; + *s = (lba % 9) + 1; + *c = lba / 18; + *h = (lba / 9) & 1; + break; + + // 1440Kb 3.5" + case 4: + *spt = 18; + *s = (lba % 18) + 1; + *c = lba / 36; + *h = (lba / 18) & 1; + //Log("1440k - lba=%i(0x%x), *s=%i,*c=%i,*h=%i", lba, lba, *s, *c, *h); + break; + + // 2880Kb 3.5" + case 5: + *spt = 36; + *s = (lba % 36) + 1; + *c = lba / 72; + *h = (lba / 32) & 1; + break; + + default: + return -2; + } + return 1; +} + +/** + * \fn void FDD_IRQHandler(int Num) + * \brief Handles IRQ6 + */ +void FDD_IRQHandler(int Num, void *Ptr) +{ + gbFDD_IrqFired = 1; +} + +/** + * \brief Wait for the FDD IRQ to fire + * \return Boolean failure (1 for timeout) + */ +inline int FDD_WaitIRQ() +{ + tTime end = now() + 2000; + + // Wait for IRQ + while(!gbFDD_IrqFired && now() < end) + Threads_Yield(); + + if( !gbFDD_IrqFired ) { + Log_Warning("FDD", "FDD_WaitIRQ - Timeout"); + return 1; + } + + gbFDD_IrqFired = 0; + return 0; +} + +void FDD_SenseInt(int base, Uint8 *sr0, Uint8 *cyl) +{ + FDD_int_SendByte(base, CMD_SENSE_INTERRUPT); + FDD_int_GetByte(base, sr0); + FDD_int_GetByte(base, cyl); +} + +/** + * void FDD_int_SendByte(int base, char byte) + * \brief Sends a command to the controller + */ +int FDD_int_SendByte(int base, Uint8 byte) +{ + tTime end = now() + 1000; // 1s + + while( (inb(base + PORT_MAINSTATUS) & 0x80) != 0x80 && now() < end ) + Threads_Yield(); //Delay +// Time_Delay(10); //Delay + + if( inb(base + PORT_MAINSTATUS) & 0x40 ) { + Log_Warning("FDD", "FDD_int_SendByte: DIO set, is this ok?"); + return -2; + } + + if( now() > end ) + { + Log_Warning("FDD", "FDD_int_SendByte: Timeout sending byte 0x%x to base 0x%x", byte, base); + return 1; + } + outb(base + PORT_DATA, byte); +// Log_Debug("FDD", "FDD_int_SendByte: Sent 0x%02x to 0x%x", byte, base); + return 0; +} + +/** + * int FDD_int_GetByte(int base, char byte) + * \brief Receive data from fdd controller + */ +int FDD_int_GetByte(int base, Uint8 *value) +{ + tTime end = now() + 1000; // 1s + + while( (inb(base + PORT_MAINSTATUS) & 0x80) != 0x80 && now() < end ) + Time_Delay(10); + + if( !(inb(base + PORT_MAINSTATUS) & 0x40) ) { + Log_Warning("FDD", "FDD_int_GetByte: DIO unset, is this ok?"); + return -2; + } + + if( now() > end ) + { + Log_Warning("FDD", "FDD_int_GetByte: Timeout reading byte from base 0x%x", base); + return -1; + } + + if(value) + *value = inb(base + PORT_DATA); + else + inb(base + PORT_DATA); + return 0; +} + +/** + * \brief Recalibrate the specified disk + */ +void FDD_Recalibrate(int disk) +{ + ENTER("idisk", disk); + + LOG("Starting Motor"); + FDD_int_StartMotor(disk); + // Wait for Spinup + while(gFDD_Devices[disk].motorState <= 1) Threads_Yield(); + + LOG("Sending Calibrate Command"); + FDD_int_SendByte(cPORTBASE[disk>>1], CMD_RECALIBRATE); + FDD_int_SendByte(cPORTBASE[disk>>1], disk&1); + + LOG("Waiting for IRQ"); + FDD_WaitIRQ(); + FDD_SenseInt(cPORTBASE[disk>>1], NULL, NULL); + + LOG("Stopping Motor"); + FDD_int_StopMotor(disk); + LEAVE('-'); +} + +/** + * \brief Reconfigure the controller + */ +int FDD_Reconfigure(int ID) +{ + Uint16 base = cPORTBASE[ID]; + + ENTER("iID", ID); + + FDD_int_SendByte(base, CMD_CONFIGURE); + FDD_int_SendByte(base, 0); + // Implied seek enabled, FIFO Enabled, Drive Polling Disabled, data buffer threshold 8 bytes + FDD_int_SendByte(base, (1 << 6) | (0 << 5) | (0 << 4) | 7); + FDD_int_SendByte(base, 0); // Precompensation - use default + + // Commit + FDD_int_SendByte(base, CMD_LOCK|CMD_FLAG_MULTI_TRACK); + FDD_int_GetByte(base, NULL); + + LEAVE('i', 0); + return 0; +} + +/** + * \brief Reset the specified FDD controller + */ +int FDD_Reset(int id) +{ + Uint16 base = cPORTBASE[id]; + Uint8 motor_state; + + ENTER("iID", id); + + // Reset the card + motor_state = inb(base + PORT_DIGOUTPUT) & 0xF0; + outb(base + PORT_DIGOUTPUT, motor_state|0); // Disable FDC + Time_Delay(1); + outb(base + PORT_DIGOUTPUT, motor_state|8|4); // Re-enable FDC (DMA and Enable) + + // Set the data rate + outb(base + PORT_DATARATE, 0); // Set data rate to 500K/s + + // Wait for IRQ + LOG("Awaiting IRQ"); + + FDD_WaitIRQ(); + + FDD_SenseInt(base, NULL, NULL); + + // Specify + FDD_int_SendByte(base, CMD_SPECIFY); // Step and Head Load Times + FDD_int_SendByte(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each) + FDD_int_SendByte(base, 0x02); // Head Load Time >> 1 + + LOG("Recalibrating Disk"); + FDD_Recalibrate((id<<1)|0); + FDD_Recalibrate((id<<1)|1); + + LEAVE_RET('i', 0); +} + +/** + * \fn void FDD_int_TimerCallback() + * \brief Called by timer + */ +void FDD_int_TimerCallback(void *Arg) +{ + int disk = (Uint)Arg; + ENTER("iarg", disk); + if(gFDD_Devices[disk].motorState == 1) + gFDD_Devices[disk].motorState = 2; + Time_RemoveTimer(gFDD_Devices[disk].timer); + gFDD_Devices[disk].timer = -1; + LEAVE('-'); +} + +/** + * \fn void FDD_int_StartMotor(char disk) + * \brief Starts FDD Motor + */ +void FDD_int_StartMotor(int disk) +{ + Uint8 state; + if( gFDD_Devices[disk].motorState != 0 ) return ; + // Set motor ON bit + state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT ); + state |= 1 << (4+disk); + outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state ); + // Mark as spinning up + gFDD_Devices[disk].motorState = 1; + // Schedule a timer for when it's up to speed + gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_ON_DELAY, FDD_int_TimerCallback, (void*)(Uint)disk); +} + +/** + * \brief Schedule the drive motor to stop + * \param Disk Disk number to stop + */ +void FDD_int_StopMotor(int Disk) +{ + // Ignore if the motor is aready off + if( gFDD_Devices[Disk].motorState == 0 ) return ; + + // Don't double-schedule timer + if( gFDD_Devices[Disk].timer != -1 ) + { + Time_RemoveTimer( gFDD_Devices[Disk].timer ); + } + + gFDD_Devices[Disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotorCallback, (void*)(Uint)Disk); +} + +/** + * \brief Stops FDD Motor + */ +void FDD_int_StopMotorCallback(void *Arg) +{ + Uint8 state, disk = (Uint)Arg; + + // Mutex is only locked if disk is in use + if( Mutex_IsLocked(&glFDD) ) return ; + + ENTER("iDisk", disk); + + // Clear motor on bit + state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT ); + state &= ~( 1 << (4+disk) ); + outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state ); + + // Mark as off + gFDD_Devices[disk].motorState = 0; + + LEAVE('-'); +} + diff --git a/KernelLand/Modules/Storage/FDDv2/Makefile b/KernelLand/Modules/Storage/FDDv2/Makefile new file mode 100644 index 00000000..97aae0c4 --- /dev/null +++ b/KernelLand/Modules/Storage/FDDv2/Makefile @@ -0,0 +1,8 @@ +# +# + +DEPS = x86/ISADMA +OBJ = main.o fdc.o +NAME = FDDv2 + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Storage/FDDv2/common.h b/KernelLand/Modules/Storage/FDDv2/common.h new file mode 100644 index 00000000..4d6a4f7e --- /dev/null +++ b/KernelLand/Modules/Storage/FDDv2/common.h @@ -0,0 +1,43 @@ +/* + * Acess2 82077AA FDC + * - By John Hodge (thePowersGang) + * + * common.h + * - Common definitions + */ +#ifndef _FDC_COMMON_H_ +#define _FDC_COMMON_H_ + +#include +#include + +// === CONSTANTS === +#define MAX_DISKS 8 // 4 per controller, 2 controllers +#define TRACKS_PER_DISK (1440*2/18) +#define BYTES_PER_TRACK (18*512) + +// === TYPEDEFS === +typedef struct sFDD_Drive tDrive; + +// === STRUCTURES === +struct sFDD_Drive +{ + int bValid; + int bInserted; + int MotorState; + tTimer *Timer; + + tMutex Mutex; + + void *TrackData[TRACKS_PER_DISK]; // Whole tracks are read +}; + +// === FUNCTIONS === +extern int FDD_SetupIO(void); +extern int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer); + +// === GLOBALS === +extern tDrive gaFDD_Disks[MAX_DISKS]; + +#endif + diff --git a/KernelLand/Modules/Storage/FDDv2/fdc.c b/KernelLand/Modules/Storage/FDDv2/fdc.c new file mode 100644 index 00000000..e86b31c6 --- /dev/null +++ b/KernelLand/Modules/Storage/FDDv2/fdc.c @@ -0,0 +1,518 @@ +/* + * Acess2 82077AA FDC + * - By John Hodge (thePowersGang) + * + * fdc.c + * - FDC IO Functions + */ +#define DEBUG 0 +#include +#include "common.h" +#include +#include + +// === CONSTANTS === +#define MOTOR_ON_DELAY 500 +#define MOTOR_OFF_DELAY 2000 + +enum eMotorState +{ + MOTOR_OFF, + MOTOR_ATSPEED, +}; + +enum eFDC_Registers +{ + FDC_DOR = 0x02, // Digital Output Register + FDC_MSR = 0x04, // Master Status Register (Read Only) + FDC_FIFO = 0x05, // FIFO port + FDC_CCR = 0x07 // Configuration Control Register (write only) +}; + +enum eFDC_Commands +{ + CMD_SPECIFY = 3, // Specify parameters + CMD_WRITE_DATA = 5, // Write Data + CMD_READ_DATA = 6, // Read Data + CMD_RECALIBRATE = 7, // Recalibrate a drive + CMD_SENSE_INTERRUPT = 8, // Sense (Ack) an interrupt + CMD_SEEK = 15, // Seek to a track +}; + +// === PROTOTYPES === + int FDD_SetupIO(void); + int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer); + int FDD_int_SeekToTrack(int Disk, int Track); + int FDD_int_Calibrate(int Disk); + int FDD_int_Reset(int Disk); +// --- FIFO + int FDD_int_WriteData(Uint16 Base, Uint8 Data); + int FDD_int_ReadData(Uint16 Base, Uint8 *Data); +void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl); +// --- Motor Control + int FDD_int_StartMotor(int Disk); + int FDD_int_StopMotor(int Disk); +void FDD_int_StopMotorCallback(void *Ptr); +// --- Helpers + int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0); +Uint16 FDD_int_GetBase(int Disk, int *Drive); +// --- Interrupt +void FDD_int_ClearIRQ(void); + int FDD_int_WaitIRQ(void); +void FDD_int_IRQHandler(int IRQ, void *Ptr); + +// === GLOBALS === +/** + * \brief Marker for IRQ6 + * \todo Convert into a semaphore? + */ + int gbFDD_IRQ6Fired; +/** + * \brief Protector for DMA and IRQ6 + */ +tMutex gFDD_IOMutex; + +// === CODE === +/** + * \brief Set up FDC IO + * \return Boolean failure + * + * Registers the IRQ handler and resets the controller + */ +int FDD_SetupIO(void) +{ + // Install IRQ6 Handler + IRQ_AddHandler(6, FDD_int_IRQHandler, NULL); + + // Reset controller + FDD_int_Reset(0); + // TODO: All controllers + + return 0; +} + +/** + * \brief Read/Write data from/to a disk + * \param Disk Global disk number + * \param Track Track number (Cyl*2+Head) + * \param bWrite Toggle write mode + * \param Buffer Destination/Source buffer + * \return Boolean failure + */ +int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer) +{ + Uint8 cmd; + int i, _disk; + Uint16 base = FDD_int_GetBase(Disk, &_disk); + int cyl = Track >> 1, head = Track & 1; + + ENTER("iDisk iTrack ibWrite pBuffer", Disk, Track, bWrite, Buffer); + + Mutex_Acquire( &gFDD_IOMutex ); + + // Initialise DMA for read/write + // TODO: Support non 1.44MiB FDs + DMA_SetChannel(2, BYTES_PER_TRACK, !bWrite); + + // Select command + if( bWrite ) + cmd = CMD_WRITE_DATA | 0xC0; + else + cmd = CMD_READ_DATA | 0xC0; + + LOG("cmd = 0x%x", cmd); + + // Seek + if( FDD_int_SeekToTrack(Disk, Track) ) { + Mutex_Release( &gFDD_IOMutex ); + LEAVE('i', -1); + return -1; + } + LOG("Track seek done"); + + for( i = 0; i < 20; i ++ ) + { + LOG("Starting motor"); + FDD_int_StartMotor(Disk); + + // Write data + if( bWrite ) + DMA_WriteData(2, BYTES_PER_TRACK, Buffer); + + LOG("Sending command stream"); + FDD_int_WriteData(base, cmd); + FDD_int_WriteData(base, (head << 2) | _disk); + FDD_int_WriteData(base, cyl); + FDD_int_WriteData(base, head); + FDD_int_WriteData(base, 1); // First Sector + FDD_int_WriteData(base, 2); // Bytes per sector (128*2^n) + FDD_int_WriteData(base, 18); // 18 tracks (full disk) - TODO: Non 1.44 + FDD_int_WriteData(base, 0x1B); // Gap length - TODO: again + FDD_int_WriteData(base, 0xFF); // Data length - ? + + LOG("Waiting for IRQ"); + FDD_int_WaitIRQ(); + + // No Sense Interrupt + + LOG("Reading result"); + Uint8 st0=0, st1=0, st2=0, bps=0; + FDD_int_ReadData(base, &st0); + FDD_int_ReadData(base, &st1); // st1 + FDD_int_ReadData(base, &st2); // st2 + FDD_int_ReadData(base, NULL); // rcy - Mutilated Cyl + FDD_int_ReadData(base, NULL); // rhe - Mutilated Head + FDD_int_ReadData(base, NULL); // rse - Mutilated sector + FDD_int_ReadData(base, &bps); // bps - Should be the same as above + + if( st0 & 0xc0 ) { + FDD_int_HandleST0Error(__func__, Disk, st0); + continue ; + } + + if( st2 & 0x02 ) { + Log_Debug("FDD", "Disk %i is not writable", Disk); + Mutex_Release( &gFDD_IOMutex ); + LEAVE('i', 2); + return 2; + } + + if( st0 & 0x08 ) { + Log_Debug("FDD", "FDD_int_ReadWriteTrack: Drive not ready"); + continue ; + } + + + if( st1 & 0x80 ) { + Log_Debug("FDD", "FDD_int_ReadWriteTrack: End of cylinder"); + continue ; + } + + if( st1 & (0x20|0x10|0x04|0x01) ) { + Log_Debug("FDD", "FDD_int_ReadWriteTrack: st1 = 0x%x", st1); + continue; + } + + if( st2 & (0x40|0x20|0x10|0x04|0x01) ) { + Log_Debug("FDD", "FDD_int_ReadWriteTrack: st2 = 0x%x", st2); + continue ; + } + + if( bps != 0x2 ) { + Log_Debug("FDD", "Wanted bps = 2 (512), got %i", bps); + continue ; + } + + // Read back data + if( !bWrite ) + DMA_ReadData(2, BYTES_PER_TRACK, Buffer); + + LOG("All data done"); + FDD_int_StopMotor(Disk); + Mutex_Release( &gFDD_IOMutex ); + LEAVE('i', 0); + return 0; + } + + Log_Debug("FDD", "%i retries exhausted", i); + FDD_int_StopMotor(Disk); + Mutex_Release( &gFDD_IOMutex ); + LEAVE('i', 1); + return 1; +} + +/** + * \brief Seek to a specific track + * \param Disk Global disk number + * \param Track Track number (Cyl*2+Head) + * \return Boolean failure + */ +int FDD_int_SeekToTrack(int Disk, int Track) +{ + Uint8 st0=0, res_cyl=0; + int cyl, head; + int _disk; + Uint16 base = FDD_int_GetBase(Disk, &_disk);; + + ENTER("iDisk iTrack", Disk, Track); + + cyl = Track / 2; + head = Track % 2; + + LOG("cyl = %i, head = %i", cyl, head); + + FDD_int_StartMotor(Disk); + + for( int i = 0; i < 10; i ++ ) + { + LOG("Sending command"); + FDD_int_ClearIRQ(); + FDD_int_WriteData(base, CMD_SEEK); + FDD_int_WriteData(base, (head << 2) + _disk); + FDD_int_WriteData(base, cyl); + + LOG("Waiting for IRQ"); + FDD_int_WaitIRQ(); + FDD_int_SenseInterrupt(base, &st0, &res_cyl); + + if( st0 & 0xC0 ) + { + FDD_int_HandleST0Error(__func__, Disk, st0); + continue ; + } + + if( res_cyl == cyl ) { + FDD_int_StopMotor(Disk); + LEAVE('i', 0); + return 0; + } + } + + Log_Error("FDD", "FDD_int_SeekToTrack: 10 retries exhausted\n"); + FDD_int_StopMotor(Disk); + LEAVE('i', 1); + return 1; +} + +/** + * \brief Calibrate a drive + * \param Disk Global disk number + */ +int FDD_int_Calibrate(int Disk) +{ + int _disk; + Uint16 base = FDD_int_GetBase(Disk, &_disk); + FDD_int_StartMotor(Disk); + + for( int i = 0; i < 10; i ++ ) + { + Uint8 st0=0, cyl = -1; + + FDD_int_ClearIRQ(); + FDD_int_WriteData(base, CMD_RECALIBRATE); + FDD_int_WriteData(base, _disk); + + FDD_int_WaitIRQ(); + + FDD_int_SenseInterrupt(base, &st0, &cyl); + + if( st0 & 0xC0 ) { + FDD_int_HandleST0Error(__func__, Disk, st0); + continue ; + } + + if( cyl == 0 ) + { + FDD_int_StopMotor(Disk); + return 0; + } + } + + Log_Error("FDD", "FDD_int_Calibrate: Retries exhausted"); + + return 1; +} + +/** + * \brief Reset a controller + * \param Base Controller base address + */ +int FDD_int_Reset(int Disk) +{ + Uint8 tmp; + int _disk; + Uint16 base = FDD_int_GetBase(Disk, &_disk); + + tmp = inb(base + FDC_DOR) & 0xF0; + outb( base + FDC_DOR, 0x00 ); + Time_Delay(1); + outb( base + FDC_DOR, tmp | 0x0C ); + + FDD_int_SenseInterrupt(base, NULL, NULL); + + outb(base + FDC_CCR, 0x00); // 500KB/s + + FDD_int_WriteData(base, CMD_SPECIFY); // Step and Head Load Times + FDD_int_WriteData(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each) + FDD_int_WriteData(base, 0x02); // Head Load Time >> 1 + + // TODO: Recalibrate all present disks + FDD_int_Calibrate(Disk); + return 0; +} + +/** + * \brief Write a byte to the FIFO + */ +int FDD_int_WriteData(Uint16 Base, Uint8 Data) +{ + for( int i = 0; i < 100; i ++ ) + { + if( inb(Base + FDC_MSR) & 0x80 ) + { + outb(Base + FDC_FIFO, Data); + return 0; + } + Time_Delay(10); + } + Log_Error("FDD", "Write timeout"); + return 1; +} + +/** + * \brief Read a byte from the FIFO + */ +int FDD_int_ReadData(Uint16 Base, Uint8 *Data) +{ + for( int i = 0; i < 100; i ++ ) + { + if( inb(Base + FDC_MSR) & 0x80 ) + { + Uint8 tmp = inb(Base + FDC_FIFO); + if(Data) *Data = tmp; + return 0; + } + Time_Delay(10); + } + Log_Error("FDD", "Read timeout"); + return 1; +} + +/** + * \brief Acknowledge an interrupt + * \param Base Controller base address + * \param ST0 Location to store the ST0 value + * \param Cyl Current cylinder + */ +void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl) +{ + FDD_int_WriteData(Base, CMD_SENSE_INTERRUPT); + FDD_int_ReadData(Base, ST0); + FDD_int_ReadData(Base, Cyl); +} + +/** + * \brief Start the motor on a disk + */ +int FDD_int_StartMotor(int Disk) +{ + int _disk; + Uint16 base = FDD_int_GetBase(Disk, &_disk); + + // Clear the motor off timer + Time_RemoveTimer(gaFDD_Disks[Disk].Timer); + gaFDD_Disks[Disk].Timer = NULL; + + // Check if the motor is already on + if( gaFDD_Disks[Disk].MotorState == MOTOR_ATSPEED ) + return 0; + + // Turn motor on + outb(base + FDC_DOR, inb(base+FDC_DOR) | (1 << (_disk + 4))); + + // Wait for it to reach speed + Time_Delay(MOTOR_ON_DELAY); + + gaFDD_Disks[Disk].MotorState = MOTOR_ATSPEED; + + return 0; +} + +/** + * \brief Schedule the motor to stop + */ +int FDD_int_StopMotor(int Disk) +{ + if( gaFDD_Disks[Disk].MotorState != MOTOR_ATSPEED ) + return 0; + if( gaFDD_Disks[Disk].Timer != NULL ) + return 0; + + gaFDD_Disks[Disk].Timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotorCallback, (void*)(tVAddr)Disk); + + return 0; +} + +/** + * \brief Actually stop the motor + * \param Ptr Actaully the global disk number + */ +void FDD_int_StopMotorCallback(void *Ptr) +{ + int Disk = (tVAddr)Ptr; + int _disk; + Uint16 base = FDD_int_GetBase(Disk, &_disk); + + gaFDD_Disks[Disk].Timer = NULL; + gaFDD_Disks[Disk].MotorState = MOTOR_OFF; + + outb(base + FDC_DOR, inb(base+FDC_DOR) & ~(1 << (_disk + 4))); + + return ; +} + +/** + * \brief Converts a global disk number into a controller and drive + * \param Disk Global disk number + * \param Drive Destination for controller disk number + * \return Controller base address + */ +Uint16 FDD_int_GetBase(int Disk, int *Drive) +{ + if(Drive) *Drive = Disk & 3; + switch(Disk >> 2) + { + case 0: return 0x3F0; + case 1: return 0x370; + default: + return 0; + } +} + +/** + * \brief Convert a ST0 error value into a message + * \param Fcn Calling function name + * \parma Disk Global disk number + * \param ST0 ST0 Value + * \return Boolean failure + */ +int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0) +{ + static const char *status_type[] = { + 0, "Error", "Invalid", "Drive Error" + }; + + Log_Debug("FDD", "%s: Disk %i ST0 Status = %s (0x%x & 0xC0 = 0x%x)", + Fcn, Disk, status_type[ST0 >> 6], ST0, ST0 & 0xC0 + ); + return 0; +} + +/** + * \brief Clear the IRQ fired flag + */ +void FDD_int_ClearIRQ(void) +{ + gbFDD_IRQ6Fired = 0; +} + +/** + * \brief Wait for an IRQ to fire + */ +int FDD_int_WaitIRQ(void) +{ + while(gbFDD_IRQ6Fired == 0) + Threads_Yield(); + return 0; +} + +/** + * \brief IRQ Handler + * \param IRQ IRQ Number (unused) + * \param Ptr Data Pointer (unused) + */ +void FDD_int_IRQHandler(int IRQ, void *Ptr) +{ + gbFDD_IRQ6Fired = 1; +} + diff --git a/KernelLand/Modules/Storage/FDDv2/main.c b/KernelLand/Modules/Storage/FDDv2/main.c new file mode 100644 index 00000000..bec157ce --- /dev/null +++ b/KernelLand/Modules/Storage/FDDv2/main.c @@ -0,0 +1,273 @@ +/* + * Acess2 82077AA FDC + * - By John Hodge (thePowersGang) + * + * fdc.c + * - Core file + */ +#define DEBUG 0 +#include +#include +#include +#include "common.h" +#include + +// === CONSTANTS === +#define FDD_VERSION VER2(1,10) + +// === STRUCTURES === + +// === PROTOTYPES === + int FDD_Install(char **Arguments); + int FDD_RegisterFS(void); +// --- VFS +char *FDD_ReadDir(tVFS_Node *Node, int pos); +tVFS_Node *FDD_FindDir(tVFS_Node *dirNode, const char *Name); + int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data); +Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); +// --- Helpers + int FDD_int_ReadWriteWithinTrack(int Disk, int Track, int bWrite, size_t Offset, size_t Length, void *Buffer); + +// === GLOBALS === +MODULE_DEFINE(0, FDD_VERSION, Storage_FDDv2, FDD_Install, NULL, "x86_ISADMA", NULL); +tDrive gaFDD_Disks[MAX_DISKS]; +tVFS_Node gaFDD_DiskNodes[MAX_DISKS]; +tVFS_NodeType gFDD_RootNodeType = { + .TypeName = "FDD Root Node", + .ReadDir = FDD_ReadDir, + .FindDir = FDD_FindDir, + .IOCtl = FDD_IOCtl + }; +tVFS_NodeType gFDD_DevNodeType = { + .TypeName = "FDD Device", + .Read = FDD_ReadFS, + .Write = NULL // FDD_WriteFS? + }; +tDevFS_Driver gFDD_DriverInfo = { + NULL, "fdd", + { + .Size = -1, + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Flags = VFS_FFLAG_DIRECTORY, + .Type = &gFDD_RootNodeType + } + }; + +// === CODE === +int FDD_Install(char **Arguments) +{ + // Query CMOS memory + { + Uint8 data; + outb(0x70, 0x10); + data = inb(0x71); + + // NOTE: CMOS only reports 2 disks + if( (data & 0xF0) == 0x40 ) + gaFDD_Disks[0].bValid = gaFDD_Disks[0].bInserted = 1; + if( (data & 0x0F) == 0x04 ) + gaFDD_Disks[1].bValid = gaFDD_Disks[1].bInserted = 1; + + if( gaFDD_Disks[0].bValid == 0 && gaFDD_Disks[1].bValid == 0 ) + return MODULE_ERR_NOTNEEDED; + } + + // Initialise controller + FDD_SetupIO(); + + FDD_RegisterFS(); + + return 0; +} + +/** + * \brief Register the FDD driver with DevFS + */ +int FDD_RegisterFS(void) +{ + gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime + = gFDD_DriverInfo.RootNode.ATime = now(); + + for( int i = 0; i < MAX_DISKS; i ++ ) + { + if( !gaFDD_Disks[i].bValid ) continue ; + + // Initialise Child Nodes + gaFDD_DiskNodes[i].Inode = i; + gaFDD_DiskNodes[i].Flags = 0; + gaFDD_DiskNodes[i].NumACLs = 0; + gaFDD_DiskNodes[i].Type = &gFDD_DevNodeType; + gaFDD_DiskNodes[i].Size = 1440*1024; // TODO: Non 1.44 disks + } + + DevFS_AddDevice( &gFDD_DriverInfo ); + return 0; +} + +/** + * \brief Get the name of the \a Pos th item in the driver root + * \param Node Root node (unused) + * \param Pos Position + * \return Heap string of node name + */ +char *FDD_ReadDir(tVFS_Node *Node, int Pos) +{ + char ret_tpl[2]; + if(Pos < 0 || Pos > MAX_DISKS ) + return NULL; + if(gaFDD_Disks[Pos].bValid) + return VFS_SKIP; + + ret_tpl[0] = '0' + Pos; + ret_tpl[1] = '\0'; + return strdup(ret_tpl); +} + +/** + * \brief Get a node by name + * \param Node Root node (unused) + * \param Name Drive name + * \return Pointer to node structure + */ +tVFS_Node *FDD_FindDir(tVFS_Node *Node, const char *Name) +{ + int pos; + if( '0' > Name[0] || Name[0] > '9' ) return NULL; + if( Name[1] != '\0' ) return NULL; + + pos = Name[0] - '0'; + + return &gaFDD_DiskNodes[pos]; +} + +static const char *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL}; +/** + * \brief Driver root IOCtl Handler + * \param Node Root node (unused) + * \param ID IOCtl ID + * \param Data IOCtl specific data pointer + */ +int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + switch(ID) + { + BASE_IOCTLS(DRV_TYPE_DISK, "FDDv2", FDD_VERSION, casIOCTLS); + + case DISK_IOCTL_GETBLOCKSIZE: return 512; + + default: + return -1; + } +} + +/** + * \brief Read from a disk + * \param Node Disk node + * \param Offset Byte offset in disk + * \param Length Number of bytes to read + * \param Buffer Destination buffer + * \return Number of bytes read + */ +Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + int disk = Node->Inode; + int track; + int rem_len; + char *dest = Buffer; + + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + + if( Offset > Node->Size ) LEAVE_RET('i', 0); + if( Length > Node->Size ) Length = Node->Size; + if( Offset + Length > Node->Size ) + Length = Node->Size - Offset; + if( Length == 0 ) return 0; + + rem_len = Length; + + track = Offset / BYTES_PER_TRACK; + Offset %= BYTES_PER_TRACK; + + if( Offset ) + { + int len; + if(rem_len > BYTES_PER_TRACK - Offset) + len = BYTES_PER_TRACK - Offset; + else + len = rem_len; + FDD_int_ReadWriteWithinTrack(disk, track, 0, Offset, len, dest); + dest += len; + rem_len -= len; + track ++; + } + + if( rem_len > 0) + { + while( rem_len > BYTES_PER_TRACK ) + { + FDD_int_ReadWriteWithinTrack(disk, track, 0, 0, BYTES_PER_TRACK, dest); + dest += BYTES_PER_TRACK; + rem_len -= BYTES_PER_TRACK; + track ++; + } + + FDD_int_ReadWriteWithinTrack(disk, track, 0, 0, rem_len, dest); + } + + LEAVE('X', Length); + return Length; +} + +/** + * \brief Read from a track + */ +int FDD_int_ReadWriteWithinTrack(int Disk, int Track, int bWrite, size_t Offset, size_t Length, void *Buffer) +{ + if( Offset > BYTES_PER_TRACK || Length > BYTES_PER_TRACK ) + return 1; + if( Offset + Length > BYTES_PER_TRACK ) + return 1; + + if( Length == 0 ) + return 0; + + ENTER("iDisk iTrack bbWrite xOffset xLength pBuffer", + Disk, Track, bWrite, Offset, Length, Buffer); + + Mutex_Acquire( &gaFDD_Disks[Disk].Mutex ); + + // If the cache doesn't exist, create it + if( !gaFDD_Disks[Disk].TrackData[Track] ) + { + gaFDD_Disks[Disk].TrackData[Track] = malloc( BYTES_PER_TRACK ); + // Don't bother reading if this is a whole track write + if( !(bWrite && Offset == 0 && Length == BYTES_PER_TRACK) ) + { + LOG("Reading track"); + FDD_int_ReadWriteTrack(Disk, Track, 0, gaFDD_Disks[Disk].TrackData[Track]); + } + } + + // Read/Write + if( bWrite ) + { + // Write to cache then commit cache to disk + char *dest = gaFDD_Disks[Disk].TrackData[Track]; + LOG("Write to cache"); + memcpy( dest + Offset, Buffer, Length ); + FDD_int_ReadWriteTrack(Disk, Track, 1, gaFDD_Disks[Disk].TrackData[Track]); + } + else + { + // Read from cache + char *src = gaFDD_Disks[Disk].TrackData[Track]; + LOG("Read from cache"); + memcpy(Buffer, src + Offset, Length); + } + + Mutex_Release( &gaFDD_Disks[Disk].Mutex ); + + LEAVE('i', 0); + return 0; +} diff --git a/KernelLand/Modules/Storage/LVM/Makefile b/KernelLand/Modules/Storage/LVM/Makefile new file mode 100644 index 00000000..1f871124 --- /dev/null +++ b/KernelLand/Modules/Storage/LVM/Makefile @@ -0,0 +1,9 @@ +# +# Acess2 Logical Volume Manager +# - Handles MBR Partitions (and eventually others) +# + +OBJ = main.o mbr.o +NAME = LVM + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/Storage/LVM/lvm.h b/KernelLand/Modules/Storage/LVM/lvm.h new file mode 100644 index 00000000..e69de29b diff --git a/KernelLand/Modules/Storage/LVM/lvm_int.h b/KernelLand/Modules/Storage/LVM/lvm_int.h new file mode 100644 index 00000000..36f2eb2c --- /dev/null +++ b/KernelLand/Modules/Storage/LVM/lvm_int.h @@ -0,0 +1,8 @@ +/* + * Acess2 Logical Volume Manager + * - By John Hodge (thePowersGang) + * + * lvm_int.h + * - Internal definitions + */ + diff --git a/KernelLand/Modules/Storage/LVM/main.c b/KernelLand/Modules/Storage/LVM/main.c new file mode 100644 index 00000000..e69de29b diff --git a/KernelLand/Modules/Storage/LVM/mbr.c b/KernelLand/Modules/Storage/LVM/mbr.c new file mode 100644 index 00000000..8cbb5b19 --- /dev/null +++ b/KernelLand/Modules/Storage/LVM/mbr.c @@ -0,0 +1,186 @@ +/* + * Acess2 IDE Harddisk Driver + * - MBR Parsing Code + * mbr.c + */ +#define DEBUG 0 +#include +#include "lvm_int.h" + +// === PROTOTYPES === +void LVM_MBR_Initialise(int Disk, tMBR *MBR); +Uint64 LVM_MBR_int_ReadExt(int Disk, Uint64 Addr, Uint64 *Base, Uint64 *Length); + +// === GLOBALS === + +// === CODE === +/** + * \fn void ATA_ParseMBR(int Disk, tMBR *MBR) + */ +void ATA_ParseMBR(int Disk, tMBR *MBR) +{ + int i, j = 0, k = 4; + Uint64 extendedLBA; + Uint64 base, len; + + ENTER("iDisk", Disk); + + // Count Partitions + gATA_Disks[Disk].NumPartitions = 0; + extendedLBA = 0; + for( i = 0; i < 4; i ++ ) + { + if( MBR->Parts[i].SystemID == 0 ) continue; + if( MBR->Parts[i].Boot == 0x0 || MBR->Parts[i].Boot == 0x80 // LBA 28 + || MBR->Parts[i].Boot == 0x1 || MBR->Parts[i].Boot == 0x81 // LBA 48 + ) + { + if( MBR->Parts[i].SystemID == 0xF || MBR->Parts[i].SystemID == 5 ) { + LOG("Extended Partition at 0x%llx", MBR->Parts[i].LBAStart); + if(extendedLBA != 0) { + Warning("Disk %i has multiple extended partitions, ignoring rest", Disk); + continue; + } + extendedLBA = MBR->Parts[i].LBAStart; + continue; + } + LOG("Primary Partition at 0x%llx", MBR->Parts[i].LBAStart); + + gATA_Disks[Disk].NumPartitions ++; + continue; + } + // Invalid Partition, so don't count it + } + while(extendedLBA != 0) + { + extendedLBA = ATA_MBR_int_ReadExt(Disk, extendedLBA, &base, &len); + if( extendedLBA == -1 ) break; + gATA_Disks[Disk].NumPartitions ++; + } + LOG("gATA_Disks[Disk].NumPartitions = %i", gATA_Disks[Disk].NumPartitions); + + // Create patition array + gATA_Disks[Disk].Partitions = malloc( gATA_Disks[Disk].NumPartitions * sizeof(tATA_Partition) ); + + // --- Fill Partition Info --- + extendedLBA = 0; + for( j = 0, i = 0; i < 4; i ++ ) + { + LOG("MBR->Parts[%i].SystemID = 0x%02x", i, MBR->Parts[i].SystemID); + if( MBR->Parts[i].SystemID == 0 ) continue; + if( MBR->Parts[i].Boot == 0x0 || MBR->Parts[i].Boot == 0x80 ) // LBA 28 + { + base = MBR->Parts[i].LBAStart; + len = MBR->Parts[i].LBALength; + } + else if( MBR->Parts[i].Boot == 0x1 || MBR->Parts[i].Boot == 0x81 ) // LBA 58 + { + base = (MBR->Parts[i].StartHi << 16) | MBR->Parts[i].LBAStart; + len = (MBR->Parts[i].LengthHi << 16) | MBR->Parts[i].LBALength; + } + else + continue; + + if( MBR->Parts[i].SystemID == 0xF || MBR->Parts[i].SystemID == 5 ) { + if(extendedLBA != 0) { + Log_Warning("ATA", "Disk %i has multiple extended partitions, ignoring rest", Disk); + continue; + } + extendedLBA = base; + continue; + } + // Create Partition + ATA_int_MakePartition( + &gATA_Disks[Disk].Partitions[j], Disk, j, + base, len + ); + j ++; + + } + // Scan extended partitions + while(extendedLBA != 0) + { + extendedLBA = ATA_MBR_int_ReadExt(Disk, extendedLBA, &base, &len); + if(extendedLBA == -1) break; + ATA_int_MakePartition( + &gATA_Disks[Disk].Partitions[j], Disk, k, base, len + ); + } + + LEAVE('-'); +} + +/** + * \brief Reads an extended partition + * \return LBA of next Extended, -1 on error, 0 for last + */ +Uint64 ATA_MBR_int_ReadExt(int Disk, Uint64 Addr, Uint64 *Base, Uint64 *Length) +{ + Uint64 link = 0; + int bFoundPart = 0;; + int i; + tMBR mbr; + Uint64 base, len; + + if( ATA_ReadDMA( Disk, Addr, 1, &mbr ) != 0 ) + return -1; // Stop on Errors + + for( i = 0; i < 4; i ++ ) + { + if( mbr.Parts[i].SystemID == 0 ) continue; + + // LBA 24 + if( mbr.Parts[i].Boot == 0x0 || mbr.Parts[i].Boot == 0x80 ) { + base = mbr.Parts[i].LBAStart; + len = mbr.Parts[i].LBALength; + } + // LBA 48 + else if( mbr.Parts[i].Boot == 0x1 || mbr.Parts[i].Boot == 0x81 ) { + base = (mbr.Parts[i].StartHi << 16) | mbr.Parts[i].LBAStart; + len = (mbr.Parts[i].LengthHi << 16) | mbr.Parts[i].LBALength; + } + else { + Log_Warning("ATA MBR", + "Unknown partition type 0x%x, Disk %i Ext 0x%llx Part %i", + mbr.Parts[i].Boot, Disk, Addr, i + ); + return -1; + } + + switch(mbr.Parts[i].SystemID) + { + case 0xF: + case 0x5: + if(link != 0) { + Log_Warning("ATA MBR", + "Disk %i has two forward links in the extended partition", + Disk + ); + return -1; + } + link = base; + break; + default: + if(bFoundPart) { + Warning("ATA MBR", + "Disk %i has more than one partition in the extended partition at 0x%llx", + Disk, Addr + ); + return -1; + } + bFoundPart = 1; + *Base = base; + *Length = len; + break; + } + } + + if(!bFoundPart) { + Log_Warning("ATA MBR", + "No partition in extended partiton, Disk %i 0x%llx", + Disk, Addr); + return -1; + } + + return link; +} diff --git a/KernelLand/Modules/Storage/LVM/volumes.c b/KernelLand/Modules/Storage/LVM/volumes.c new file mode 100644 index 00000000..e69de29b diff --git a/KernelLand/Modules/Storage/Makefile.tpl b/KernelLand/Modules/Storage/Makefile.tpl new file mode 100644 index 00000000..d5291f69 --- /dev/null +++ b/KernelLand/Modules/Storage/Makefile.tpl @@ -0,0 +1,3 @@ +CATEGORY = Storage + +-include ../../Makefile.tpl diff --git a/KernelLand/Modules/USB/Core/Makefile b/KernelLand/Modules/USB/Core/Makefile new file mode 100644 index 00000000..4d6e6845 --- /dev/null +++ b/KernelLand/Modules/USB/Core/Makefile @@ -0,0 +1,10 @@ +# +# + +OBJ = main.o +OBJ += usb.o usb_lowlevel.o usb_devinit.o usb_io.o usb_poll.o +OBJ += hub.o +CPPFLAGS = -Iinclude +NAME = Core + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/USB/Core/hub.c b/KernelLand/Modules/USB/Core/hub.c new file mode 100644 index 00000000..849e8a3f --- /dev/null +++ b/KernelLand/Modules/USB/Core/hub.c @@ -0,0 +1,179 @@ +/* + * Acess2 USB Stack + * - By John Hodge (thePowersGang) + * + * hub.c + * - Basic hub driver + */ +#define DEBUG 1 +#include + +#define MAX_PORTS 32 // Not actually a max, but used for DeviceRemovable + + +#define GET_STATUS 0 +#define CLEAR_FEATURE 1 +// resvd +#define SET_FEATURE 3 + +#define PORT_CONNECTION 0 +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVER_CURRENT 3 +#define PORT_RESET 4 +#define PORT_POWER 8 +#define PORT_LOW_SPEED 9 +#define C_PORT_CONNECTION 16 +#define C_PORT_ENABLE 17 +#define C_PORT_SUSPEND 18 +#define C_PORT_OVER_CURRENT 19 +#define C_PORT_RESET 20 +#define PORT_TEST 21 +#define PORT_INDICATOR 21 + +struct sHubDescriptor +{ + Uint8 DescLength; + Uint8 DescType; // = 0x29 + Uint8 NbrPorts; + Uint16 HubCharacteristics; + Uint8 PwrOn2PwrGood; // 2 ms intervals + Uint8 HubContrCurrent; // Max internal current (mA) + Uint8 DeviceRemovable[MAX_PORTS]; +}; + +struct sHubInfo +{ + tUSBHub *HubPtr; + int PowerOnDelay; // in ms + int nPorts; + Uint8 DeviceRemovable[]; +}; + +// === PROTOTYPES === +void Hub_Connected(tUSBInterface *Dev); +void Hub_Disconnected(tUSBInterface *Dev); +void Hub_PortStatusChange(tUSBInterface *Dev, int Endpoint, int Length, void *Data); +void Hub_int_HandleChange(tUSBInterface *Dev, int Port); + +// === GLOBALS === +tUSBDriver gUSBHub_Driver = { + .Name = "Hub", + .Match = {.Class = {0x090000, 0xFF0000}}, + .Connected = Hub_Connected, + .Disconnected = Hub_Disconnected, + .MaxEndpoints = 1, + .Endpoints = { + {0x83, Hub_PortStatusChange} + } +}; + +// === CODE === +#if 0 +int Hub_DriverInitialise(char **Arguments) +{ + USB_RegisterDriver( &gUSBHub_Driver ); + return 0; +} +#endif + +void Hub_Connected(tUSBInterface *Dev) +{ + struct sHubDescriptor hub_desc; + struct sHubInfo *info; + + // Read hub descriptor (Class descriptor 0x29) + USB_ReadDescriptor(Dev, 0x129, 0, sizeof(hub_desc), &hub_desc); + + LOG("%i Ports", hub_desc.NbrPorts); + LOG("Takes %i ms for power to stabilise", hub_desc.PwrOn2PwrGood*2); + + // Allocate infomation structure + info = malloc(sizeof(*info) + (hub_desc.NbrPorts+7)/8); + if(!info) { + Log_Error("USBHub", "malloc() failed"); + return ; + } + USB_SetDeviceDataPtr(Dev, info); + + // Fill data + info->nPorts = hub_desc.NbrPorts; + info->PowerOnDelay = hub_desc.PwrOn2PwrGood * 2; + memcpy(info->DeviceRemovable, hub_desc.DeviceRemovable, (hub_desc.NbrPorts+7)/8); + // Register + info->HubPtr = USB_RegisterHub(Dev, info->nPorts); + + // Register poll on endpoint + USB_StartPollingEndpoint(Dev, 1); +} + +void Hub_Disconnected(tUSBInterface *Dev) +{ + struct sHubInfo *info = USB_GetDeviceDataPtr(Dev); + USB_RemoveHub(info->HubPtr); + free(info); +} + +void Hub_PortStatusChange(tUSBInterface *Dev, int Endpoint, int Length, void *Data) +{ + Uint8 *status = Data; + struct sHubInfo *info = USB_GetDeviceDataPtr(Dev); + int i; + for( i = 0; i < info->nPorts; i += 8, status ++ ) + { + if( i/8 >= Length ) break; + if( *status == 0 ) continue; + + for( int j = 0; j < 8; j ++ ) + if( *status & (1 << j) ) + Hub_int_HandleChange(Dev, i+j); + } +} + +void Hub_int_HandleChange(tUSBInterface *Dev, int Port) +{ + struct sHubInfo *info = USB_GetDeviceDataPtr(Dev); + Uint16 status[2]; // Status, Change + + // Get port status + USB_Request(Dev, 0, 0xA3, GET_STATUS, 0, Port, 4, status); + + LOG("Port %i: status = {0b%b, 0b%b}", Port, status[0], status[1]); + + // Handle connections / disconnections + if( status[1] & 0x0001 ) + { + if( status[0] & 0x0001 ) { + // Connected + // - Power on port + USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_POWER, Port, 0, NULL); + Time_Delay(info->PowerOnDelay); + // - Reset + USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_RESET, Port, 0, NULL); + Time_Delay(20); // Spec says 10ms after reset, but how long is reset? + // - Enable + USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_ENABLE, Port, 0, NULL); + // - Poke USB Stack + USB_DeviceConnected(info->HubPtr, Port); + } + else { + // Disconnected + USB_DeviceDisconnected(info->HubPtr, Port); + } + + USB_Request(Dev, 0, 0x23, CLEAR_FEATURE, C_PORT_CONNECTION, Port, 0, NULL); + } + + // Reset change + if( status[1] & 0x0010 ) + { + if( status[0] & 0x0010 ) { + // Reset complete + } + else { + // Useful? + } + // ACK + USB_Request(Dev, 0, 0x23, CLEAR_FEATURE, C_PORT_RESET, Port, 0, NULL); + } +} diff --git a/KernelLand/Modules/USB/Core/include/usb_core.h b/KernelLand/Modules/USB/Core/include/usb_core.h new file mode 100644 index 00000000..43af16f2 --- /dev/null +++ b/KernelLand/Modules/USB/Core/include/usb_core.h @@ -0,0 +1,66 @@ +/* + * Acess2 USB Stack + * - By John Hodge (thePowersGang) + * + * usb_core.h + * - Core USB IO Header + */ +#ifndef _USB_CORE_H_ +#define _USB_CORE_H_ + +#include + +typedef struct sUSBInterface tUSBInterface; +typedef struct sUSBDriver tUSBDriver; + +typedef void (*tUSB_DataCallback)(tUSBInterface *Dev, int EndPt, int Length, void *Data); + +/** + */ +struct sUSBDriver +{ + tUSBDriver *Next; + + const char *Name; + + int MatchType; // 0: Interface, 1: Device, 2: Vendor + union { + struct { + // 23:16 - Interface Class + // 15:8 - Interface Sub Class + // 7:0 - Interface Protocol + Uint32 ClassCode; + Uint32 ClassMask; + } Class; + struct { + Uint16 VendorID; + Uint16 DeviceID; + } VendorDev; + } Match; + + void (*Connected)(tUSBInterface *Dev); + void (*Disconnected)(tUSBInterface *Dev); + + int MaxEndpoints; + struct { + // USB Attrbute byte + // NOTE: Top bit indicates the direction (1=Input) + Uint8 Attributes; + // Data availiable Callback + tUSB_DataCallback DataAvail; + } Endpoints[]; +}; + +extern void *USB_GetDeviceDataPtr(tUSBInterface *Dev); +extern void USB_SetDeviceDataPtr(tUSBInterface *Dev, void *Ptr); + +extern void USB_StartPollingEndpoint(tUSBInterface *Dev, int Endpoint); +extern void USB_ReadDescriptor(tUSBInterface *Dev, int Type, int Index, int Length, void *Data); +extern void USB_Request(tUSBInterface *Dev, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data); +// TODO: Async +extern void USB_SendData(tUSBInterface *Dev, int Endpoint, int Length, void *Data); +extern void USB_RecvData(tUSBInterface *Dev, int Endpoint, int Length, void *Data); +extern void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf, tUSB_DataCallback Callback); + +#endif + diff --git a/KernelLand/Modules/USB/Core/include/usb_host.h b/KernelLand/Modules/USB/Core/include/usb_host.h new file mode 100644 index 00000000..c5f3e39d --- /dev/null +++ b/KernelLand/Modules/USB/Core/include/usb_host.h @@ -0,0 +1,37 @@ +/* + * Acess2 USB Stack + * - By John Hodge (thePowersGang) + * + * usb_host.h + * - USB Host Controller Interface + */ +#ifndef _USB_HOST_H_ +#define _USB_HOST_H_ + +#include "usb_core.h" +#include "usb_hub.h" + +typedef struct sUSBHostDef tUSBHostDef; + +typedef void (*tUSBHostCb)(void *DataPtr, void *Data, int Length); + +typedef void *(*tUSBHostOp)(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb CB, void *CbData, void *Data, size_t Length); + +/** + * \brief Defines a USB Host Controller type + */ +struct sUSBHostDef +{ + tUSBHostOp SendIN; + tUSBHostOp SendOUT; + tUSBHostOp SendSETUP; + + int (*IsOpComplete)(void *Ptr, void *OpPtr); + + void (*CheckPorts)(void *Ptr); +}; + +extern tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts); + +#endif + diff --git a/KernelLand/Modules/USB/Core/include/usb_hub.h b/KernelLand/Modules/USB/Core/include/usb_hub.h new file mode 100644 index 00000000..6b32d49c --- /dev/null +++ b/KernelLand/Modules/USB/Core/include/usb_hub.h @@ -0,0 +1,27 @@ +/* + * Acess2 USB Stack + * - By John Hodge (thePowersGang) + * + * usb_hub.h + * - Core Hub Definitions + */ +#ifndef _USB_HUB_H_ +#define _USB_HUB_H_ + +#include "usb_core.h" + +typedef struct sUSBHub tUSBHub; + +/** + * \brief Register a device as a hub + * + * Used by the hub class initialisation routine. + */ +extern tUSBHub *USB_RegisterHub(tUSBInterface *Device, int nPorts); +extern void USB_RemoveHub(tUSBHub *Hub); + +extern void USB_DeviceConnected(tUSBHub *Hub, int Port); +extern void USB_DeviceDisconnected(tUSBHub *Hub, int Port); + +#endif + diff --git a/KernelLand/Modules/USB/Core/main.c b/KernelLand/Modules/USB/Core/main.c new file mode 100644 index 00000000..df7f174f --- /dev/null +++ b/KernelLand/Modules/USB/Core/main.c @@ -0,0 +1,86 @@ +/* + * Acess2 + * USB Stack + */ +#define VERSION ( (0<<8)| 5 ) +#define DEBUG 1 +#include +#include +#include +#include +#include "usb.h" + +// === IMPORTS === +extern void USB_PollThread(void *unused); +extern void USB_AsyncThread(void *Unused); + +// === PROTOTYPES === + int USB_Install(char **Arguments); +void USB_Cleanup(void); +char *USB_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *USB_FindDir(tVFS_Node *Node, const char *Name); + int USB_IOCtl(tVFS_Node *Node, int Id, void *Data); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, USB_Core, USB_Install, NULL, NULL); +tVFS_NodeType gUSB_RootNodeType = { + .ReadDir = USB_ReadDir, + .FindDir = USB_FindDir, + .IOCtl = USB_IOCtl +}; +tDevFS_Driver gUSB_DrvInfo = { + NULL, "usb", { + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Flags = VFS_FFLAG_DIRECTORY, + .Type = &gUSB_RootNodeType + } +}; +tUSBHost *gUSB_Hosts = NULL; + +// === CODE === +/** + * \brief Called once module is loaded + */ +int USB_Install(char **Arguments) +{ + Log_Warning("USB", "Not Complete - Devel Only"); + + Proc_SpawnWorker(USB_PollThread, NULL); + Proc_SpawnWorker(USB_AsyncThread, NULL); + + return MODULE_ERR_OK; +} + +/** + * \brief Called just before module is unloaded + */ +void USB_Cleanup() +{ +} + +/** + * \fn char *USB_ReadDir(tVFS_Node *Node, int Pos) + * \brief Read from the USB root + */ +char *USB_ReadDir(tVFS_Node *Node, int Pos) +{ + return NULL; +} + +/** + * \fn tVFS_Node *USB_FindDir(tVFS_Node *Node, const char *Name) + * \brief Locate an entry in the USB root + */ +tVFS_Node *USB_FindDir(tVFS_Node *Node, const char *Name) +{ + return NULL; +} + +/** + * \brief Handles IOCtl Calls to the USB driver + */ +int USB_IOCtl(tVFS_Node *Node, int Id, void *Data) +{ + return 0; +} diff --git a/KernelLand/Modules/USB/Core/usb.c b/KernelLand/Modules/USB/Core/usb.c new file mode 100644 index 00000000..b3195804 --- /dev/null +++ b/KernelLand/Modules/USB/Core/usb.c @@ -0,0 +1,108 @@ +/* + * Acess2 USB Stack + * - By John Hodge (thePowersGang) + * + * usb.c + * - USB Structure + */ +#define DEBUG 1 +#include +#include +#include +#include "usb.h" + +// === IMPORTS === +extern tUSBHost *gUSB_Hosts; +extern tUSBDriver gUSBHub_Driver; + +// === STRUCTURES === + +// === PROTOTYPES === +tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts); + +// === GLOBALS === +tUSBDriver *gpUSB_InterfaceDrivers = &gUSBHub_Driver; + +// === CODE === +tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts) +{ + tUSBHost *host; + + host = malloc(sizeof(tUSBHost) + nPorts*sizeof(void*)); + if(!host) { + // Oh, bugger. + return NULL; + } + host->HostDef = HostDef; + host->Ptr = ControllerPtr; + memset(host->AddressBitmap, 0, sizeof(host->AddressBitmap)); + + host->RootHubDev.ParentHub = NULL; + host->RootHubDev.Host = host; + host->RootHubDev.Address = 0; + +// host->RootHubIf.Next = NULL; + host->RootHubIf.Dev = &host->RootHubDev; + host->RootHubIf.Driver = NULL; + host->RootHubIf.Data = NULL; + host->RootHubIf.nEndpoints = 0; + + host->RootHub.Interface = &host->RootHubIf; + host->RootHub.nPorts = nPorts; + memset(host->RootHub.Devices, 0, sizeof(void*)*nPorts); + + // TODO: Lock + host->Next = gUSB_Hosts; + gUSB_Hosts = host; + + return &host->RootHub; +} + +// --- Drivers --- +void USB_RegisterDriver(tUSBDriver *Driver) +{ + Log_Warning("USB", "TODO: Implement USB_RegisterDriver"); +} + +tUSBDriver *USB_int_FindDriverByClass(Uint32 ClassCode) +{ + ENTER("xClassCode", ClassCode); + for( tUSBDriver *ret = gpUSB_InterfaceDrivers; ret; ret = ret->Next ) + { + LOG(" 0x%x & 0x%x == 0x%x?", ClassCode, ret->Match.Class.ClassMask, ret->Match.Class.ClassCode); + if( (ClassCode & ret->Match.Class.ClassMask) == ret->Match.Class.ClassCode ) + { + LOG("Found '%s'", ret->Name); + LEAVE('p', ret); + return ret; + } + } + LEAVE('n'); + return NULL; +} + +// --- Hub Registration --- +// NOTE: Doesn't do much nowdays +tUSBHub *USB_RegisterHub(tUSBInterface *Device, int PortCount) +{ + tUSBHub *ret; + + ret = malloc(sizeof(tUSBHub) + sizeof(ret->Devices[0])*PortCount); + ret->Interface = Device; + ret->nPorts = PortCount; + memset(ret->Devices, 0, sizeof(ret->Devices[0])*PortCount); + return ret; +} + +void USB_RemoveHub(tUSBHub *Hub) +{ + for( int i = 0; i < Hub->nPorts; i ++ ) + { + if( Hub->Devices[i] ) + { + USB_DeviceDisconnected( Hub, i ); + } + } + free(Hub); +} + diff --git a/KernelLand/Modules/USB/Core/usb.h b/KernelLand/Modules/USB/Core/usb.h new file mode 100644 index 00000000..5a133630 --- /dev/null +++ b/KernelLand/Modules/USB/Core/usb.h @@ -0,0 +1,94 @@ +/* + * Acess2 USB Stack + * - By John Hodge (thePowersGang) + * + * usb.h + * - USB Internal definitions + */ +#ifndef _USB_H_ +#define _USB_H_ + +#include +#include +#include + +typedef struct sUSBHost tUSBHost; +typedef struct sUSBDevice tUSBDevice; +typedef struct sUSBEndpoint tUSBEndpoint; + +// === STRUCTURES === +/** + * \brief USB Hub data + */ +struct sUSBHub +{ + tUSBInterface *Interface; + + int nPorts; + tUSBDevice *Devices[]; +}; + +struct sUSBEndpoint +{ + tUSBEndpoint *Next; // (usb_poll.c) Clock list + tUSBInterface *Interface; + int EndpointIdx; // Interface endpoint index + int EndpointNum; // Device endpoint num + + int PollingPeriod; // In 1ms intervals + int MaxPacketSize; // In bytes + Uint8 Type; // Same as sUSBDriver.Endpoints.Type + + int PollingAtoms; // (usb_poll.c) Period in clock list + void *InputData; +}; + +/** + * \brief Structure for a device's interface + */ +struct sUSBInterface +{ +// tUSBInterface *Next; + tUSBDevice *Dev; + + tUSBDriver *Driver; + void *Data; + + int nEndpoints; + tUSBEndpoint Endpoints[]; +}; + +/** + * \brief Defines a single device on the USB Bus + */ +struct sUSBDevice +{ + tUSBHub *ParentHub; + + /** + * \brief Host controller used + */ + tUSBHost *Host; + int Address; + + int nInterfaces; + tUSBInterface *Interfaces[]; +}; + +struct sUSBHost +{ + struct sUSBHost *Next; + + tUSBHostDef *HostDef; + void *Ptr; + + Uint8 AddressBitmap[128/8]; + + tUSBDevice RootHubDev; + tUSBInterface RootHubIf; + tUSBHub RootHub; +}; + +extern tUSBDriver *USB_int_FindDriverByClass(Uint32 ClassCode); + +#endif diff --git a/KernelLand/Modules/USB/Core/usb_devinit.c b/KernelLand/Modules/USB/Core/usb_devinit.c new file mode 100644 index 00000000..d01d7d1c --- /dev/null +++ b/KernelLand/Modules/USB/Core/usb_devinit.c @@ -0,0 +1,239 @@ +/* + * Acess 2 USB Stack + * - By John Hodge (thePowersGang) + * + * usb_devinit.c + * - USB Device Initialisation + */ +#define DEBUG 1 + +#include +#include +#include +#include "usb.h" +#include "usb_proto.h" +#include "usb_lowlevel.h" + +// === PROTOTYPES === +void USB_DeviceConnected(tUSBHub *Hub, int Port); +void USB_DeviceDisconnected(tUSBHub *Hub, int Port); +void *USB_GetDeviceDataPtr(tUSBInterface *Dev); +void USB_SetDeviceDataPtr(tUSBInterface *Dev, void *Ptr); + int USB_int_AllocateAddress(tUSBHost *Host); + +// === CODE === +void USB_DeviceConnected(tUSBHub *Hub, int Port) +{ + tUSBDevice tmpdev; + tUSBDevice *dev = &tmpdev; + if( Port >= Hub->nPorts ) return ; + if( Hub->Devices[Port] ) return ; + + ENTER("pHub iPort", Hub, Port); + + // Device should be in 'Default' state + + // Create structure + dev->ParentHub = Hub; + dev->Host = Hub->Interface->Dev->Host; + dev->Address = 0; + + // 1. Assign an address + dev->Address = USB_int_AllocateAddress(dev->Host); + if(dev->Address == 0) { + Log_Error("USB", "No addresses avaliable on host %p", dev->Host); + free(dev); + LEAVE('-'); + return ; + } + USB_int_SendSetupSetAddress(dev->Host, dev->Address); + LOG("Assigned address %i", dev->Address); + + // 2. Get device information + { + struct sDescriptor_Device desc; + LOG("Getting device descriptor"); + // Endpoint 0, Desc Type 1, Index 0 + USB_int_ReadDescriptor(dev, 0, 1, 0, sizeof(desc), &desc); + + LOG("Device Descriptor = {"); + LOG(" .Length = %i", desc.Length); + LOG(" .Type = %i", desc.Type); + LOG(" .USBVersion = 0x%04x", desc.USBVersion); + LOG(" .DeviceClass = 0x%02x", desc.DeviceClass); + LOG(" .DeviceSubClass = 0x%02x", desc.DeviceSubClass); + LOG(" .DeviceProtocol = 0x%02x", desc.DeviceProtocol); + LOG(" .MaxPacketSize = 0x%02x", desc.MaxPacketSize); + LOG(" .VendorID = 0x%04x", desc.VendorID); + LOG(" .ProductID = 0x%04x", desc.ProductID); + LOG(" .DeviceID = 0x%04x", desc.DeviceID); + LOG(" .ManufacturerStr = Str %i", desc.ManufacturerStr); + LOG(" .ProductStr = Str %i", desc.ProductStr); + LOG(" .SerialNumberStr = Str %i", desc.SerialNumberStr); + LOG(" .NumConfigurations = %i", desc.SerialNumberStr); + LOG("}"); + + if( desc.ManufacturerStr ) + { + char *tmp = USB_int_GetDeviceString(dev, 0, desc.ManufacturerStr); + LOG("ManufacturerStr = '%s'", tmp); + free(tmp); + } + if( desc.ProductStr ) + { + char *tmp = USB_int_GetDeviceString(dev, 0, desc.ProductStr); + LOG("ProductStr = '%s'", tmp); + free(tmp); + } + if( desc.SerialNumberStr ) + { + char *tmp = USB_int_GetDeviceString(dev, 0, desc.SerialNumberStr); + LOG("SerialNumbertStr = '%s'", tmp); + free(tmp); + } + } + + // TODO: Support alternate configurations + + // 3. Get configurations + for( int i = 0; i < 1; i ++ ) + { + struct sDescriptor_Configuration desc; + void *full_buf; + char *cur_ptr; + + USB_int_ReadDescriptor(dev, 0, 2, i, sizeof(desc), &desc); + LOG("Configuration Descriptor %i = {", i); + LOG(" .Length = %i", desc.Length); + LOG(" .Type = %i", desc.Type); + LOG(" .TotalLength = 0x%x", LittleEndian16(desc.TotalLength)); + LOG(" .NumInterfaces = %i", desc.NumInterfaces); + LOG(" .ConfigurationValue = %i", desc.ConfigurationValue); + LOG(" .ConfigurationStr = %i", desc.ConfigurationStr); + LOG(" .AttributesBmp = 0b%b", desc.AttributesBmp); + LOG(" .MaxPower = %i (*2mA)", desc.MaxPower); + LOG("}"); + if( desc.ConfigurationStr ) { + char *tmp = USB_int_GetDeviceString(dev, 0, desc.ConfigurationStr); + LOG("ConfigurationStr = '%s'", tmp); + free(tmp); + } + + // TODO: Split here and allow some method of selection + + // Allocate device now that we have the configuration + dev = malloc(sizeof(tUSBDevice) + desc.NumInterfaces * sizeof(void*)); + memcpy(dev, &tmpdev, sizeof(tUSBDevice)); + dev->nInterfaces = desc.NumInterfaces; + + // Allocate a temp buffer for config info + cur_ptr = full_buf = malloc( LittleEndian16(desc.TotalLength) ); + USB_int_ReadDescriptor(dev, 0, 2, i, desc.TotalLength, full_buf); + + cur_ptr += desc.Length; + + // TODO: Interfaces + for( int j = 0; j < desc.NumInterfaces; j ++ ) + { + struct sDescriptor_Interface *iface; + tUSBInterface *dev_if; + iface = (void*)cur_ptr; + // TODO: Sanity check with remaining space + cur_ptr += sizeof(*iface); + + LOG("Interface %i/%i = {", i, j); + LOG(" .InterfaceNum = %i", iface->InterfaceNum); + LOG(" .NumEndpoints = %i", iface->NumEndpoints); + LOG(" .InterfaceClass = 0x%x", iface->InterfaceClass); + LOG(" .InterfaceSubClass = 0x%x", iface->InterfaceSubClass); + LOG(" .InterfaceProcol = 0x%x", iface->InterfaceProtocol); + + if( iface->InterfaceStr ) { + char *tmp = USB_int_GetDeviceString(dev, 0, iface->InterfaceStr); + LOG(" .InterfaceStr = %i '%s'", iface->InterfaceStr, tmp); + free(tmp); + } + LOG("}"); + + dev_if = malloc(sizeof(tUSBInterface) + iface->NumEndpoints*sizeof(dev_if->Endpoints[0])); + dev_if->Dev = dev; + dev_if->Driver = NULL; + dev_if->Data = NULL; + dev_if->nEndpoints = iface->NumEndpoints; + dev->Interfaces[j] = dev_if; + + // Copy interface data + for( int k = 0; k < iface->NumEndpoints; k ++ ) + { + struct sDescriptor_Endpoint *endpt; + endpt = (void*)cur_ptr; + // TODO: Sanity check with remaining space + cur_ptr += sizeof(*endpt); + + LOG("Endpoint %i/%i/%i = {", i, j, k); + LOG(" .Address = 0x%2x", endpt->Address); + LOG(" .Attributes = 0b%8b", endpt->Attributes); + LOG(" .MaxPacketSize = %i", LittleEndian16(endpt->MaxPacketSize)); + LOG(" .PollingInterval = %i", endpt->PollingInterval); + LOG("}"); + + dev_if->Endpoints[k].Next = NULL; + dev_if->Endpoints[k].Interface = dev_if; + dev_if->Endpoints[k].EndpointIdx = k; + dev_if->Endpoints[k].EndpointNum = endpt->Address & 0x7F; + dev_if->Endpoints[k].PollingPeriod = endpt->PollingInterval; + dev_if->Endpoints[k].MaxPacketSize = LittleEndian16(endpt->MaxPacketSize); + dev_if->Endpoints[k].Type = endpt->Attributes | (endpt->Address & 0x80); + dev_if->Endpoints[k].PollingAtoms = 0; + dev_if->Endpoints[k].InputData = NULL; + } + + // Initialise driver + dev_if->Driver = USB_int_FindDriverByClass( + ((int)iface->InterfaceClass << 16) + |((int)iface->InterfaceSubClass << 8) + |((int)iface->InterfaceProtocol << 0) + ); + if(!dev_if->Driver) { + Log_Notice("USB", "No driver for Class %02x:%02x:%02x", + iface->InterfaceClass, iface->InterfaceSubClass, iface->InterfaceProtocol + ); + } + else { + dev_if->Driver->Connected( dev_if ); + } + } + + free(full_buf); + } + + // Done. + LEAVE('-'); +} + +void USB_DeviceDisconnected(tUSBHub *Hub, int Port) +{ + +} + +void *USB_GetDeviceDataPtr(tUSBInterface *Dev) { return Dev->Data; } +void USB_SetDeviceDataPtr(tUSBInterface *Dev, void *Ptr) { Dev->Data = Ptr; } + +int USB_int_AllocateAddress(tUSBHost *Host) +{ + int i; + for( i = 1; i < 128; i ++ ) + { + if(Host->AddressBitmap[i/8] & (1 << (i%8))) + continue ; + Host->AddressBitmap[i/8] |= 1 << (i%8); + return i; + } + return 0; +} + +void USB_int_DeallocateAddress(tUSBHost *Host, int Address) +{ + Host->AddressBitmap[Address/8] &= ~(1 << (Address%8)); +} + diff --git a/KernelLand/Modules/USB/Core/usb_io.c b/KernelLand/Modules/USB/Core/usb_io.c new file mode 100644 index 00000000..25ff59d2 --- /dev/null +++ b/KernelLand/Modules/USB/Core/usb_io.c @@ -0,0 +1,119 @@ +/* + * Acess2 USB Stack + * - By John Hodge (thePowersGang) + * + * usb_io.c + * - High-level IO + */ +#define DEBUG 0 + +#include +#include "usb.h" +#include "usb_lowlevel.h" +#include + +typedef struct sAsyncOp tAsyncOp; + +struct sAsyncOp +{ + tAsyncOp *Next; + tUSBEndpoint *Endpt; + int Length; + void *Data; +}; + +// === PROTOTYPES === +void USB_ReadDescriptor(tUSBInterface *Iface, int Type, int Index, int Length, void *Data); +void USB_Request(tUSBInterface *Iface, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data); +void USB_AsyncCallback(void *Ptr, void *Buf, int Length); +void USB_AsyncThread(void *unused); + +// === GLOBALS === +tWorkqueue gUSB_AsyncQueue; + +// === CODE === +void USB_ReadDescriptor(tUSBInterface *Iface, int Type, int Index, int Length, void *Data) +{ + USB_int_ReadDescriptor(Iface->Dev, 0, Type, Index, Length, Data); +} + +void USB_Request(tUSBInterface *Iface, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data) +{ + int endpt; + + // Sanity check + if(Endpoint < 0 || Endpoint >= Iface->nEndpoints) + return ; + + // Get endpoint number + if(Endpoint) + endpt = Iface->Endpoints[Endpoint-1].EndpointNum; + else + endpt = 0; + + USB_int_Request(Iface->Dev->Host, Iface->Dev->Address, endpt, Type, Req, Value, Index, Len, Data); +} + + +void USB_SendData(tUSBInterface *Dev, int Endpoint, int Length, void *Data) +{ + Log_Warning("USB", "TODO: Implement USB_SendData"); +} + +void USB_RecvData(tUSBInterface *Dev, int Endpoint, int Length, void *Data) +{ + Log_Warning("USB", "TODO: Implement USB_RecvData"); +} + +void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf, tUSB_DataCallback Callback) +{ + tAsyncOp *op; + tUSBHost *host; + + ENTER("pDev iEndpoint iLength pDataBuf", Dev, Endpoint, Length, DataBuf); + + op = malloc(sizeof(*op)); + op->Next = NULL; + op->Endpt = &Dev->Endpoints[Endpoint-1]; + op->Length = Length; + op->Data = DataBuf; + + // TODO: Handle transfers that are larger than one packet + + host = Dev->Dev->Host; + LOG("IN from %p %i:%i", host->Ptr, Dev->Dev->Address, op->Endpt->EndpointNum); + host->HostDef->SendIN( + host->Ptr, Dev->Dev->Address, op->Endpt->EndpointNum, + 0, USB_AsyncCallback, op, + DataBuf, Length + ); + + LEAVE('-'); + +// Log_Warning("USB", "TODO: Implement USB_RecvDataA"); +} + +void USB_AsyncCallback(void *Ptr, void *Buf, int Length) +{ + tAsyncOp *op = Ptr; + op->Length = Length; + LOG("adding %p to work queue", op); + Workqueue_AddWork(&gUSB_AsyncQueue, op); +} + +void USB_AsyncThread(void *Unused) +{ + Threads_SetName("USB Async IO Thread"); + for(;;) + { + tAsyncOp *op = Workqueue_GetWork(&gUSB_AsyncQueue); + tUSBInterface *iface = op->Endpt->Interface; + + LOG("op = %p", op); + + iface->Driver->Endpoints[op->Endpt->EndpointIdx].DataAvail( + iface, op->Endpt->EndpointIdx, + op->Length, op->Data); + } +} + diff --git a/KernelLand/Modules/USB/Core/usb_lowlevel.c b/KernelLand/Modules/USB/Core/usb_lowlevel.c new file mode 100644 index 00000000..0e53bad9 --- /dev/null +++ b/KernelLand/Modules/USB/Core/usb_lowlevel.c @@ -0,0 +1,195 @@ +/* + * Acess 2 USB Stack + * - By John Hodge (thePowersGang) + * + * usb_lowlevel.c + * - Low Level IO + */ +#define DEBUG 1 +#include +#include "usb.h" +#include "usb_proto.h" +#include "usb_lowlevel.h" + +// === PROTOTYPES === +void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data); + int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address); + int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest); +char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index); + int _UTF16to8(Uint16 *Input, int InputLen, char *Dest); + +// === CODE === +void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data) +{ + void *hdl; + // TODO: Sanity check (and check that Type is valid) + struct sDeviceRequest req; + req.ReqType = Type; + req.Request = Req; + req.Value = LittleEndian16( Val ); + req.Index = LittleEndian16( Indx ); + req.Length = LittleEndian16( Len ); + + hdl = Host->HostDef->SendSETUP(Host->Ptr, Addr, EndPt, 0, NULL, NULL, &req, sizeof(req)); + + // TODO: Data toggle? + // TODO: Multi-packet transfers + if( Type & 0x80 ) + { + void *hdl2; + + hdl = Host->HostDef->SendIN(Host->Ptr, Addr, EndPt, 0, NULL, NULL, Data, Len); + + hdl2 = Host->HostDef->SendOUT(Host->Ptr, Addr, EndPt, 0, NULL, NULL, NULL, 0); + while( Host->HostDef->IsOpComplete(Host->Ptr, hdl2) == 0 ) + Time_Delay(1); + } + else + { + void *hdl2; + + if( Len > 0 ) + hdl = Host->HostDef->SendOUT(Host->Ptr, Addr, EndPt, 0, NULL, NULL, Data, Len); + else + hdl = NULL; + + // Status phase (DataToggle=1) + hdl2 = Host->HostDef->SendIN(Host->Ptr, Addr, EndPt, 1, NULL, NULL, NULL, 0); + while( Host->HostDef->IsOpComplete(Host->Ptr, hdl2) == 0 ) + Time_Delay(1); + } + return hdl; +} + +int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address) +{ + USB_int_Request(Host, 0, 0, 0x00, 5, Address & 0x7F, 0, 0, NULL); + return 0; +} + +int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest) +{ + const int ciMaxPacketSize = 0x400; + struct sDeviceRequest req; + int bToggle = 0; + void *final; + + req.ReqType = 0x80; + switch( Type & 0xF00 ) + { + case 0x000: req.ReqType |= (0 << 5); break; // Standard + case 0x100: req.ReqType |= (1 << 5); break; // Class + case 0x200: req.ReqType |= (2 << 5); break; // Vendor + } + + req.Request = 6; // GET_DESCRIPTOR + req.Value = LittleEndian16( ((Type & 0xFF) << 8) | (Index & 0xFF) ); + req.Index = LittleEndian16( 0 ); // TODO: Language ID + req.Length = LittleEndian16( Length ); + + Dev->Host->HostDef->SendSETUP( + Dev->Host->Ptr, Dev->Address, Endpoint, + 0, NULL, NULL, + &req, sizeof(req) + ); + + bToggle = 1; + while( Length > ciMaxPacketSize ) + { + Dev->Host->HostDef->SendIN( + Dev->Host->Ptr, Dev->Address, Endpoint, + bToggle, NULL, NULL, + Dest, ciMaxPacketSize + ); + bToggle = !bToggle; + Length -= ciMaxPacketSize; + } + + final = Dev->Host->HostDef->SendIN( + Dev->Host->Ptr, Dev->Address, Endpoint, + bToggle, INVLPTR, NULL, + Dest, Length + ); + + while( Dev->Host->HostDef->IsOpComplete(Dev->Host->Ptr, final) == 0 ) + Time_Delay(1); + + return 0; +} + +char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index) +{ + struct sDescriptor_String str; + int src_len, new_len; + char *ret; + + if(Index == 0) return strdup(""); + + USB_int_ReadDescriptor(Dev, Endpoint, 3, Index, sizeof(str), &str); + if(str.Length < 2) { + Log_Error("USB", "String %p:%i:%i:%i descriptor is undersized (%i)", + Dev->Host, Dev->Address, Endpoint, Index, str.Length); + return NULL; + } +// if(str.Length > sizeof(str)) { +// // IMPOSSIBLE! +// Log_Error("USB", "String is %i bytes, which is over prealloc size (%i)", +// str.Length, sizeof(str) +// ); +// } + src_len = (str.Length - 2) / sizeof(str.Data[0]); + + LOG("&str = %p, src_len = %i", &str, src_len); + + new_len = _UTF16to8(str.Data, src_len, NULL); + ret = malloc( new_len + 1 ); + _UTF16to8(str.Data, src_len, ret); + ret[new_len] = 0; + return ret; +} + +int _UTF16to8(Uint16 *Input, int InputLen, char *Dest) +{ + int str_len, cp_len; + Uint32 saved_bits = 0; + str_len = 0; + for( int i = 0; i < InputLen; i ++) + { + Uint32 cp; + Uint16 val = Input[i]; + if( val >= 0xD800 && val <= 0xDBFF ) + { + // Multibyte - Leading + if(i + 1 > InputLen) { + cp = '?'; + } + else { + saved_bits = (val - 0xD800) << 10; + saved_bits += 0x10000; + continue ; + } + } + else if( val >= 0xDC00 && val <= 0xDFFF ) + { + if( !saved_bits ) { + cp = '?'; + } + else { + saved_bits |= (val - 0xDC00); + cp = saved_bits; + } + } + else + cp = val; + + cp_len = WriteUTF8((Uint8*)Dest, cp); + if(Dest) + Dest += cp_len; + str_len += cp_len; + + saved_bits = 0; + } + + return str_len; +} + diff --git a/KernelLand/Modules/USB/Core/usb_lowlevel.h b/KernelLand/Modules/USB/Core/usb_lowlevel.h new file mode 100644 index 00000000..9159cbac --- /dev/null +++ b/KernelLand/Modules/USB/Core/usb_lowlevel.h @@ -0,0 +1,16 @@ +/** + * Acess2 USB Stack + * - By John Hodge (thePowersGang) + * + * usb_lowlevel.h + * - Low-Level USB IO Functions + */ +#ifndef _USB_LOWLEVEL_H_ +#define _USB_LOWLEVEL_H_ + +extern void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data); +extern int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address); +extern int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest); +extern char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index); + +#endif diff --git a/KernelLand/Modules/USB/Core/usb_poll.c b/KernelLand/Modules/USB/Core/usb_poll.c new file mode 100644 index 00000000..b6927fed --- /dev/null +++ b/KernelLand/Modules/USB/Core/usb_poll.c @@ -0,0 +1,133 @@ +/* + * Acess2 USB Stack + * - By John Hodge (thePowersGang) + * + * usb_poll.c + * - Endpoint polling + */ +#define DEBUG 1 +#include +#include "usb.h" + +#define POLL_ATOM 25 // 25ms atom +#define POLL_MAX 256 // Max period that can be nominated +#define POLL_SLOTS ((int)(POLL_MAX/POLL_ATOM)) + +// === IMPORTS === +extern tUSBHost *gUSB_Hosts; + +// === PROTOTYPES === +void USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint); + +// === GLOBALS === +tUSBEndpoint *gUSB_PollQueues[POLL_MAX/POLL_ATOM]; + int giUSB_PollPosition; // Index into gUSB_PollQueues + +// === CODE === +void USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint) +{ + tUSBEndpoint *endpt; + + // Some sanity checks + if(Endpoint <= 0 || Endpoint > Iface->nEndpoints) return ; + endpt = &Iface->Endpoints[Endpoint-1]; + if(endpt->PollingPeriod > POLL_MAX || endpt->PollingPeriod <= 0) + return ; + + // TODO: Check that this endpoint isn't already on the queue + + endpt->InputData = malloc(endpt->MaxPacketSize); + + // Determine polling period in atoms + endpt->PollingAtoms = (endpt->PollingPeriod + POLL_ATOM-1) / POLL_ATOM; + if(endpt->PollingAtoms > POLL_SLOTS) endpt->PollingAtoms = POLL_SLOTS; + // Add to poll queue + // TODO: Locking + { + int idx = giUSB_PollPosition + 1; + if(idx >= POLL_SLOTS) idx -= POLL_SLOTS; + endpt->Next = gUSB_PollQueues[idx]; + gUSB_PollQueues[idx] = endpt; + } +} + +/** + * \brief USB polling thread + */ +int USB_PollThread(void *unused) +{ + Threads_SetName("USB Polling Thread"); + for(;;) + { + tUSBEndpoint *ep, *prev; + + if(giUSB_PollPosition == 0) + { + // Check hosts + for( tUSBHost *host = gUSB_Hosts; host; host = host->Next ) + { + host->HostDef->CheckPorts(host->Ptr); + } + } + +// Log_Debug("USBPoll", "giUSB_PollPosition = %i", giUSB_PollPosition); + + // A little evil for neater code + prev = (void*)( (tVAddr)&gUSB_PollQueues[giUSB_PollPosition] - offsetof(tUSBEndpoint, Next) ); + + // Process queue +// LOG("giUSB_PollPosition = %i", giUSB_PollPosition); + for( ep = gUSB_PollQueues[giUSB_PollPosition]; ep; prev = ep, ep = ep->Next ) + { + int period_in_atoms = ep->PollingAtoms; +// LOG("%i: ep = %p", giUSB_PollPosition, ep); + + // Check for invalid entries + if(period_in_atoms < 0 || period_in_atoms > POLL_ATOM) + { + Log_Warning("USB", "Endpoint on polling queue with invalid period"); + continue ; + } + // Check for entries to delete + if(period_in_atoms == 0) + { + // Remove + prev->Next = ep->Next; + ep->PollingAtoms = -1; // Mark as removed + ep = prev; // Make sure prev is kept valid + continue ; + } + + // Read data + // TODO: Check the endpoint + // TODO: Async checking? + // - Send the read request on all of them then wait for the first to complete + USB_RecvDataA( + ep->Interface, ep->EndpointIdx+1, + ep->MaxPacketSize, ep->InputData, + ep->Interface->Driver->Endpoints[ep->EndpointIdx].DataAvail + ); + + // Call callback + + // Reschedule + if( period_in_atoms != POLL_SLOTS ) + { + int newqueue_id = (giUSB_PollPosition + period_in_atoms) % POLL_SLOTS; + tUSBEndpoint **newqueue = &gUSB_PollQueues[newqueue_id]; + + prev->Next = ep->Next; + + ep->Next = *newqueue; + *newqueue = ep; + ep = prev; + } + } + giUSB_PollPosition ++; + if(giUSB_PollPosition == POLL_SLOTS) + giUSB_PollPosition = 0; + // TODO: Check for a longer delay + Time_Delay(POLL_ATOM); + } +} + diff --git a/KernelLand/Modules/USB/Core/usb_proto.h b/KernelLand/Modules/USB/Core/usb_proto.h new file mode 100644 index 00000000..5641dd24 --- /dev/null +++ b/KernelLand/Modules/USB/Core/usb_proto.h @@ -0,0 +1,111 @@ +/** + * Acess2 USB Stack + * - By John Hodge (thePowersGang) + * + * usb_proto.h + * - USB Core Protocol Definitions + */ +#ifndef _USB_PROTO_H_ +#define _USB_PROTO_H_ + +struct sDeviceRequest +{ + Uint8 ReqType; + Uint8 Request; + Uint16 Value; + Uint16 Index; + Uint16 Length; +}; + +/* + */ +struct sDescriptor_Device +{ + Uint8 Length; + Uint8 Type; // = 1 + Uint16 USBVersion; // BCD, 0x210 = 2.10 + Uint8 DeviceClass; + Uint8 DeviceSubClass; + Uint8 DeviceProtocol; + Uint8 MaxPacketSize; + + Uint16 VendorID; + Uint16 ProductID; + Uint16 DeviceID; // BCD + + Uint8 ManufacturerStr; + Uint8 ProductStr; + Uint8 SerialNumberStr; + + Uint8 NumConfigurations; +} PACKED; + +struct sDescriptor_Configuration +{ + Uint8 Length; + Uint8 Type; // = 2 + + Uint16 TotalLength; + Uint8 NumInterfaces; + Uint8 ConfigurationValue; + Uint8 ConfigurationStr; + Uint8 AttributesBmp; + Uint8 MaxPower; // in units of 2 mA +} PACKED; + +struct sDescriptor_String +{ + Uint8 Length; + Uint8 Type; // = 3 + + Uint16 Data[128-1]; // (256 bytes - 2 bytes) / Uint16 +} PACKED; + +struct sDescriptor_Interface +{ + Uint8 Length; + Uint8 Type; // = 4 + + Uint8 InterfaceNum; + Uint8 AlternateSetting; + Uint8 NumEndpoints; // Excludes endpoint 0 + + Uint8 InterfaceClass; // + Uint8 InterfaceSubClass; + Uint8 InterfaceProtocol; + + Uint8 InterfaceStr; +} PACKED; + +struct sDescriptor_Endpoint +{ + Uint8 Length; + Uint8 Type; // = 5 + Uint8 Address; // 3:0 Endpoint Num, 7: Direction (1=IN) + /** + * 1:0 - Transfer Type + * - 00 = Control + * - 01 = Isochronous + * - 10 = Bulk + * - 11 = Interrupt + * 3:2 - Synchronisation type (Isonchronous only) + * - 00 = No Synchronisation + * - 01 = Asynchronous + * - 10 = Adaptive + * - 11 = Synchronous + * 5:4 - Usage type (Isonchronous only) + * - 00 = Data endpoint + * - 01 = Feedback endpoint + */ + Uint8 Attributes; + + Uint16 MaxPacketSize; + + /** + * + */ + Uint8 PollingInterval; +} PACKED; + +#endif + diff --git a/KernelLand/Modules/USB/HID/hid.h b/KernelLand/Modules/USB/HID/hid.h new file mode 100644 index 00000000..373496e8 --- /dev/null +++ b/KernelLand/Modules/USB/HID/hid.h @@ -0,0 +1,47 @@ +/* + * Acess2 USB Stack HID Driver + * - By John Hodge (thePowersGang) + * + * hid.h + * - Core header + */ +#ifndef _HID_H_ +#define _HID_H_ + +// Report Descriptor Types +// - 0: Main +// > 8: Input - Axis/Button etc +// > 9: Output - LED/Position +// > B: Feature - +// > A: Collection - Group of Input/Output/Feature +// > C: End collection +// - 1: Global (Not restored on main) +// - 2: Local +// > +// - 3: Reserved/Unused + +// I/O/F Data Values +#define HID_IOF_CONSTANT 0x001 //!< Host Read-only +#define HID_IOF_VARIABLE 0x002 //!< + +struct sLongItem +{ + Uint8 Header; // 0xFE (Tag 15, Type 3, Size 2) + Uint8 DataSize; + Uint8 Tag; +}; + +struct sDescriptor_HID +{ + Uint8 Length; + Uint8 Type; // + Uint16 Version; // 0x0111 = 1.11 + Uint8 CountryCode; + Uint8 NumDescriptors; // >= 1 + struct { + Uint8 DescType; + Uint16 DescLen; + } Descriptors[]; +} + +#endif diff --git a/KernelLand/Modules/USB/HID/keysyms.h b/KernelLand/Modules/USB/HID/keysyms.h new file mode 100644 index 00000000..558464e5 --- /dev/null +++ b/KernelLand/Modules/USB/HID/keysyms.h @@ -0,0 +1,124 @@ +/* + * Acess2 USB Stack HID Driver + * - By John Hodge (thePowersGang) + * + * usb_keysyms.h + * - USB HID Keyboard Symbols + */ +#ifndef _USB_KEYSYMS_H_ +#define _USB_KEYSYMS_H_ + +enum eUSB_Keysyms +{ + KEYSYM_NONE, + KEYSYM_ERRORROLLOVER, + KEYSYM_POSTFAIL, + KEYSYM_ERRORUNDEFINED, + // 0x04 / 4 + KEYSYM_a, KEYSYM_b, KEYSYM_c, + KEYSYM_d, KEYSYM_e, KEYSYM_f, + KEYSYM_g, KEYSYM_h, KEYSYM_i, + KEYSYM_j, KEYSYM_k, KEYSYM_l, + KEYSYM_m, KEYSYM_n, KEYSYM_o, + KEYSYM_p, KEYSYM_q, KEYSYM_r, + KEYSYM_s, KEYSYM_t, KEYSYM_u, + KEYSYM_v, KEYSYM_w, KEYSYM_x, + KEYSYM_y, KEYSYM_z, + + // 0x1E / 30 + KEYSYM_1, KEYSYM_2, + KEYSYM_3, KEYSYM_4, + KEYSYM_5, KEYSYM_6, + KEYSYM_7, KEYSYM_8, + KEYSYM_9, KEYSYM_0, + + KEYSYM_RETURN, // Enter + KEYSYM_ESC, // Esc. + KEYSYM_BACKSP, // Backspace + KEYSYM_TAB, // Tab + KEYSYM_SPACE, // Spacebar + KEYSYM_MINUS, // - _ + KEYSYM_EQUALS, // = + + KEYSYM_SQUARE_OPEN, // [ { + KEYSYM_SQUARE_CLOSE, // ] } + KEYSYM_BACKSLASH, // \ | + KEYSYM_HASH_TILDE, // # ~ (Non-US) + KEYSYM_SEMICOLON, // ; : + KEYSYM_QUOTE, // ' " + KEYSYM_GRAVE_TILDE, // Grave Accent, Tilde + KEYSYM_COMMA, // , < + KEYSYM_PERIOD, // . > + KEYSYM_SLASH, // / ? + KEYSYM_CAPS, // Caps Lock + KEYSYM_F1, KEYSYM_F2, + KEYSYM_F3, KEYSYM_F4, + KEYSYM_F5, KEYSYM_F6, + KEYSYM_F7, KEYSYM_F8, + KEYSYM_F9, KEYSYM_F10, + KEYSYM_F11, KEYSYM_F12, + KEYSYM_PRINTSCREEN, + KEYSYM_SCROLLLOCK, + KEYSYM_PAUSE, + KEYSYM_INSERT, + KEYSYM_HOME, + KEYSYM_PGUP, + KEYSYM_DELETE, + KEYSYM_END, + KEYSYM_PGDN, + KEYSYM_RIGHTARROW, + KEYSYM_LEFTARROW, + KEYSYM_DOWNARROW, + KEYSYM_UPARROW, + + KEYSYM_NUMLOCK, + KEYSYM_KPSLASH, + KEYSYM_KPSTAR, + KEYSYM_KPMINUS, + KEYSYM_KPPLUS, + KEYSYM_KPENTER, + KEYSYM_KP1, + KEYSYM_KP2, + KEYSYM_KP3, + KEYSYM_KP4, + KEYSYM_KP5, + KEYSYM_KP7, + KEYSYM_KP8, + KEYSYM_KP9 + KEYSYM_KP0, + KEYSYM_KPPERIOD, + + KEYSYM_NONUS_BACKSLASH, + KEYSYM_APPLICATION, // Windows Key + KEYSYM_POWER, + KEYSYM_KPEQUALS, + + KEYSYM_F13, KEYSYM_F14, + KEYSYM_F15, KEYSYM_F16, + KEYSYM_F17, KEYSYM_F18, + KEYSYM_F19, KEYSYM_F20, + KEYSYM_F21, KEYSYM_F22, + KEYSYM_F23, KEYSYM_F24, + KEYSYM_EXECUTE, + KEYSYM_HELP, + KEYSYM_MENU, + KEYSYM_SELECT, + KEYSYM_STOP, + KEYSYM_AGAIN, + KEYSYM_UNDO, + KEYSYM_CUT, + KEYSYM_COPY, + KEYSYM_PASTE, + KEYSYM_FIND, + KEYSYM_MUTE, + KEYSYM_VOLUP, + KEYSYM_VOLDN, + KEYSYM_LOCKING_CAPS, // Physically toggles + KEYSYM_LOGKING_NUM, + KEYSYM_LOGKING_SCROLL, + KEYSYM_KPCOMMA + + // TODO: Define the rest +}; + +#endif + diff --git a/KernelLand/Modules/USB/HID/main.c b/KernelLand/Modules/USB/HID/main.c new file mode 100644 index 00000000..31375b0b --- /dev/null +++ b/KernelLand/Modules/USB/HID/main.c @@ -0,0 +1,36 @@ +/* + * Acess2 USB Stack HID Driver + * - By John Hodge (thePowersGang) + * + * main.c + * - Driver Core + */ +#define DEBUG 0 +#define VERSION VER2(0,1) +#include +#include + +// === PROTOTYPES === + int HID_Initialise(const char **Arguments); +void HID_DeviceConnected(tUSBInterface *Dev); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, USB_HID, HID_Initialise, NULL, "USB_Core", NULL); +tUSBDriver gHID_Driver = { + .Name = "HID", + .Match = {.Class = {0x030000, 0xFF0000}}, + .Connected = HID_DeviceConnected, +}; + +// === CODE === +int HID_Initialise(const char **Arguments) +{ + USB_RegisterDriver( &gHID_Driver ); + return 0; +} + +void HID_DeviceConnected(tUSBInterface *Dev) +{ + +} + diff --git a/KernelLand/Modules/USB/Makefile.tpl b/KernelLand/Modules/USB/Makefile.tpl new file mode 100644 index 00000000..8df70988 --- /dev/null +++ b/KernelLand/Modules/USB/Makefile.tpl @@ -0,0 +1,3 @@ +CATEGORY = USB + +-include ../../Makefile.tpl diff --git a/KernelLand/Modules/USB/OHCI/ohci.h b/KernelLand/Modules/USB/OHCI/ohci.h new file mode 100644 index 00000000..30eeb962 --- /dev/null +++ b/KernelLand/Modules/USB/OHCI/ohci.h @@ -0,0 +1,81 @@ +/* + * Acess2 OHCI Driver + * - By John Hodge (thePowersGang) + * + * ohci.h + * - Core Header + */ +#ifndef _OHCI_H_ +#define _OHCI_H_ + +struct sEndpointDesc +{ + // 0: 6 = Address + // 7:10 = Endpoint Num + // 11:12 = Direction (TD, OUT, IN, TD) + // 13 = Speed (Full, Low) + // 14 = Skip entry + // 15 = Format (Others, Isochronous) + // 16:26 = Max Packet Size + // 27:31 = AVAIL + Uint32 Flags; + // 0: 3 = AVAIL + // 4:31 = TailP + Uint32 TailP; // Last TD in queue + // 0 = Halted (Queue stopped due to error) + // 1 = Data toggle carry + // 2: 3 = ZERO + // 4:31 = HeadP + Uint32 HeadP; // First TD in queue + // 0: 3 = AVAIL + // 4:31 = NextED + Uint32 NextED; // Next endpoint descriptor +}; + +struct sGeneralTD +{ + // 0:17 = AVAIL + // 18 = Buffer Rounding (Allow an undersized packet) + // 19:20 = Direction (SETUP, OUT, IN, Resvd) + // 21:23 = Delay Interrupt (Frame count, 7 = no int) + // 24:25 = Data Toggle (ToggleCarry, ToggleCarry, 0, 1) + // 26:27 = Error Count + // 28:31 = Condition Code + Uint32 Flags; + + // Base address of packet (or current when being read) + Uint32 CBP; + + Uint32 NextTD; + + // Address of final byte in buffer + Uint32 BE; +}; + +struct sIsochronousTD +{ + // 0:15 = Starting Frame + // 16:20 = AVAIL + // 21:23 = Delay Interrupt + // 24:26 = Frame Count - 1 (1, 2, 3, 4, 5, 6, 7, 8) + // 27 = AVAIL + // 28:31 = Condition Code + Uint32 Flags; + + // 0:11 = AVAIL + // 12:31 = Page number of first byte in buffer + Uint32 BP0; // Buffer Page 0 + + Uint32 NextTD; + + // Address of last byte in buffer + Uint32 BufferEnd; + + // 0:11 = Page Offset + // 12 = Page selector (BufferPage0, BufferEnd) + // 13:15 = Unused? + Uint16 Offsets[8]; +}; + +#endif + diff --git a/KernelLand/Modules/USB/UHCI/Makefile b/KernelLand/Modules/USB/UHCI/Makefile new file mode 100644 index 00000000..798159af --- /dev/null +++ b/KernelLand/Modules/USB/UHCI/Makefile @@ -0,0 +1,8 @@ +# +# + +OBJ = uhci.o +CPPFLAGS = -I../Core/include +NAME = UHCI + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/USB/UHCI/uhci.c b/KernelLand/Modules/USB/UHCI/uhci.c new file mode 100644 index 00000000..7b40b143 --- /dev/null +++ b/KernelLand/Modules/USB/UHCI/uhci.c @@ -0,0 +1,456 @@ +/* + * Acess 2 USB Stack + * - By John Hodge (thePowersGang) + * + * Universal Host Controller Interface + */ +#define DEBUG 0 +#define VERSION VER2(0,5) +#include +#include +#include +#include +#include +#include "uhci.h" + +// === CONSTANTS === +#define MAX_CONTROLLERS 4 +#define NUM_TDs 1024 + +// === PROTOTYPES === + int UHCI_Initialise(char **Arguments); +void UHCI_Cleanup(); +tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont); +void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD); +void *UHCI_int_SendTransaction(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); +void *UHCI_DataIN(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); +void *UHCI_DataOUT(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); +void *UHCI_SendSetup(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); + int UHCI_IsTransferComplete(void *Ptr, void *Handle); + int UHCI_Int_InitHost(tUHCI_Controller *Host); +void UHCI_CheckPortUpdate(void *Ptr); +void UHCI_InterruptHandler(int IRQ, void *Ptr); +// +static void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value); +static void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value); +static void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value); +static Uint16 _InWord(tUHCI_Controller *Host, int Reg); + +// === GLOBALS === +MODULE_DEFINE(0, VERSION, USB_UHCI, UHCI_Initialise, NULL, "USB_Core", NULL); +tUHCI_TD gaUHCI_TDPool[NUM_TDs]; +tUHCI_Controller gUHCI_Controllers[MAX_CONTROLLERS]; +tUSBHostDef gUHCI_HostDef = { + .SendIN = UHCI_DataIN, + .SendOUT = UHCI_DataOUT, + .SendSETUP = UHCI_SendSetup, + .CheckPorts = UHCI_CheckPortUpdate, + .IsOpComplete = UHCI_IsTransferComplete + }; + +// === CODE === +/** + * \fn int UHCI_Initialise() + * \brief Called to initialise the UHCI Driver + */ +int UHCI_Initialise(char **Arguments) +{ + int i=0, id=-1; + int ret; + + ENTER(""); + + // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices + while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS ) + { + tUHCI_Controller *cinfo = &gUHCI_Controllers[i]; + Uint32 base_addr; + // NOTE: Check "protocol" from PCI? + + cinfo->PciId = id; + base_addr = PCI_GetBAR(id, 4); + + if( base_addr & 1 ) + { + cinfo->IOBase = base_addr & ~1; + cinfo->MemIOMap = NULL; + } + else + { + cinfo->MemIOMap = (void*)MM_MapHWPages(base_addr, 1); + } + cinfo->IRQNum = PCI_GetIRQ(id); + + Log_Debug("UHCI", "Controller PCI #%i: IO Base = 0x%x, IRQ %i", + id, base_addr, cinfo->IRQNum); + + IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo); + + // Initialise Host + ret = UHCI_Int_InitHost(&gUHCI_Controllers[i]); + // Detect an error + if(ret != 0) { + LEAVE('i', ret); + return ret; + } + + cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2); + LOG("cinfo->RootHub = %p", cinfo->RootHub); + + i ++; + } + + if(i == 0) { + LEAVE('i', MODULE_ERR_NOTNEEDED); + return MODULE_ERR_NOTNEEDED; + } + + if(i == MAX_CONTROLLERS) { + Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest"); + } + LEAVE('i', MODULE_ERR_OK); + return MODULE_ERR_OK; +} + +/** + * \fn void UHCI_Cleanup() + * \brief Called just before module is unloaded + */ +void UHCI_Cleanup() +{ +} + +tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont) +{ + int i; + for(i = 0; i < NUM_TDs; i ++) + { + if(gaUHCI_TDPool[i].Link == 0) { + gaUHCI_TDPool[i].Link = 1; + gaUHCI_TDPool[i].Control = 1 << 23; + return &gaUHCI_TDPool[i]; + } + // Still in use? Skip + if( gaUHCI_TDPool[i].Control & (1 << 23) ) + continue ; + // Is there a callback on it? Skip + if( gaUHCI_TDPool[i]._info.Callback ) + continue ; + // TODO: Garbage collect, but that means removing from the list too + #if 0 + // Ok, this is actually unused + gaUHCI_TDPool[i].Link = 1; + gaUHCI_TDPool[i].Control = 1 << 23; + return &gaUHCI_TDPool[i]; + #endif + } + return NULL; +} + +tUHCI_TD *UHCI_int_GetTDFromPhys(tPAddr PAddr) +{ + // TODO: Fix this to work with a non-contiguous pool + static tPAddr td_pool_base; + const int pool_size = NUM_TDs; + int offset; + if(!td_pool_base) td_pool_base = MM_GetPhysAddr( (tVAddr)gaUHCI_TDPool ); + offset = (PAddr - td_pool_base) / sizeof(gaUHCI_TDPool[0]); + if( offset < 0 || offset >= pool_size ) + { + Log_Error("UHCI", "TD PAddr %P not from pool", PAddr); + return NULL; + } + return gaUHCI_TDPool + offset; +} + +void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD) +{ + int next_frame = (_InWord(Cont, FRNUM) + 2) & (1024-1); + tUHCI_TD *prev_td; + Uint32 link; + + // TODO: How to handle FRNUM incrementing while we are in this function? + + // Empty list + if( Cont->FrameList[next_frame] & 1 ) + { + // TODO: Ensure 32-bit paddr + Cont->FrameList[next_frame] = MM_GetPhysAddr( (tVAddr)TD ); + TD->Control |= (1 << 24); // Ensure that there is an interrupt for each used frame + LOG("next_frame = %i", next_frame); + return; + } + + // Find the end of the list + link = Cont->FrameList[next_frame]; + do { + prev_td = UHCI_int_GetTDFromPhys(link); + link = prev_td->Link; + } while( !(link & 1) ); + + // Append + prev_td->Link = MM_GetPhysAddr( (tVAddr)TD ); + + LOG("next_frame = %i, prev_td = %p", next_frame, prev_td); +} + +/** + * \brief Send a transaction to the USB bus + * \param Cont Controller pointer + * \param Addr Function Address * 16 + Endpoint + * \param bTgl Data toggle value + */ +void *UHCI_int_SendTransaction( + tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, + tUSBHostCb Cb, void *CbData, void *Data, size_t Length) +{ + tUHCI_TD *td; + + if( Length > 0x400 ) return NULL; // Controller allows up to 0x500, but USB doesn't + + td = UHCI_int_AllocateTD(Cont); + + if( !td ) { + // TODO: Wait for one to free? + Log_Error("UHCI", "No avaliable TDs, transaction dropped"); + return NULL; + } + + td->Link = 1; + td->Control = (Length - 1) & 0x7FF; + td->Control |= (1 << 23); + td->Token = ((Length - 1) & 0x7FF) << 21; + td->Token |= (bTgl & 1) << 19; + td->Token |= (Addr & 0xF) << 15; + td->Token |= ((Addr/16) & 0xFF) << 8; + td->Token |= Type; + + // TODO: Ensure 32-bit paddr + if( ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE ) { + Log_Warning("UHCI", "TODO: Support non single page transfers (%x + %x > %x)", + (tVAddr)Data & (PAGE_SIZE-1), Length, PAGE_SIZE + ); + // TODO: Need to enable IOC to copy the data back +// td->BufferPointer = + td->_info.bCopyData = 1; + return NULL; + } + else { + td->BufferPointer = MM_GetPhysAddr( (tVAddr)Data ); + td->_info.bCopyData = 0; + } + + // Interrupt on completion + if( Cb ) { + td->Control |= (1 << 24); + LOG("IOC Cb=%p CbData=%p", Cb, CbData); + td->_info.Callback = Cb; // NOTE: if ERRPTR then the TD is kept allocated until checked + td->_info.CallbackPtr = CbData; + } + + td->_info.DataPtr = Data; + + UHCI_int_AppendTD(Cont, td); + + return td; +} + +void *UHCI_DataIN(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length) +{ + return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0x69, DataTgl, Cb, CbData, Buf, Length); +} + +void *UHCI_DataOUT(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length) +{ + return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0xE1, DataTgl, Cb, CbData, Buf, Length); +} + +void *UHCI_SendSetup(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length) +{ + return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0x2D, DataTgl, Cb, CbData, Buf, Length); +} + +int UHCI_IsTransferComplete(void *Ptr, void *Handle) +{ + tUHCI_TD *td = Handle; + int ret; + ret = !(td->Control & (1 << 23)); + if(ret) { + td->_info.Callback = NULL; + td->Link = 0; + } + return ret; +} + +// === INTERNAL FUNCTIONS === +/** + * \fn int UHCI_Int_InitHost(tUCHI_Controller *Host) + * \brief Initialises a UHCI host controller + * \param Host Pointer - Host to initialise + */ +int UHCI_Int_InitHost(tUHCI_Controller *Host) +{ + ENTER("pHost", Host); + + _OutWord( Host, USBCMD, 4 ); // GRESET + // TODO: Wait for at least 10ms + _OutWord( Host, USBCMD, 0 ); // GRESET + + // Allocate Frame List + // - 1 Page, 32-bit address + // - 1 page = 1024 4 byte entries + Host->FrameList = (void *) MM_AllocDMA(1, 32, &Host->PhysFrameList); + if( !Host->FrameList ) { + Log_Warning("UHCI", "Unable to allocate frame list, aborting"); + LEAVE('i', -1); + return -1; + } + LOG("Allocated frame list 0x%x (0x%x)", Host->FrameList, Host->PhysFrameList); + memsetd( Host->FrameList, 1, 1024 ); // Clear List (Disabling all entries) + + //! \todo Properly fill frame list + + // Set frame length to 1 ms + _OutByte( Host, SOFMOD, 64 ); + + // Set Frame List + _OutDWord( Host, FLBASEADD, Host->PhysFrameList ); + _OutWord( Host, FRNUM, 0 ); + + // Enable Interrupts + _OutWord( Host, USBINTR, 0x000F ); + PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 ); + + // Enable processing + _OutWord( Host, USBCMD, 0x0001 ); + + LEAVE('i', 0); + return 0; +} + +void UHCI_CheckPortUpdate(void *Ptr) +{ + tUHCI_Controller *Host = Ptr; + // Enable ports + for( int i = 0; i < 2; i ++ ) + { + int port = PORTSC1 + i*2; + Uint16 status; + + status = _InWord(Host, port); + // Check for port change + if( !(status & 0x0002) ) continue; + _OutWord(Host, port, 0x0002); + + // Check if the port is connected + if( !(status & 1) ) + { + // Tell the USB code it's gone. + USB_DeviceDisconnected(Host->RootHub, i); + continue; + } + else + { + LOG("Port %i has something", i); + // Reset port (set bit 9) + LOG("Reset"); + _OutWord(Host, port, 0x0200); + Time_Delay(50); // 50ms delay + _OutWord(Host, port, _InWord(Host, port) & ~0x0200); + // Enable port + LOG("Enable"); + Time_Delay(50); // 50ms delay + _OutWord(Host, port, _InWord(Host, port) | 0x0004); + // Tell USB there's a new device + USB_DeviceConnected(Host->RootHub, i); + } + } +} + +void UHCI_InterruptHandler(int IRQ, void *Ptr) +{ + tUHCI_Controller *Host = Ptr; + int frame = (_InWord(Host, FRNUM) - 1) & 0x3FF; + Uint16 status = _InWord(Host, USBSTS); +// Log_Debug("UHCI", "UHIC Interrupt, status = 0x%x, frame = %i", status, frame); + + // Interrupt-on-completion + if( status & 1 ) + { + tPAddr link; + + for( int i = 0; i < 10; i ++ ) + { + link = Host->FrameList[frame]; + Host->FrameList[frame] = 1; + while( link && !(link & 1) ) + { + tUHCI_TD *td = UHCI_int_GetTDFromPhys(link); + int byte_count = (td->Control&0x7FF)+1; + LOG("link = 0x%x, td = %p, byte_count = %i", link, td, byte_count); + // Handle non-page aligned destination + // TODO: This will break if the destination is not in global memory + if(td->_info.bCopyData) + { + void *ptr = (void*)MM_MapTemp(td->BufferPointer); + Log_Debug("UHCI", "td->_info.DataPtr = %p", td->_info.DataPtr); + memcpy(td->_info.DataPtr, ptr, byte_count); + MM_FreeTemp((tVAddr)ptr); + } + // Callback + if(td->_info.Callback && td->_info.Callback != INVLPTR) + { + LOG("Calling cb %p", td->_info.Callback); + td->_info.Callback(td->_info.CallbackPtr, td->_info.DataPtr, byte_count); + td->_info.Callback = NULL; + } + link = td->Link; + if( td->_info.Callback != INVLPTR ) + td->Link = 0; + } + + if(frame == 0) + frame = 0x3ff; + else + frame --; + } + +// Host->LastCleanedFrame = frame; + } + + LOG("status = 0x%02x", status); + _OutWord(Host, USBSTS, status); +} + +void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value) +{ + if( Host->MemIOMap ) + ((Uint8*)Host->MemIOMap)[Reg] = Value; + else + outb(Host->IOBase + Reg, Value); +} + +void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value) +{ + if( Host->MemIOMap ) + Host->MemIOMap[Reg/2] = Value; + else + outw(Host->IOBase + Reg, Value); +} + +void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value) +{ + if( Host->MemIOMap ) + ((Uint32*)Host->MemIOMap)[Reg/4] = Value; + else + outd(Host->IOBase + Reg, Value); +} + +Uint16 _InWord(tUHCI_Controller *Host, int Reg) +{ + if( Host->MemIOMap ) + return Host->MemIOMap[Reg/2]; + else + return inw(Host->IOBase + Reg); +} + diff --git a/KernelLand/Modules/USB/UHCI/uhci.h b/KernelLand/Modules/USB/UHCI/uhci.h new file mode 100644 index 00000000..a93459a9 --- /dev/null +++ b/KernelLand/Modules/USB/UHCI/uhci.h @@ -0,0 +1,236 @@ +/* + * AcessOS Version 1 + * USB Stack + * - Universal Host Controller Interface + */ +#ifndef _UHCI_H_ +#define _UHCI_H_ + +// === TYPES === +typedef struct sUHCI_Controller tUHCI_Controller; +typedef struct sUHCI_TD tUHCI_TD; +typedef struct sUHCI_QH tUHCI_QH; + +// === STRUCTURES === +struct sUHCI_Controller +{ + /** + * \brief PCI Device ID + */ + Uint16 PciId; + + /** + * \brief IO Base Address + */ + Uint16 IOBase; + + /** + * \brief Memory Mapped-IO base address + */ + Uint16 *MemIOMap; + + /** + * \brief IRQ Number assigned to the device + */ + int IRQNum; + + /** + * \brief Number of the last frame to be cleaned + */ + int LastCleanedFrame; + + /** + * \brief Frame list + * + * 31:4 - Frame Pointer + * 3:2 - Reserved + * 1 - QH/TD Selector + * 0 - Terminate (Empty Pointer) + */ + Uint32 *FrameList; + + /** + * \brief Physical Address of the Frame List + */ + tPAddr PhysFrameList; + + tUSBHub *RootHub; +}; + +struct sUHCI_TD +{ + /** + * \brief Next Entry in list + * + * 31:4 - Address + * 3 - Reserved + * 2 - Depth/Breadth Select + * 1 - QH/TD Select + * 0 - Terminate (Last in List) + */ + Uint32 Link; + + /** + * \brief Control and Status Field + * + * 31:30 - Reserved + * 29 - Short Packet Detect (Input Only) + * 28:27 - Number of Errors Allowed + * 26 - Low Speed Device (Communicating with a low speed device) + * 25 - Isynchonious Select + * 24 - Interrupt on Completion (IOC) + * 23:16 - Status + * 23 - Active + * 22 - Stalled + * 21 - Data Buffer Error + * 20 - Babble Detected + * 19 - NAK Detected + * 18 - CRC/Timout Error + * 17 - Bitstuff Error + * 16 - Reserved + * 15:11 - Reserved + * 10:0 - Actual Length (Number of bytes transfered) + */ + Uint32 Control; + + /** + * \brief Packet Header + * + * 31:21 - Maximum Length (0=1, Max 0x4FF, 0x7FF=0) + * 20 - Reserved + * 19 - Data Toggle + * 18:15 - Endpoint + * 14:8 - Device Address + * 7:0 - PID (Packet Identifcation) - Only 96, E1, 2D allowed + * + * 0x96 = Data IN + * 0xE1 = Data Out + * 0x2D = Setup + */ + Uint32 Token; + + /** + * \brief Pointer to the data to send + */ + Uint32 BufferPointer; + + struct + { + tUSBHostCb Callback; + void *CallbackPtr; + void *DataPtr; + int bCopyData; + } _info; +} __attribute__((aligned(16))); + +struct sUHCI_QH +{ + /** + * \brief Next Entry in list + * + * 31:4 - Address + * 3:2 - Reserved + * 1 - QH/TD Select + * 0 - Terminate (Last in List) + */ + Uint32 Next; + + + /** + * \brief Next Entry in list + * + * 31:4 - Address + * 3:2 - Reserved + * 1 - QH/TD Select + * 0 - Terminate (Last in List) + */ + Uint32 Child; +}; + +// === ENUMERATIONS === +enum eUHCI_IOPorts { + /** + * \brief USB Command Register + * + * 15:8 - Reserved + * 7 - Maximum Packet Size selector (1: 64 bytes, 0: 32 bytes) + * 6 - Configure Flag (No Hardware Effect) + * 5 - Software Debug (Don't think it will be needed) + * 4 - Force Global Resume + * 3 - Enter Global Suspend Mode + * 2 - Global Reset (Resets all devices on the bus) + * 1 - Host Controller Reset (Reset just the controller) + * 0 - Run/Stop + */ + USBCMD = 0x00, + /** + * \brief USB Status Register + * + * 15:6 - Reserved + * 5 - HC Halted, set to 1 when USBCMD:RS is set to 0 + * 4 - Host Controller Process Error (Errors related to the bus) + * 3 - Host System Error (Errors related to the OS/PCI Bus) + * 2 - Resume Detect (Set if a RESUME command is sent to the Controller) + * 1 - USB Error Interrupt + * 0 - USB Interrupts (Set if a transaction with the IOC bit set is completed) + */ + USBSTS = 0x02, + /** + * \brief USB Interrupt Enable Register + * + * 15:4 - Reserved + * 3 - Short Packet Interrupt Enable + * 2 - Interrupt on Complete (IOC) Enable + * 1 - Resume Interrupt Enable + * 0 - Timout / CRC Error Interrupt Enable + */ + USBINTR = 0x04, + /** + * \brief Frame Number (Index into the Frame List) + * + * 15:11 - Reserved + * 10:0 - Index (Incremented each approx 1ms) + */ + FRNUM = 0x06, + /** + * \brief Frame List Base Address + * + * 31:12 - Pysical Address >> 12 + * 11:0 - Reserved (Set to Zero) + */ + FLBASEADD = 0x08, // 32-bit + /** + * \brief Start-of-frame Modify Register + * \note 8-bits only + * + * Sets the size of a frame + * Frequency = (11936+n)/12000 kHz + * + * 7 - Reserved + * 6:0 - + */ + SOFMOD = 0x0C, // 8bit + /** + * \brief Port Status and Controll Register (Port 1) + * + * 15:13 - Reserved + * 12 - Suspend + * 11:10 - Reserved + * 9 - Port Reset + * 8 - Low Speed Device Attached + * 5:4 - Line Status + * 3 - Port Enable/Disable Change - Used for detecting device removal + * 2 - Port Enable/Disable + * 1 - Connect Status Change + * 0 - Current Connect Status + */ + PORTSC1 = 0x10, + /** + * \brief Port Status and Controll Register (Port 2) + * + * See ::PORTSC1 + */ + PORTSC2 = 0x12 +}; + +#endif diff --git a/KernelLand/Modules/armv7/GIC/Makefile b/KernelLand/Modules/armv7/GIC/Makefile new file mode 100644 index 00000000..d4a72b6a --- /dev/null +++ b/KernelLand/Modules/armv7/GIC/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ := gic.o +NAME := GIC + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/armv7/GIC/gic.c b/KernelLand/Modules/armv7/GIC/gic.c new file mode 100644 index 00000000..8dbab3a3 --- /dev/null +++ b/KernelLand/Modules/armv7/GIC/gic.c @@ -0,0 +1,91 @@ +/* + * ARMv7 GIC Support + * - By John Hodge (thePowersGang) + * + * gic.c + * - GIC Core + */ +#define DEBUG 1 + +#include +#include +#include "gic.h" +#include + +#define N_IRQS 1024 + +// === IMPORTS === +extern void *gpIRQHandler; + +// === TYPES === +typedef void (*tIRQ_Handler)(int, void*); + +// === PROTOTYPES === + int GIC_Install(char **Arguments); +void GIC_IRQHandler(void); + +// === GLOBALS === +MODULE_DEFINE(0, 0x100, armv7_GIC, GIC_Install, NULL, NULL); +Uint32 *gpGIC_DistributorBase; +Uint32 *gpGIC_InterfaceBase; +tPAddr gGIC_DistributorAddr; +tPAddr gGIC_InterfaceAddr; +tIRQ_Handler gaIRQ_Handlers[N_IRQS]; +void *gaIRQ_HandlerData[N_IRQS]; + +// === CODE === +int GIC_Install(char **Arguments) +{ + // Realview PB + gGIC_InterfaceAddr = 0x1e000000; + gGIC_DistributorAddr = 0x1e001000; + + // Initialise + gpGIC_InterfaceBase = (void*)MM_MapHWPages(gGIC_InterfaceAddr, 1); + LOG("gpGIC_InterfaceBase = %p", gpGIC_InterfaceBase); + gpGIC_DistributorBase = (void*)MM_MapHWPages(gGIC_DistributorAddr, 1); + LOG("gpGIC_DistributorBase = %p", gpGIC_DistributorBase); + + gpGIC_InterfaceBase[GICC_PMR] = 0xFF; + gpGIC_InterfaceBase[GICC_CTLR] = 1; // Enable CPU + gpGIC_DistributorBase[GICD_CTLR] = 1; // Enable Distributor + + gpIRQHandler = GIC_IRQHandler; + + __asm__ __volatile__ ("cpsie if"); // Enable IRQs and FIQs + + return MODULE_ERR_OK; +} + +void GIC_IRQHandler(void) +{ + Uint32 num = gpGIC_InterfaceBase[GICC_IAR]; +// Log_Debug("GIC", "IRQ 0x%x", num); + gaIRQ_Handlers[num]( num, gaIRQ_HandlerData[num] ); + gpGIC_InterfaceBase[GICC_EOIR] = num; +} + +int IRQ_AddHandler(int IRQ, tIRQ_Handler Handler, void *Ptr) +{ + if( IRQ < 0 || IRQ >= N_IRQS-32 ) { + return 1; + } + + LOG("IRQ = %i", IRQ); + IRQ += 32; // 32 internal IRQs + LOG("IRQ = %i (after adjust)", IRQ); + LOG("mask = 0x%x", 1 << (IRQ & (31-1))); + gpGIC_DistributorBase[GICD_ISENABLER0+IRQ/32] = 1 << (IRQ & (32-1)); + ((Uint8*)&gpGIC_DistributorBase[GICD_ITARGETSR0])[IRQ] = 1; + +// Log_Warning("GIC", "TODO: Implement IRQ_AddHandler"); + + if( gaIRQ_Handlers[IRQ] ) + return 2; + + gaIRQ_Handlers[IRQ] = Handler; + gaIRQ_HandlerData[IRQ] = Ptr; + + return 0; +} + diff --git a/KernelLand/Modules/armv7/GIC/gic.h b/KernelLand/Modules/armv7/GIC/gic.h new file mode 100644 index 00000000..d7a5a8dc --- /dev/null +++ b/KernelLand/Modules/armv7/GIC/gic.h @@ -0,0 +1,57 @@ +/* + * ARMv7 GIC Support + * - By John Hodge (thePowersGang) + * + * gic.h + * - GIC Core Definitions + */ +#ifndef _ARM7_GIC_H_ +#define _ARM7_GIC_H_ + +enum eGICD_Registers +{ + GICD_CTLR = 0x000/4, // Distributor Control Register + GICD_TYPER = 0x004/4, // Interrupt Controller Type + GICD_IIDR = 0x008/4, // Distributor Implementer Identifcation + + GICD_IGROUPR0 = 0x080/4, // Interrupt Group Register (#0) + GICD_ISENABLER0 = 0x100/4, // Interrupt Set-Enable Register #0 (128*8=1024) + GICD_ICENABLER0 = 0x180/4, // Interrupt Clear-Enable Register #0 + GICD_ISPENDR0 = 0x200/4, // Interrupt Set-Pending Register #0 + GICD_ICPENDR0 = 0x280/4, // Interrupt Clear-Pending Register #0 + GICD_ISACTIVER0 = 0x300/4, // Interrupt Set-Active Register (GICv2) + GICD_ICACTIVER0 = 0x380/4, // Interrupt Clear-Active Register (GICv2) + + GICD_IPRIORITYR0 = 0x400/4, // Interrupt priority registers (254*4 = ) + + GICD_ITARGETSR0 = 0x800/4, // Interrupt Processor Targets Register (8*4) + + GICD_ICFGR0 = 0xC00/4, // Interrupt Configuration Register (64*4) + GICD_NSACR0 = 0xE00/4, // Non-secure Access Control Register (64*4) + GICD_SIGR = 0xF00/4, // Software Generated Interrupt Register (Write Only) + GICD_CPENDSGIR0 = 0xF10/4, // SGI Clear-Pending Registers (4*4) + GICD_SPENDSGIR0 = 0xF20/4, // SGI Set-Pending Registers (4*4) +}; + +enum eGICC_Registers +{ + GICC_CTLR = 0x000/4, // CPU Interface Control Register + GICC_PMR = 0x004/4, // Interrupt Priority Mask Register + GICC_BPR = 0x008/4, // Binary Point Register + GICC_IAR = 0x00C/4, // Interrupt Acknowledge Register + GICC_EOIR = 0x010/4, // End of Interrupt Register + GICC_RPR = 0x014/4, // Running Priority Register + GICC_HPPIR = 0x018/4, // Highest Priority Pending Interrupt Register + GICC_ABPR = 0x01C/4, // Aliased Binary Point Register + GICC_AIAR = 0x020/4, // Aliased Interrupt Acknowledge Register, + GICC_AEOIR = 0x024/4, // Aliased End of Interrupt Register + GICC_AHPPIR = 0x028/4, // Aliased Highest Priority Pending Interrupt Register + + GICC_APR0 = 0x0D0/4, // Active Priorities Registers (4*4) + GICC_NSAPR0 = 0x0E0/4, // Non-secure Active Priorities Registers (4*4) + + GICC_IIDR = 0x0FC/4, // CPU Interface Identifcation Register + GICC_DIR = 0x0FC/4, // Deactivate Interrupt Register (Write Only) +}; + +#endif diff --git a/KernelLand/Modules/armv7/Makefile.tpl b/KernelLand/Modules/armv7/Makefile.tpl new file mode 100644 index 00000000..58eb3c99 --- /dev/null +++ b/KernelLand/Modules/armv7/Makefile.tpl @@ -0,0 +1,3 @@ +CATEGORY = armv7 + +-include ../../Makefile.tpl diff --git a/KernelLand/Modules/link.ld b/KernelLand/Modules/link.ld new file mode 100644 index 00000000..503f63d6 --- /dev/null +++ b/KernelLand/Modules/link.ld @@ -0,0 +1,35 @@ +/* + * Acess2 Kernel Modules + * Linker Script + */ + +ENTRY(ModuleEntry) +OUTPUT_FORMAT(elf32-i386) + +SECTIONS +{ + . = 0 + SIZEOF_HEADERS; + + .text : AT(ADDR(.text)) { + textzero = .; + *(.text) + } + + .rodata ALIGN(0x1000): AT(ADDR(.rodata)) { + *(.rodata) + *(.rdata) + DriverInfo = .; + *(KMODULES) + } + + .data ALIGN (0x1000) : AT(ADDR(.data)) { + *(.data) + } + + .bss : AT(ADDR(.bss)) { + _sbss = .; + *(COMMON) + *(.bss) + _ebss = .; + } +} diff --git a/KernelLand/Modules/x86/ISADMA/Makefile b/KernelLand/Modules/x86/ISADMA/Makefile new file mode 100644 index 00000000..b74c63fc --- /dev/null +++ b/KernelLand/Modules/x86/ISADMA/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ := dma.o +NAME := ISADMA + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/x86/ISADMA/dma.c b/KernelLand/Modules/x86/ISADMA/dma.c new file mode 100644 index 00000000..732a93c3 --- /dev/null +++ b/KernelLand/Modules/x86/ISADMA/dma.c @@ -0,0 +1,122 @@ +/* + * AcessOS 1.0 + * DMA Driver + */ +#include +#include + +#define DMA_SIZE (0x2400) +#define DMA_ADDRESS(c) ((c)*DMA_SIZE+0x500) //Save Space for IDT and BDA + +#define LOWB(x) ((x)&0xFF) +#define HIB(x) (((x)>>8)&0xFF) +#define HIW(x) (((x)>>16)&0xFFFF) + +// === TYPES === +typedef struct +{ + int mode; + char *address; +} t_dmaChannel; + +// === PROTOTYPES === + int DMA_Install(char **Arguments); +void DMA_SetChannel(int Channel, int length, int read); + int DMA_ReadData(int channel, int count, void *buffer); + int DMA_WriteData(int channel, int count, const void *buffer); + +// === CONSTANTS === +const Uint8 cMASKPORT [8] = { 0x0A, 0x0A, 0x0A, 0x0A, 0xD4, 0xD4, 0xD4, 0xD4 }; +const Uint8 cMODEPORT [8] = { 0x0B, 0x0B, 0x0B, 0x0B, 0xD6, 0xD6, 0xD6, 0xD6 }; +const Uint8 cCLEARPORT[8] = { 0x0C, 0x0C, 0x0C, 0x0C, 0xD8, 0xD8, 0xD8, 0xD8 }; +const Uint8 cPAGEPORT [8] = { 0x87, 0x83, 0x81, 0x82, 0x8F, 0x8B, 0x89, 0x8A }; +const Uint8 cADDRPORT [8] = { 0x00, 0x02, 0x04, 0x06, 0xC0, 0xC4, 0xC8, 0xCC }; +const Uint8 cCOUNTPORT[8] = { 0x01, 0x03, 0x05, 0x07, 0xC2, 0xC6, 0xCA, 0xCE }; + +// === GLOBALS === +MODULE_DEFINE(0, 0x0100, x86_ISADMA, DMA_Install, NULL, NULL); +char *dma_addresses[8]; +t_dmaChannel dma_channels[8]; + +// === CODE === +/** + * \brief Initialise DMA channels + * \param Arguments Arguments passed at boot time + */ +int DMA_Install(char **Arguments) +{ + Uint i; + for(i=8;i--;) + { + outb( cMASKPORT[i], 0x04 | (i & 0x3) ); // mask channel + outb( cCLEARPORT[i], 0x00 ); + outb( cMODEPORT[i], 0x48 | (i & 0x3) ); //Read Flag + outb( 0xd8, 0xff); //Reset Flip-Flop + outb( cADDRPORT[i], LOWB(DMA_ADDRESS(i)) ); // send address + outb( cADDRPORT[i], HIB(DMA_ADDRESS(i)) ); // send address + outb( 0xd8, 0xff); //Reset Flip-Flop + outb( cCOUNTPORT[i], LOWB(DMA_SIZE) ); // send size + outb( cCOUNTPORT[i], HIB(DMA_SIZE) ); // send size + outb( cPAGEPORT[i], LOWB(HIW(DMA_ADDRESS(i))) ); // send page + outb( cMASKPORT[i], i & 0x3 ); // unmask channel + + dma_channels[i].mode = 0; + dma_addresses[i] = (char*)DMA_ADDRESS(i); + dma_addresses[i] += KERNEL_BASE; + } + return MODULE_ERR_OK; +} + +/** + * \fn void DMA_SetChannel(int Channel, int length, int read) + * \brief Set DMA Channel Length and RW + */ +void DMA_SetChannel(int Channel, int length, int read) +{ + Uint chan = Channel & 7; + read = !!read; + if(length > DMA_SIZE) length = DMA_SIZE; + length --; //Adjust for DMA + outb( cMASKPORT[chan], 0x04 | (chan & 0x3) ); // mask channel + outb( cCLEARPORT[chan], 0x00 ); + outb( cMODEPORT[chan], (0x44 + (!read)*4) | (chan & 0x3) ); + outb( cADDRPORT[chan], LOWB(DMA_ADDRESS(chan)) ); // send address + outb( cADDRPORT[chan], HIB(DMA_ADDRESS(chan)) ); // send address + outb( cPAGEPORT[chan], HIW(DMA_ADDRESS(chan)) ); // send page + outb( cCOUNTPORT[chan], LOWB(length) ); // send size + outb( cCOUNTPORT[chan], HIB(length) ); // send size + outb( cMASKPORT[chan], chan & 0x3 ); // unmask channel + dma_addresses[chan] = (char*)DMA_ADDRESS(chan); + dma_addresses[chan] += KERNEL_BASE; +} + +/** + * \fn void DMA_ReadData(int channel, int count, void *buffer) + * \brief Read data from a DMA buffer + */ +int DMA_ReadData(int channel, int count, void *buffer) +{ + if(channel < 0 || channel > 7) + return -1; + if(count < 0 || count > DMA_SIZE) + return -2; + //LogF("memcpy(*0x%x, dma_channels[channel].address, count)\n", buffer + memcpy(buffer, dma_addresses[channel], count); + return 0; +} + +/** + * \fn void DMA_WriteData(int channel, int count, void *buffer) + * \brief Write data to a DMA buffer + */ +int DMA_WriteData(int channel, int count, const void *buffer) +{ + if(channel < 0 || channel > 7) + return -1; + if(count < 0 || count > DMA_SIZE) + return -2; + + memcpy(dma_addresses[channel], buffer, count); + + return 0; +} diff --git a/KernelLand/Modules/x86/ISADMA/include/dma.h b/KernelLand/Modules/x86/ISADMA/include/dma.h new file mode 100644 index 00000000..b1a6d1a6 --- /dev/null +++ b/KernelLand/Modules/x86/ISADMA/include/dma.h @@ -0,0 +1,11 @@ +/* + * Acess2 DMA Driver + */ +#ifndef _DMA_H_ +#define _DMA_H_ + +extern void DMA_SetChannel(int channel, int length, int read); +extern int DMA_ReadData(int channel, int count, void *buffer); +extern int DMA_WriteData(int channel, int count, void *buffer); + +#endif diff --git a/KernelLand/Modules/x86/Makefile.tpl b/KernelLand/Modules/x86/Makefile.tpl new file mode 100644 index 00000000..61b234f3 --- /dev/null +++ b/KernelLand/Modules/x86/Makefile.tpl @@ -0,0 +1,3 @@ +CATEGORY = x86 + +-include ../../Makefile.tpl diff --git a/KernelLand/Modules/x86/VGAText/Makefile b/KernelLand/Modules/x86/VGAText/Makefile new file mode 100644 index 00000000..8f1aee6d --- /dev/null +++ b/KernelLand/Modules/x86/VGAText/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ := vga.o +NAME := VGAText + +-include ../Makefile.tpl diff --git a/KernelLand/Modules/x86/VGAText/vga.c b/KernelLand/Modules/x86/VGAText/vga.c new file mode 100644 index 00000000..41f0bd01 --- /dev/null +++ b/KernelLand/Modules/x86/VGAText/vga.c @@ -0,0 +1,319 @@ +/* + * Acess2 VGA Controller Driver + */ +#define DEBUG 0 +#include +#include +#include +#include + +// === CONSTANTS === +#define VGA_WIDTH 80 +#define VGA_HEIGHT 25 + +// === PROTOTYPES === + int VGA_Install(char **Arguments); +Uint64 VGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); + int VGA_IOCtl(tVFS_Node *Node, int Id, void *Data); +Uint8 VGA_int_GetColourNibble(Uint16 col); +Uint16 VGA_int_GetWord(const tVT_Char *Char); +void VGA_int_SetCursor(Sint16 x, Sint16 y); +// --- 2D Acceleration Functions -- +void VGA_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour); +void VGA_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H); + +// === GLOBALS === +MODULE_DEFINE(0, 0x000A, x86_VGAText, VGA_Install, NULL, NULL); +tVFS_NodeType gVGA_NodeType = { + //.Read = VGA_Read, + .Write = VGA_Write, + .IOCtl = VGA_IOCtl + }; +tDevFS_Driver gVGA_DevInfo = { + NULL, "x86_VGAText", + { + .NumACLs = 0, + .Size = VGA_WIDTH*VGA_HEIGHT*sizeof(tVT_Char), + .Type = &gVGA_NodeType + } +}; +Uint16 *gVGA_Framebuffer = (void*)( KERNEL_BASE|0xB8000 ); + int giVGA_BufferFormat = VIDEO_BUFFMT_TEXT; +tDrvUtil_Video_2DHandlers gVGA_2DFunctions = { + NULL, + VGA_2D_Fill, + VGA_2D_Blit +}; + +// === CODE === +/** + * \fn int VGA_Install(char **Arguments) + */ +int VGA_Install(char **Arguments) +{ + Uint8 byte; + + // Enable Bright Backgrounds + inb(0x3DA); // Reset flipflop + outb(0x3C0, 0x30); // Index 0x10, PAS + byte = inb(0x3C1); + byte &= ~8; // Disable Blink + outb(0x3C0, byte); // Write value + + + // Install DevFS + DevFS_AddDevice( &gVGA_DevInfo ); + + return MODULE_ERR_OK; +} + +/** + * \brief Writes a string of bytes to the VGA controller + */ +Uint64 VGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) +{ + if( giVGA_BufferFormat == VIDEO_BUFFMT_TEXT ) + { + int num = Length / sizeof(tVT_Char); + int ofs = Offset / sizeof(tVT_Char); + int i = 0; + const tVT_Char *chars = Buffer; + Uint16 word; + + //ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + + for( ; num--; i ++, ofs ++) + { + word = VGA_int_GetWord( &chars[i] ); + gVGA_Framebuffer[ ofs ] = word; + } + + //LEAVE('X', Length); + return Length; + } + else if( giVGA_BufferFormat == VIDEO_BUFFMT_2DSTREAM ) + { + return DrvUtil_Video_2DStream(NULL, Buffer, Length, &gVGA_2DFunctions, sizeof(gVGA_2DFunctions)); + } + else + { + return 0; + } +} + +/** + * \fn int VGA_IOCtl(tVFS_Node *Node, int Id, void *Data) + * \brief IO Control Call + */ +int VGA_IOCtl(tVFS_Node *Node, int ID, void *Data) +{ + int rv; + switch(ID) + { + case DRV_IOCTL_TYPE: return DRV_TYPE_VIDEO; + case DRV_IOCTL_IDENT: memcpy(Data, "VGA\0", 4); return 1; + case DRV_IOCTL_VERSION: *(int*)Data = 50; return 1; + case DRV_IOCTL_LOOKUP: return 0; + + case VIDEO_IOCTL_GETSETMODE: return 0; // Mode 0 only + case VIDEO_IOCTL_FINDMODE: + if( !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)) ) return -1; + ((tVideo_IOCtl_Mode*)Data)->id = 0; // Text Only! + case VIDEO_IOCTL_MODEINFO: + if( !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)) ) return -1; + if( ((tVideo_IOCtl_Mode*)Data)->id != 0) return 0; + ((tVideo_IOCtl_Mode*)Data)->width = VGA_WIDTH*giVT_CharWidth; + ((tVideo_IOCtl_Mode*)Data)->height = VGA_HEIGHT*giVT_CharHeight; + ((tVideo_IOCtl_Mode*)Data)->bpp = 4; + return 1; + + case VIDEO_IOCTL_SETBUFFORMAT: + if( !CheckMem(Data, sizeof(int)) ) return -1; + switch( *(int*)Data ) + { + case VIDEO_BUFFMT_TEXT: + case VIDEO_BUFFMT_2DSTREAM: + rv = giVGA_BufferFormat; + giVGA_BufferFormat = *(int*)Data; +// Log_Debug("VGA", "Buffer format set to %i", giVGA_BufferFormat); + return rv; + default: + break; + } + return -1; + + case VIDEO_IOCTL_SETCURSOR: + VGA_int_SetCursor( ((tVideo_IOCtl_Pos*)Data)->x, ((tVideo_IOCtl_Pos*)Data)->y ); + return 1; + } + return 0; +} + +/** + * \fn Uint8 VGA_int_GetColourNibble(Uint16 col) + * \brief Converts a 12-bit colour into a VGA 4-bit colour + */ +Uint8 VGA_int_GetColourNibble(Uint16 col) +{ + Uint8 ret = 0; + int bright = 0; + + col = col & 0xCCC; + col = ((col>>2)&3) | ((col>>4)&0xC) | ((col>>6)&0x30); + bright = ( (col & 2 ? 1 : 0) + (col & 8 ? 1 : 0) + (col & 32 ? 1 : 0) ) / 2; + + switch(col) + { + // Black + case 0x00: ret = 0x0; break; + // Dark Grey + case 0x15: ret = 0x8; break; + // Blues + case 0x01: + case 0x02: ret = 0x1; break; + case 0x03: ret = 0x9; break; + // Green + case 0x04: + case 0x08: ret = 0x2; break; + case 0x0C: ret = 0xA; break; + // Reds + case 0x10: + case 0x20: ret = 0x4; break; + case 0x30: ret = 0xC; break; + // Light Grey + case 0x2A: ret = 0x7; break; + // White + case 0x3F: ret = 0xF; break; + + default: + ret |= (col & 0x03 ? 1 : 0); + ret |= (col & 0x0C ? 2 : 0); + ret |= (col & 0x30 ? 4 : 0); + ret |= (bright ? 8 : 0); + break; + } + return ret; +} + +/** + * \fn Uint16 VGA_int_GetWord(tVT_Char *Char) + * \brief Convers a character structure to a VGA character word + */ +Uint16 VGA_int_GetWord(const tVT_Char *Char) +{ + Uint16 ret; + Uint16 col; + + // Get Character + if(Char->Ch < 128) + ret = Char->Ch; + else { + switch(Char->Ch) + { + default: ret = 0; break; + } + } + + col = VGA_int_GetColourNibble(Char->BGCol); + ret |= col << 12; + + col = VGA_int_GetColourNibble(Char->FGCol); + ret |= col << 8; + + return ret; +} + +/** + * \fn void VGA_int_SetCursor(Sint16 x, Sint16 y) + * \brief Updates the cursor position + */ +void VGA_int_SetCursor(Sint16 x, Sint16 y) +{ + int pos = x+y*VGA_WIDTH; + if(x == -1 || y == -1) + pos = -1; + outb(0x3D4, 14); + outb(0x3D5, pos >> 8); + outb(0x3D4, 15); + outb(0x3D5, pos); +} + +void VGA_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour) +{ + tVT_Char ch; + Uint16 word, *buf; + + X /= giVT_CharWidth; + W /= giVT_CharWidth; + Y /= giVT_CharHeight; + H /= giVT_CharHeight; + + ch.Ch = 0x20; + ch.BGCol = (Colour & 0x0F0000) >> (16-8); + ch.BGCol |= (Colour & 0x000F00) >> (8-4); + ch.BGCol |= (Colour & 0x00000F); + word = VGA_int_GetWord(&ch); + + Log("Fill (%i,%i) %ix%i with 0x%x", X, Y, W, H, word); + + if( X > VGA_WIDTH || Y > VGA_HEIGHT ) return ; + if( X + W > VGA_WIDTH ) W = VGA_WIDTH - X; + if( Y + H > VGA_HEIGHT ) H = VGA_HEIGHT - Y; + + buf = gVGA_Framebuffer + Y*VGA_WIDTH + X; + + + while( H -- ) { + int i; + for( i = 0; i < W; i ++ ) + *buf++ = word; + buf += VGA_WIDTH - W; + } +} + +void VGA_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H) +{ + Uint16 *src, *dst; + + DstX /= giVT_CharWidth; + SrcX /= giVT_CharWidth; + W /= giVT_CharWidth; + + DstY /= giVT_CharHeight; + SrcY /= giVT_CharHeight; + H /= giVT_CharHeight; + +// Log("(%i,%i) from (%i,%i) %ix%i", DstX, DstY, SrcX, SrcY, W, H); + + if( SrcX > VGA_WIDTH || SrcY > VGA_HEIGHT ) return ; + if( SrcX + W > VGA_WIDTH ) W = VGA_WIDTH - SrcX; + if( SrcY + H > VGA_HEIGHT ) H = VGA_HEIGHT - SrcY; + if( DstX > VGA_WIDTH || DstY > VGA_HEIGHT ) return ; + if( DstX + W > VGA_WIDTH ) W = VGA_WIDTH - DstX; + if( DstY + H > VGA_HEIGHT ) H = VGA_HEIGHT - DstY; + + + src = gVGA_Framebuffer + SrcY*VGA_WIDTH + SrcX; + dst = gVGA_Framebuffer + DstY*VGA_WIDTH + DstX; + + if( src > dst ) + { + // Simple copy + while( H-- ) { + memcpy(dst, src, W*2); + dst += VGA_WIDTH; + src += VGA_WIDTH; + } + } + else + { + dst += H*VGA_WIDTH; + src += H*VGA_WIDTH; + while( H -- ) { + int i; + dst -= VGA_WIDTH-W; + src -= VGA_WIDTH-W; + for( i = W; i --; ) *--dst = *--src; + } + } +} diff --git a/Modules/Display/BochsGA/Makefile b/Modules/Display/BochsGA/Makefile deleted file mode 100644 index 2df219af..00000000 --- a/Modules/Display/BochsGA/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = bochsvbe.o -NAME = BochsGA - --include ../Makefile.tpl diff --git a/Modules/Display/BochsGA/bochsvbe.c b/Modules/Display/BochsGA/bochsvbe.c deleted file mode 100644 index 806c1c13..00000000 --- a/Modules/Display/BochsGA/bochsvbe.c +++ /dev/null @@ -1,362 +0,0 @@ -/** - * Acess2 Bochs graphics adapter Driver - * - By John Hodge (thePowersGang) - * - * bochsvbe.c - * - Driver core - */ -#define DEBUG 0 -#define VERSION VER2(0,10) - -#include -#include -#include -#include -#include -#include -#include - -// === TYPES === -typedef struct sBGA_Mode { - Uint16 width; - Uint16 height; - Uint16 bpp; - Uint32 fbSize; -} tBGA_Mode; - -// === CONSTANTS === -#define BGA_LFB_MAXSIZE (1024*768*4) -#define VBE_DISPI_BANK_ADDRESS 0xA0000 -#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 -#define VBE_DISPI_IOPORT_INDEX 0x01CE -#define VBE_DISPI_IOPORT_DATA 0x01CF -#define VBE_DISPI_DISABLED 0x00 -#define VBE_DISPI_ENABLED 0x01 -#define VBE_DISPI_LFB_ENABLED 0x40 -#define VBE_DISPI_NOCLEARMEM 0x80 -enum { - VBE_DISPI_INDEX_ID, - VBE_DISPI_INDEX_XRES, - VBE_DISPI_INDEX_YRES, - VBE_DISPI_INDEX_BPP, - VBE_DISPI_INDEX_ENABLE, - VBE_DISPI_INDEX_BANK, - VBE_DISPI_INDEX_VIRT_WIDTH, - VBE_DISPI_INDEX_VIRT_HEIGHT, - VBE_DISPI_INDEX_X_OFFSET, - VBE_DISPI_INDEX_Y_OFFSET -}; - -extern void MM_DumpTables(tVAddr Start, tVAddr End); - -// === PROTOTYPES === -// Driver - int BGA_Install(char **Arguments); -void BGA_Uninstall(); -// Internal -void BGA_int_WriteRegister(Uint16 reg, Uint16 value); -Uint16 BGA_int_ReadRegister(Uint16 reg); -void BGA_int_SetBank(Uint16 bank); -void BGA_int_SetMode(Uint16 width, Uint16 height); - int BGA_int_UpdateMode(int id); - int BGA_int_FindMode(tVideo_IOCtl_Mode *info); - int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info); - int BGA_int_MapFB(void *Dest); -// Filesystem -Uint64 BGA_Read(tVFS_Node *Node, Uint64 off, Uint64 len, void *buffer); -Uint64 BGA_Write(tVFS_Node *Node, Uint64 off, Uint64 len, const void *buffer); - int BGA_IOCtl(tVFS_Node *Node, int ID, void *Data); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, BochsGA, BGA_Install, NULL, "PCI", NULL); -tVFS_NodeType gBGA_NodeType = { - .Read = BGA_Read, - .Write = BGA_Write, - .IOCtl = BGA_IOCtl - }; -tDevFS_Driver gBGA_DriverStruct = { - NULL, "BochsGA", - {.Type = &gBGA_NodeType} - }; - int giBGA_CurrentMode = -1; -tVideo_IOCtl_Pos gBGA_CursorPos = {-1,-1}; -Uint *gBGA_Framebuffer; -const tBGA_Mode *gpBGA_CurrentMode; -const tBGA_Mode gBGA_Modes[] = { - {640,480,32, 640*480*4}, - {800,600,32, 800*600*4}, - {1024,768,32, 1024*768*4} -}; -#define BGA_MODE_COUNT (sizeof(gBGA_Modes)/sizeof(gBGA_Modes[0])) -tDrvUtil_Video_BufInfo gBGA_DrvUtil_BufInfo; - -// === CODE === -/** - * \fn int BGA_Install(char **Arguments) - */ -int BGA_Install(char **Arguments) -{ - int version = 0; - tPAddr base; - tPCIDev dev; - - // Check BGA Version - version = BGA_int_ReadRegister(VBE_DISPI_INDEX_ID); - LOG("version = 0x%x", version); - - // NOTE: This driver was written for BGA versions >= 0xBOC2 - // NOTE: However, Qemu is braindead and doesn't return the actual version - if( version != 0xB0C0 && ((version & 0xFFF0) != 0xB0C0 || version < 0xB0C2) ) { - Log_Warning("BGA", "Bochs Adapter Version is not compatible (need >= 0xB0C2), instead 0x%x", version); - return MODULE_ERR_NOTNEEDED; - } - - // Get framebuffer base - dev = PCI_GetDevice(0x1234, 0x1111, 0); - if(dev == -1) - base = VBE_DISPI_LFB_PHYSICAL_ADDRESS; - else - base = PCI_GetBAR(dev, 0); - - // Map Framebuffer to hardware address - gBGA_Framebuffer = (void *) MM_MapHWPages(base, 768); // 768 pages (3Mb) - - // Install Device - if( DevFS_AddDevice( &gBGA_DriverStruct ) == -1 ) - { - Log_Warning("BGA", "Unable to register with DevFS, maybe already loaded?"); - return MODULE_ERR_MISC; - } - - return MODULE_ERR_OK; -} - -/** - * \brief Clean up driver resources before destruction - */ -void BGA_Uninstall(void) -{ - DevFS_DelDevice( &gBGA_DriverStruct ); - MM_UnmapHWPages( (tVAddr)gBGA_Framebuffer, 768 ); -} - -/** - * \fn Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) - * \brief Read from the framebuffer - */ -Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) -{ - // Check Mode - if(giBGA_CurrentMode == -1) return -1; - - // Check Offset and Length against Framebuffer Size - if(off+len > gpBGA_CurrentMode->fbSize) - return -1; - - // Copy from Framebuffer - memcpy(buffer, (void*)((Uint)gBGA_Framebuffer + (Uint)off), len); - return len; -} - -/** - * \brief Write to the framebuffer - */ -Uint64 BGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - if( giBGA_CurrentMode == -1 ) BGA_int_UpdateMode(0); - return DrvUtil_Video_WriteLFB(&gBGA_DrvUtil_BufInfo, Offset, Length, Buffer); -} - -const char *csaBGA_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL}; -/** - * \brief Handle messages to the device - */ -int BGA_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - int ret = -2; - ENTER("pNode iId pData", Node, ID, Data); - - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_VIDEO, "BochsGA", VERSION, csaBGA_IOCtls); - - case VIDEO_IOCTL_GETSETMODE: - if( Data ) BGA_int_UpdateMode(*(int*)(Data)); - ret = giBGA_CurrentMode; - break; - - case VIDEO_IOCTL_FINDMODE: - ret = BGA_int_FindMode((tVideo_IOCtl_Mode*)Data); - break; - - case VIDEO_IOCTL_MODEINFO: - ret = BGA_int_ModeInfo((tVideo_IOCtl_Mode*)Data); - break; - - case VIDEO_IOCTL_SETBUFFORMAT: - DrvUtil_Video_RemoveCursor( &gBGA_DrvUtil_BufInfo ); - ret = gBGA_DrvUtil_BufInfo.BufferFormat; - if(Data) - gBGA_DrvUtil_BufInfo.BufferFormat = *(int*)Data; - if(gBGA_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) - DrvUtil_Video_SetCursor( &gBGA_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor ); - break; - - case VIDEO_IOCTL_SETCURSOR: - DrvUtil_Video_RemoveCursor( &gBGA_DrvUtil_BufInfo ); - gBGA_CursorPos.x = ((tVideo_IOCtl_Pos*)Data)->x; - gBGA_CursorPos.y = ((tVideo_IOCtl_Pos*)Data)->y; - if(gBGA_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) - DrvUtil_Video_DrawCursor( - &gBGA_DrvUtil_BufInfo, - gBGA_CursorPos.x*giVT_CharWidth, - gBGA_CursorPos.y*giVT_CharHeight - ); - else - DrvUtil_Video_DrawCursor( - &gBGA_DrvUtil_BufInfo, - gBGA_CursorPos.x, gBGA_CursorPos.y - ); - break; - - default: - LEAVE('i', -2); - return -2; - } - - LEAVE('i', ret); - return ret; -} - -//== Internal Functions == -/** - * \brief Writes to a BGA register - */ -void BGA_int_WriteRegister(Uint16 reg, Uint16 value) -{ - outw(VBE_DISPI_IOPORT_INDEX, reg); - outw(VBE_DISPI_IOPORT_DATA, value); -} - -Uint16 BGA_int_ReadRegister(Uint16 reg) -{ - outw(VBE_DISPI_IOPORT_INDEX, reg); - return inw(VBE_DISPI_IOPORT_DATA); -} - -/** - * \brief Sets the video mode (32bpp only) - */ -void BGA_int_SetMode(Uint16 Width, Uint16 Height) -{ - ENTER("iWidth iHeight", Width, Height); - BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED); - BGA_int_WriteRegister(VBE_DISPI_INDEX_XRES, Width); - BGA_int_WriteRegister(VBE_DISPI_INDEX_YRES, Height); - BGA_int_WriteRegister(VBE_DISPI_INDEX_BPP, 32); - BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM | VBE_DISPI_LFB_ENABLED); - LEAVE('-'); -} - -/** - * \fn int BGA_int_UpdateMode(int id) - * \brief Set current vide mode given a mode id - */ -int BGA_int_UpdateMode(int id) -{ - // Sanity Check - if(id < 0 || id >= BGA_MODE_COUNT) return -1; - - BGA_int_SetMode( - gBGA_Modes[id].width, - gBGA_Modes[id].height); - - gBGA_DrvUtil_BufInfo.Framebuffer = gBGA_Framebuffer; - gBGA_DrvUtil_BufInfo.Pitch = gBGA_Modes[id].width * 4; - gBGA_DrvUtil_BufInfo.Width = gBGA_Modes[id].width; - gBGA_DrvUtil_BufInfo.Height = gBGA_Modes[id].height; - gBGA_DrvUtil_BufInfo.Depth = gBGA_Modes[id].bpp; - - giBGA_CurrentMode = id; - gpBGA_CurrentMode = &gBGA_Modes[id]; - return id; -} - -/** - * \fn int BGA_int_FindMode(tVideo_IOCtl_Mode *info) - * \brief Find a mode matching the given options - */ -int BGA_int_FindMode(tVideo_IOCtl_Mode *info) -{ - int i; - int best = 0, bestFactor = 1000; - int tmp; - int rqdProduct = info->width * info->height; - - ENTER("pinfo", info); - LOG("info = {width:%i,height:%i,bpp:%i})\n", info->width, info->height, info->bpp); - - for(i = 0; i < BGA_MODE_COUNT; i++) - { - #if DEBUG >= 2 - LOG("Mode %i (%ix%i,%ibpp), ", i, gBGA_Modes[i].width, gBGA_Modes[i].height, gBGA_Modes[i].bpp); - #endif - - if( gBGA_Modes[i].bpp != info->bpp ) - continue ; - - // Ooh! A perfect match - if(gBGA_Modes[i].width == info->width && gBGA_Modes[i].height == info->height) - { - #if DEBUG >= 2 - LOG("Perfect"); - #endif - best = i; - break; - } - - - // If not, how close are we? - tmp = gBGA_Modes[i].width * gBGA_Modes[i].height - rqdProduct; - tmp = tmp < 0 ? -tmp : tmp; // tmp = ABS(tmp) - - #if DEBUG >= 2 - LOG("tmp = %i", tmp); - #endif - - if(tmp < bestFactor) - { - bestFactor = tmp; - best = i; - } - } - - info->id = best; - info->width = gBGA_Modes[best].width; - info->height = gBGA_Modes[best].height; - info->bpp = gBGA_Modes[best].bpp; - - LEAVE('i', best); - return best; -} - -/** - * \fn int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info) - * \brief Get mode information - */ -int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info) -{ - // Sanity Check - //if( !MM_IsUser( (Uint)info, sizeof(tVideo_IOCtl_Mode) ) ) { - // return -EINVAL; - //} - - if(info->id < 0 || info->id >= BGA_MODE_COUNT) return -1; - - info->width = gBGA_Modes[info->id].width; - info->height = gBGA_Modes[info->id].height; - info->bpp = gBGA_Modes[info->id].bpp; - - return 1; -} - diff --git a/Modules/Display/Makefile.tpl b/Modules/Display/Makefile.tpl deleted file mode 100644 index b5431609..00000000 --- a/Modules/Display/Makefile.tpl +++ /dev/null @@ -1,3 +0,0 @@ -CATEGORY = Video - --include ../../Makefile.tpl diff --git a/Modules/Display/PL110/Makefile b/Modules/Display/PL110/Makefile deleted file mode 100644 index 0f90347b..00000000 --- a/Modules/Display/PL110/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = main.o -NAME = PL110 - --include ../Makefile.tpl diff --git a/Modules/Display/PL110/main.c b/Modules/Display/PL110/main.c deleted file mode 100644 index c3b6e526..00000000 --- a/Modules/Display/PL110/main.c +++ /dev/null @@ -1,345 +0,0 @@ -/** - * Acess2 ARM PrimeCell Colour LCD Controller (PL110) Driver - * - By John Hodge (thePowersGang) - * - * main.c - * - Driver core - * - * - * NOTE: The PL110 is set to 24bpp, but these are stored as 32-bit words. - * This corresponds to the Acess 32bpp mode, as the Acess 24bpp is packed - */ -#define DEBUG 0 -#define VERSION ((0<<8)|10) -#include -#include -#include -#include -#include -#include -#include -#include -#include // ARM Arch - -#define ABS(a) ((a)>0?(a):-(a)) - -// === TYPEDEFS === -typedef struct sPL110 tPL110; - -struct sPL110 -{ - Uint32 LCDTiming0; - Uint32 LCDTiming1; - Uint32 LCDTiming2; - Uint32 LCDTiming3; - - Uint32 LCDUPBase; - Uint32 LCDLPBase; - Uint32 LCDIMSC; - Uint32 LCDControl; - Uint32 LCDRIS; - Uint32 LCDMIS; - Uint32 LCDICR; - Uint32 LCDUPCurr; - Uint32 LCDLPCurr; -}; - -#ifndef PL110_BASE -#define PL110_BASE 0x10020000 // Integrator -#endif - -// === CONSTANTS === -const struct { - short W, H; -} caPL110_Modes[] = { - {640,480}, - {800,600}, - {1024,768} // MAX -}; -const int ciPL110_ModeCount = sizeof(caPL110_Modes)/sizeof(caPL110_Modes[0]); - -// === PROTOTYPES === -// Driver - int PL110_Install(char **Arguments); -void PL110_Uninstall(); -// Internal -// Filesystem -Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); -Uint64 PL110_Write(tVFS_Node *node, Uint64 off, Uint64 len, const void *buffer); - int PL110_IOCtl(tVFS_Node *node, int id, void *data); -// -- Internals - int PL110_int_SetResolution(int W, int H); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, PL110, PL110_Install, NULL, NULL); -tVFS_NodeType gPL110_DevNodeType = { - .Read = PL110_Read, - .Write = PL110_Write, - .IOCtl = PL110_IOCtl - }; -tDevFS_Driver gPL110_DriverStruct = { - NULL, "PL110", - {.Type = &gPL110_DevNodeType} -}; -// -- Options -tPAddr gPL110_PhysBase = PL110_BASE; - int gbPL110_IsVersatile = 1; -// -- KeyVal parse rules -const tKeyVal_ParseRules gPL110_KeyValueParser = { - NULL, - { - {"Base", "P", &gPL110_PhysBase}, - {"IsVersatile", "i", &gbPL110_IsVersatile}, - {NULL, NULL, NULL} - } -}; -// -- Driver state - int giPL110_CurrentMode = 0; - int giPL110_BufferMode; - int giPL110_Width = 640; - int giPL110_Height = 480; -size_t giPL110_FramebufferSize; -tPL110 *gpPL110_IOMem; -tPAddr gPL110_FramebufferPhys; -void *gpPL110_Framebuffer; -// -- Misc -tDrvUtil_Video_BufInfo gPL110_DrvUtil_BufInfo; -tVideo_IOCtl_Pos gPL110_CursorPos; - -// === CODE === -/** - */ -int PL110_Install(char **Arguments) -{ -// KeyVal_Parse(&gPL110_KeyValueParser, Arguments); - - gpPL110_IOMem = (void*)MM_MapHWPages(gPL110_PhysBase, 1); - - PL110_int_SetResolution(caPL110_Modes[0].W, caPL110_Modes[0].H); - - DevFS_AddDevice( &gPL110_DriverStruct ); - - return 0; -} - -/** - * \brief Clean up resources for driver unloading - */ -void PL110_Uninstall() -{ -} - -/** - * \brief Read from the framebuffer - */ -Uint64 PL110_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) -{ - return 0; -} - -/** - * \brief Write to the framebuffer - */ -Uint64 PL110_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - gPL110_DrvUtil_BufInfo.BufferFormat = giPL110_BufferMode; - return DrvUtil_Video_WriteLFB(&gPL110_DrvUtil_BufInfo, Offset, Length, Buffer); -} - -const char *csaPL110_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL}; - -/** - * \brief Handle messages to the device - */ -int PL110_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - int ret = -2; - ENTER("pNode iID pData", Node, ID, Data); - - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaPL110_IOCtls); - - case VIDEO_IOCTL_SETBUFFORMAT: - DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo ); - ret = giPL110_BufferMode; - if(Data) giPL110_BufferMode = *(int*)Data; - if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) - DrvUtil_Video_SetCursor( &gPL110_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor ); - break; - - case VIDEO_IOCTL_GETSETMODE: - if(Data) - { - int newMode; - - if( !CheckMem(Data, sizeof(int)) ) - LEAVE_RET('i', -1); - - newMode = *(int*)Data; - - if(newMode < 0 || newMode >= ciPL110_ModeCount) - LEAVE_RET('i', -1); - - if(newMode != giPL110_CurrentMode) - { - giPL110_CurrentMode = newMode; - PL110_int_SetResolution( caPL110_Modes[newMode].W, caPL110_Modes[newMode].H ); - } - } - ret = giPL110_CurrentMode; - break; - - case VIDEO_IOCTL_FINDMODE: - { - tVideo_IOCtl_Mode *mode = Data; - int closest, closestArea, reqArea = 0; - if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode))) - LEAVE_RET('i', -1); - if( mode->bpp != 32 ) - LEAVE_RET('i', 0); - if( mode->flags != 0 ) - LEAVE_RET('i', 0); - - ret = 0; - - for( int i = 0; i < ciPL110_ModeCount; i ++ ) - { - int area; - if(mode->width == caPL110_Modes[i].W && mode->height == caPL110_Modes[i].H) { - mode->id = i; - ret = 1; - break; - } - - area = caPL110_Modes[i].W * caPL110_Modes[i].H; - if(!reqArea) { - reqArea = mode->width * mode->height; - closest = i; - closestArea = area; - } - else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) { - closest = i; - closestArea = area; - } - } - - if( ret == 0 ) - { - mode->id = closest; - ret = 1; - } - mode->width = caPL110_Modes[mode->id].W; - mode->height = caPL110_Modes[mode->id].H; - break; - } - - case VIDEO_IOCTL_MODEINFO: - { - tVideo_IOCtl_Mode *mode = Data; - if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode))) - LEAVE_RET('i', -1); - if(mode->id < 0 || mode->id >= ciPL110_ModeCount) - LEAVE_RET('i', 0); - - - mode->bpp = 32; - mode->flags = 0; - mode->width = caPL110_Modes[mode->id].W; - mode->height = caPL110_Modes[mode->id].H; - - ret = 1; - break; - } - - case VIDEO_IOCTL_SETCURSOR: - if( !Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) ) - LEAVE_RET('i', -1); - - DrvUtil_Video_RemoveCursor( &gPL110_DrvUtil_BufInfo ); - - gPL110_CursorPos = *(tVideo_IOCtl_Pos*)Data; - if(gPL110_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) - DrvUtil_Video_DrawCursor( - &gPL110_DrvUtil_BufInfo, - gPL110_CursorPos.x*giVT_CharWidth, - gPL110_CursorPos.y*giVT_CharHeight - ); - else - DrvUtil_Video_DrawCursor( - &gPL110_DrvUtil_BufInfo, - gPL110_CursorPos.x, - gPL110_CursorPos.y - ); - break; - - default: - LEAVE('i', -2); - return -2; - } - - LEAVE('i', ret); - return ret; -} - -// -// -// - -/** - * \brief Set the LCD controller resolution - * \param W Width (aligned to 16 pixels, cipped to 1024) - * \param H Height (clipped to 768) - * \return Boolean failure - */ -int PL110_int_SetResolution(int W, int H) -{ - W = (W + 15) & ~0xF; - if(W <= 0 || H <= 0) { - Log_Warning("PL110", "Attempted to set invalid resolution (%ix%i)", W, H); - return 1; - } - if(W > 1024) W = 1024; - if(H > 768) H = 768; - - gpPL110_IOMem->LCDTiming0 = ((W/16)-1) << 2; - gpPL110_IOMem->LCDTiming1 = H-1; - gpPL110_IOMem->LCDTiming2 = (14 << 27); - gpPL110_IOMem->LCDTiming3 = 0; - - if( gpPL110_Framebuffer ) { - MM_UnmapHWPages((tVAddr)gpPL110_Framebuffer, (giPL110_FramebufferSize+0xFFF)>>12); - } - giPL110_FramebufferSize = W*H*4; - - gpPL110_Framebuffer = (void*)MM_AllocDMA( (giPL110_FramebufferSize+0xFFF)>>12, 32, &gPL110_FramebufferPhys ); - gpPL110_IOMem->LCDUPBase = gPL110_FramebufferPhys; - gpPL110_IOMem->LCDLPBase = 0; - - // Power on, BGR mode, ???, ???, enabled - Uint32 controlWord = (1 << 11)|(1 << 8)|(1 << 5)|(5 << 1)|1; - // According to qemu, the Versatile version has these two the wrong - // way around - if( gbPL110_IsVersatile ) - { - gpPL110_IOMem->LCDIMSC = controlWord; // Actually LCDControl - gpPL110_IOMem->LCDControl = 0; // Actually LCDIMSC - } - else - { - gpPL110_IOMem->LCDIMSC = 0; - gpPL110_IOMem->LCDControl = controlWord; - } - - giPL110_Width = W; - giPL110_Height = H; - - // Update the DrvUtil buffer info - gPL110_DrvUtil_BufInfo.Framebuffer = gpPL110_Framebuffer; - gPL110_DrvUtil_BufInfo.Pitch = W * 4; - gPL110_DrvUtil_BufInfo.Width = W; - gPL110_DrvUtil_BufInfo.Height = H; - gPL110_DrvUtil_BufInfo.Depth = 32; - - return 0; -} diff --git a/Modules/Display/Tegra2Vid/Makefile b/Modules/Display/Tegra2Vid/Makefile deleted file mode 100644 index ecab050e..00000000 --- a/Modules/Display/Tegra2Vid/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = main.o -NAME = Tegra2Vid - --include ../Makefile.tpl diff --git a/Modules/Display/Tegra2Vid/main.c b/Modules/Display/Tegra2Vid/main.c deleted file mode 100644 index d9405be0..00000000 --- a/Modules/Display/Tegra2Vid/main.c +++ /dev/null @@ -1,326 +0,0 @@ -/** - * main.c - * - Driver core - */ -#define DEBUG 0 -#define VERSION ((0<<8)|10) -#include -#include -#include -#include -#include -#include -#include -#include -#include // ARM Arch -#include "tegra2.h" - -#define ABS(a) ((a)>0?(a):-(a)) - -// === PROTOTYPES === -// Driver - int Tegra2Vid_Install(char **Arguments); -void Tegra2Vid_Uninstall(); -// Internal -// Filesystem -Uint64 Tegra2Vid_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); -Uint64 Tegra2Vid_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); - int Tegra2Vid_IOCtl(tVFS_Node *node, int id, void *data); -// -- Internals - int Tegra2Vid_int_SetMode(int Mode); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, Tegra2Vid, Tegra2Vid_Install, NULL, NULL); -tDevFS_Driver gTegra2Vid_DriverStruct = { - NULL, "Tegra2Vid", - { - .Read = Tegra2Vid_Read, - .Write = Tegra2Vid_Write, - .IOCtl = Tegra2Vid_IOCtl - } -}; -// -- Options -tPAddr gTegra2Vid_PhysBase = TEGRA2VID_BASE; - int gbTegra2Vid_IsVersatile = 1; -// -- KeyVal parse rules -const tKeyVal_ParseRules gTegra2Vid_KeyValueParser = { - NULL, - { - {"Base", "P", &gTegra2Vid_PhysBase}, - {NULL, NULL, NULL} - } -}; -// -- Driver state - int giTegra2Vid_CurrentMode = 0; - int giTegra2Vid_BufferMode; -size_t giTegra2Vid_FramebufferSize; -Uint32 *gpTegra2Vid_IOMem; -tPAddr gTegra2Vid_FramebufferPhys; -void *gpTegra2Vid_Framebuffer; -// -- Misc -tDrvUtil_Video_BufInfo gTegra2Vid_DrvUtil_BufInfo; -tVideo_IOCtl_Pos gTegra2Vid_CursorPos; - -// === CODE === -/** - */ -int Tegra2Vid_Install(char **Arguments) -{ -// KeyVal_Parse(&gTegra2Vid_KeyValueParser, Arguments); - - gpTegra2Vid_IOMem = (void*)MM_MapHWPages(gTegra2Vid_PhysBase, 256/4); - { - Log_Debug("Tegra2Vid", "Display CMD Registers"); - for( int i = 0x000; i <= 0x01A; i ++ ) - Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); - for( int i = 0x028; i <= 0x043; i ++ ) - Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); - Log_Debug("Tegra2Vid", "Display COM Registers"); - for( int i = 0x300; i <= 0x329; i ++ ) - Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); - Log_Debug("Tegra2Vid", "Display DISP Registers"); - for( int i = 0x400; i <= 0x446; i ++ ) - Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); - for( int i = 0x480; i <= 0x484; i ++ ) - Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); - for( int i = 0x4C0; i <= 0x4C1; i ++ ) - Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); - - Log_Debug("Tegra2Vid", "WINC_A Registers"); - for( int i = 0x700; i <= 0x714; i ++ ) - Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); - Log_Debug("Tegra2Vid", "WINBUF_A"); - for( int i = 0x800; i <= 0x80A; i ++ ) - Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]); - } -// return 1; - - giTegra2Vid_FramebufferSize = - (gpTegra2Vid_IOMem[DC_WIN_A_SIZE_0]&0xFFFF) - *(gpTegra2Vid_IOMem[DC_WIN_A_SIZE_0]>>16)*4; - - Log_Debug("Tegra2Vid", "giTegra2Vid_FramebufferSize = 0x%x", giTegra2Vid_FramebufferSize); - gpTegra2Vid_Framebuffer = MM_MapHWPages( - gpTegra2Vid_IOMem[DC_WINBUF_A_START_ADDR_0], - (giTegra2Vid_FramebufferSize+PAGE_SIZE-1)/PAGE_SIZE - ); - memset(gpTegra2Vid_Framebuffer, 0x1F, 0x1000); - - -// Tegra2Vid_int_SetMode(4); - - DevFS_AddDevice( &gTegra2Vid_DriverStruct ); - - return 0; -} - -/** - * \brief Clean up resources for driver unloading - */ -void Tegra2Vid_Uninstall() -{ -} - -/** - * \brief Read from the framebuffer - */ -Uint64 Tegra2Vid_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) -{ - return 0; -} - -/** - * \brief Write to the framebuffer - */ -Uint64 Tegra2Vid_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - gTegra2Vid_DrvUtil_BufInfo.BufferFormat = giTegra2Vid_BufferMode; - return DrvUtil_Video_WriteLFB(&gTegra2Vid_DrvUtil_BufInfo, Offset, Length, Buffer); -} - -const char *csaTegra2Vid_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL}; - -/** - * \brief Handle messages to the device - */ -int Tegra2Vid_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - int ret = -2; - ENTER("pNode iID pData", Node, ID, Data); - - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaTegra2Vid_IOCtls); - - case VIDEO_IOCTL_SETBUFFORMAT: - DrvUtil_Video_RemoveCursor( &gTegra2Vid_DrvUtil_BufInfo ); - ret = giTegra2Vid_BufferMode; - if(Data) giTegra2Vid_BufferMode = *(int*)Data; - if(gTegra2Vid_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) - DrvUtil_Video_SetCursor( &gTegra2Vid_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor ); - break; - - case VIDEO_IOCTL_GETSETMODE: - if(Data) - { - int newMode; - - if( !CheckMem(Data, sizeof(int)) ) - LEAVE_RET('i', -1); - - newMode = *(int*)Data; - - if(newMode < 0 || newMode >= ciTegra2Vid_ModeCount) - LEAVE_RET('i', -1); - - if(newMode != giTegra2Vid_CurrentMode) - { - giTegra2Vid_CurrentMode = newMode; - Tegra2Vid_int_SetMode( newMode ); - } - } - ret = giTegra2Vid_CurrentMode; - break; - - case VIDEO_IOCTL_FINDMODE: - { - tVideo_IOCtl_Mode *mode = Data; - int closest, closestArea, reqArea = 0; - if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode))) - LEAVE_RET('i', -1); - if( mode->bpp != 32 ) - LEAVE_RET('i', 0); - if( mode->flags != 0 ) - LEAVE_RET('i', 0); - - ret = 0; - - for( int i = 0; i < ciTegra2Vid_ModeCount; i ++ ) - { - int area; - if(mode->width == caTegra2Vid_Modes[i].W && mode->height == caTegra2Vid_Modes[i].H) { - mode->id = i; - ret = 1; - break; - } - - area = caTegra2Vid_Modes[i].W * caTegra2Vid_Modes[i].H; - if(!reqArea) { - reqArea = mode->width * mode->height; - closest = i; - closestArea = area; - } - else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) { - closest = i; - closestArea = area; - } - } - - if( ret == 0 ) - { - mode->id = closest; - ret = 1; - } - mode->width = caTegra2Vid_Modes[mode->id].W; - mode->height = caTegra2Vid_Modes[mode->id].H; - break; - } - - case VIDEO_IOCTL_MODEINFO: - { - tVideo_IOCtl_Mode *mode = Data; - if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode))) - LEAVE_RET('i', -1); - if(mode->id < 0 || mode->id >= ciTegra2Vid_ModeCount) - LEAVE_RET('i', 0); - - - mode->bpp = 32; - mode->flags = 0; - mode->width = caTegra2Vid_Modes[mode->id].W; - mode->height = caTegra2Vid_Modes[mode->id].H; - - ret = 1; - break; - } - - case VIDEO_IOCTL_SETCURSOR: - if( !Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) ) - LEAVE_RET('i', -1); - - DrvUtil_Video_RemoveCursor( &gTegra2Vid_DrvUtil_BufInfo ); - - gTegra2Vid_CursorPos = *(tVideo_IOCtl_Pos*)Data; - if(gTegra2Vid_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) - DrvUtil_Video_DrawCursor( - &gTegra2Vid_DrvUtil_BufInfo, - gTegra2Vid_CursorPos.x*giVT_CharWidth, - gTegra2Vid_CursorPos.y*giVT_CharHeight - ); - else - DrvUtil_Video_DrawCursor( - &gTegra2Vid_DrvUtil_BufInfo, - gTegra2Vid_CursorPos.x, - gTegra2Vid_CursorPos.y - ); - break; - - default: - LEAVE('i', -2); - return -2; - } - - LEAVE('i', ret); - return ret; -} - -// -// -// - -int Tegra2Vid_int_SetMode(int Mode) -{ - const struct sTegra2_Disp_Mode *mode = &caTegra2Vid_Modes[Mode]; - int w = mode->W, h = mode->H; // Horizontal/Vertical Active - *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_FRONT_PORCH_0) = (mode->VFP << 16) | mode->HFP; - *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_SYNC_WIDTH_0) = (mode->HS << 16) | mode->HS; - *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_BACK_PORCH_0) = (mode->VBP << 16) | mode->HBP; - *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_DISP_ACTIVE_0) = (mode->H << 16) | mode->W; - - *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_POSITION_0) = 0; - *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_SIZE_0) = (mode->H << 16) | mode->W; - *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_DISP_COLOR_CONTROL_0) = 0x8; // BASE888 - *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_COLOR_DEPTH_0) = 12; // Could be 13 (BGR/RGB) - *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_PRESCALED_SIZE_0) = (mode->H << 16) | mode->W; - - Log_Debug("Tegra2Vid", "Mode %i (%ix%i) selected", Mode, w, h); - - if( !gpTegra2Vid_Framebuffer || w*h*4 != giTegra2Vid_FramebufferSize ) - { - if( gpTegra2Vid_Framebuffer ) - { - // TODO: Free framebuffer for reallocation - } - - giTegra2Vid_FramebufferSize = w*h*4; - - gpTegra2Vid_Framebuffer = (void*)MM_AllocDMA( - (giTegra2Vid_FramebufferSize + PAGE_SIZE-1) / PAGE_SIZE, - 32, - &gTegra2Vid_FramebufferPhys - ); - // TODO: Catch allocation failures - Log_Debug("Tegra2Vid", "0x%x byte framebuffer at %p (%P phys)", - giTegra2Vid_FramebufferSize, - gpTegra2Vid_Framebuffer, - gTegra2Vid_FramebufferPhys - ); - - // Tell hardware - *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_START_ADDR_0) = gTegra2Vid_FramebufferPhys; - *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_ADDR_V_OFFSET_0) = 0; // Y offset - *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_ADDR_H_OFFSET_0) = 0; // X offset - } - - return 0; -} diff --git a/Modules/Display/Tegra2Vid/tegra2.h b/Modules/Display/Tegra2Vid/tegra2.h deleted file mode 100644 index a3f126b6..00000000 --- a/Modules/Display/Tegra2Vid/tegra2.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Acess2 NVidia Tegra2 Display Driver - * - By John Hodge (thePowersGang) - * - * tegra2.h - * - Driver definitions - */ -#ifndef _TEGRA2_DISP_H_ -#define _TEGRA2_DISP_H_ - -#define TEGRA2VID_BASE 0x54200000 // 0x40000 Large (256 KB) - -const struct sTegra2_Disp_Mode -{ - Uint16 W, H; - Uint16 HFP, VFP; - Uint16 HS, VS; - Uint16 HBP, VBP; -} caTegra2Vid_Modes[] = { - // TODO: VESA timings - {720, 487, 16,33, 63, 33, 59, 133}, // NTSC 2 - {720, 576, 12,33, 63, 33, 69, 193}, // PAL 2 (VFP shown as 2/33, used 33) - {720, 483, 16, 6, 63, 6, 59, 30}, // 480p - {1280, 720, 70, 5, 804, 6, 220, 20}, // 720p - {1920,1080, 44, 4, 884, 5, 148, 36}, // 1080p - // TODO: Can all but HA/VA be constant and those select the resolution? -}; -const int ciTegra2Vid_ModeCount = sizeof(caTegra2Vid_Modes)/sizeof(caTegra2Vid_Modes[0]); - -enum eTegra2_Disp_Regs -{ - DC_DISP_DISP_SIGNAL_OPTIONS0_0 = 0x400, - DC_DISP_DISP_SIGNAL_OPTIONS1_0, // 401 - DC_DISP_DISP_WIN_OPTIONS_0, // 402 - DC_DISP_MEM_HIGH_PRIORITY_0, // 403 - DC_DISP_MEM_HIGH_PRIORITY_TIMER_0, // 404 - DC_DISP_DISP_TIMING_OPTIONS_0, // 405 - DC_DISP_REF_TO_SYNC_0, // 406 (TrimSlice 0x0001 000B) - DC_DISP_SYNC_WIDTH_0, // 407 (TrimSlice 0x0004 003A) - DC_DISP_BACK_PORCH_0, // 408 (TrimSlice 0x0004 003A) - DC_DISP_DISP_ACTIVE_0, // 409 (TrimSlice 0x0300 0400) - DC_DISP_FRONT_PORCH_0, // 40A (TrimSlice 0x0004 003A) - - DC_DISP_H_PULSE0_CONTROL_0, // 40B - - DC_DISP_DISP_COLOR_CONTROL_0 = 0x430, - - DC_WINC_A_COLOR_PALETTE_0 = 0x500, - DC_WINC_A_PALETTE_COLOR_EXT_0 = 0x600, - DC_WIN_A_WIN_OPTIONS_0 = 0x700, - DC_WIN_A_BYTE_SWAP_0, // 701 - DC_WIN_A_BUFFER_CONTROL_0, // 702 - DC_WIN_A_COLOR_DEPTH_0, // 703 - DC_WIN_A_POSITION_0, // 704 - DC_WIN_A_SIZE_0, // 705 (TrimSlice 0x0300 0400) - DC_WIN_A_PRESCALED_SIZE_0, - DC_WIN_A_H_INITIAL_DDA_0, - DC_WIN_A_V_INITIAL_DDA_0, - DC_WIN_A_DDA_INCREMENT_0, - DC_WIN_A_LINE_STRIDE_0, - DC_WIN_A_BUF_STRIDE_0, - DC_WIN_A_BUFFER_ADDR_MODE_0, - DC_WIN_A_DV_CONTROL_0, - DC_WIN_A_BLEND_NOKEY_0, - - DC_WINBUF_A_START_ADDR_0 = 0x800, - DC_WINBUF_A_START_ADDR_NS_0, - DC_WINBUF_A_ADDR_H_OFFSET_0, - DC_WINBUF_A_ADDR_H_OFFSET_NS_0, - DC_WINBUF_A_ADDR_V_OFFSET_0, - DC_WINBUF_A_ADDR_V_OFFSET_NS_0, -}; - -#endif - diff --git a/Modules/Display/VESA/Makefile b/Modules/Display/VESA/Makefile deleted file mode 100644 index d8da2334..00000000 --- a/Modules/Display/VESA/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = main.o -NAME = VESA - --include ../Makefile.tpl diff --git a/Modules/Display/VESA/common.h b/Modules/Display/VESA/common.h deleted file mode 100644 index eeaddd56..00000000 --- a/Modules/Display/VESA/common.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - */ -#ifndef _COMMON_H_ -#define _COMMON_H_ - -// === TYPES === -typedef struct sFarPtr -{ - Uint16 ofs; - Uint16 seg; -} tFarPtr; - -typedef struct sVesa_Mode -{ - Uint16 code; - Uint16 width, height; - Uint16 pitch, bpp; - Uint16 flags; - Uint32 fbSize; - Uint32 framebuffer; -} tVesa_Mode; - -typedef struct sVesa_CallModeInfo -{ - Uint16 attributes; - Uint8 winA,winB; - Uint16 granularity; - Uint16 winsize; - Uint16 segmentA, segmentB; - tFarPtr realFctPtr; - Uint16 pitch; // Bytes per scanline - - Uint16 Xres, Yres; - Uint8 Wchar, Ychar, planes, bpp, banks; - Uint8 memory_model, bank_size, image_pages; - Uint8 reserved0; - - Uint8 red_mask, red_position; - Uint8 green_mask, green_position; - Uint8 blue_mask, blue_position; - Uint8 rsv_mask, rsv_position; - Uint8 directcolor_attributes; - - Uint32 physbase; // Your LFB address ;) - Uint32 reserved1; - Sint16 reserved2; -} tVesa_CallModeInfo; - -typedef struct sVesa_CallInfo -{ - char signature[4]; // == "VESA" - Uint16 Version; // == 0x0300 for Vesa 3.0 - tFarPtr OEMString; // isa vbeFarPtr - Uint8 Capabilities[4]; - tFarPtr VideoModes; // isa vbeParPtr - Uint16 TotalMemory; // as # of 64KB blocks -} tVesa_CallInfo; - -#endif diff --git a/Modules/Display/VESA/main.c b/Modules/Display/VESA/main.c deleted file mode 100644 index a85d3137..00000000 --- a/Modules/Display/VESA/main.c +++ /dev/null @@ -1,418 +0,0 @@ -/* - * AcessOS 1 - * Video BIOS Extensions (Vesa) Driver - */ -#define DEBUG 0 -#define VERSION 0x100 - -#include -#include -#include -#include -#include -#include -#include "common.h" - -// === CONSTANTS === -#define FLAG_LFB 0x1 -#define VESA_DEFAULT_FRAMEBUFFER (KERNEL_BASE|0xA0000) -#define BLINKING_CURSOR 1 -#if BLINKING_CURSOR -# define VESA_CURSOR_PERIOD 1000 -#endif - -// === PROTOTYPES === - int Vesa_Install(char **Arguments); -Uint64 Vesa_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -Uint64 Vesa_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); - int Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data); - int Vesa_Int_SetMode(int Mode); - int Vesa_Int_FindMode(tVideo_IOCtl_Mode *data); - int Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data); -void Vesa_int_HideCursor(void); -void Vesa_int_ShowCursor(void); -void Vesa_FlipCursor(void *Arg); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, Vesa, Vesa_Install, NULL, "PCI", "VM8086", NULL); -tVFS_NodeType gVesa_NodeType = { - .Read = Vesa_Read, - .Write = Vesa_Write, - .IOCtl = Vesa_IOCtl - }; -tDevFS_Driver gVesa_DriverStruct = { - NULL, "Vesa", - {.Type = &gVesa_NodeType} - }; -tMutex glVesa_Lock; -tVM8086 *gpVesa_BiosState; - int giVesaDriverId = -1; -// --- Video Modes --- - int giVesaCurrentMode = 0; -tVesa_Mode *gVesa_Modes; -tVesa_Mode *gpVesaCurMode; - int giVesaModeCount = 0; - int gbVesaModesChecked; -// --- Framebuffer --- -char *gpVesa_Framebuffer = (void*)VESA_DEFAULT_FRAMEBUFFER; - int giVesaPageCount = 0; //!< Framebuffer size in pages -// --- Cursor Control --- - int giVesaCursorX = -1; - int giVesaCursorY = -1; - int giVesaCursorTimer = -1; // Invalid timer - int gbVesa_CursorVisible = 0; -// --- 2D Video Stream Handlers --- -tDrvUtil_Video_BufInfo gVesa_BufInfo; - -// === CODE === -int Vesa_Install(char **Arguments) -{ - tVesa_CallInfo *info; - tFarPtr infoPtr; - Uint16 *modes; - int i; - - // Allocate Info Block - gpVesa_BiosState = VM8086_Init(); - info = VM8086_Allocate(gpVesa_BiosState, 512, &infoPtr.seg, &infoPtr.ofs); - // Set Requested Version - memcpy(info->signature, "VBE2", 4); - // Set Registers - gpVesa_BiosState->AX = 0x4F00; - gpVesa_BiosState->ES = infoPtr.seg; gpVesa_BiosState->DI = infoPtr.ofs; - // Call Interrupt - VM8086_Int(gpVesa_BiosState, 0x10); - if(gpVesa_BiosState->AX != 0x004F) { - Log_Warning("VESA", "Vesa_Install - VESA/VBE Unsupported (AX = 0x%x)", gpVesa_BiosState->AX); - return MODULE_ERR_NOTNEEDED; - } - - //Log_Debug("VESA", "info->VideoModes = %04x:%04x", info->VideoModes.seg, info->VideoModes.ofs); - modes = (Uint16 *) VM8086_GetPointer(gpVesa_BiosState, info->VideoModes.seg, info->VideoModes.ofs); - - // Read Modes - for( giVesaModeCount = 0; modes[giVesaModeCount] != 0xFFFF; giVesaModeCount++ ); - gVesa_Modes = (tVesa_Mode *)malloc( giVesaModeCount * sizeof(tVesa_Mode) ); - - Log_Debug("VESA", "%i Modes", giVesaModeCount); - - // Insert Text Mode - gVesa_Modes[0].width = 80; - gVesa_Modes[0].height = 25; - gVesa_Modes[0].bpp = 12; - gVesa_Modes[0].code = 0x3; - gVesa_Modes[0].flags = 1; - gVesa_Modes[0].fbSize = 80*25*2; - gVesa_Modes[0].framebuffer = 0xB8000; - - for( i = 1; i < giVesaModeCount; i++ ) - { - gVesa_Modes[i].code = modes[i]; - } - -// VM8086_Deallocate( info ); - - // Install Device - giVesaDriverId = DevFS_AddDevice( &gVesa_DriverStruct ); - if(giVesaDriverId == -1) return MODULE_ERR_MISC; - - return MODULE_ERR_OK; -} - -void Vesa_int_FillModeList(void) -{ - if( !gbVesaModesChecked ) - { - int i; - tVesa_CallModeInfo *modeinfo; - tFarPtr modeinfoPtr; - - modeinfo = VM8086_Allocate(gpVesa_BiosState, 512, &modeinfoPtr.seg, &modeinfoPtr.ofs); - for( i = 1; i < giVesaModeCount; i ++ ) - { - // Get Mode info - gpVesa_BiosState->AX = 0x4F01; - gpVesa_BiosState->CX = gVesa_Modes[i].code; - gpVesa_BiosState->ES = modeinfoPtr.seg; - gpVesa_BiosState->DI = modeinfoPtr.ofs; - VM8086_Int(gpVesa_BiosState, 0x10); - - // Parse Info - gVesa_Modes[i].flags = 0; - if ( (modeinfo->attributes & 0x90) == 0x90 ) - { - gVesa_Modes[i].flags |= FLAG_LFB; - gVesa_Modes[i].framebuffer = modeinfo->physbase; - gVesa_Modes[i].fbSize = modeinfo->Yres*modeinfo->pitch; - } else { - gVesa_Modes[i].framebuffer = 0; - gVesa_Modes[i].fbSize = 0; - } - - gVesa_Modes[i].pitch = modeinfo->pitch; - gVesa_Modes[i].width = modeinfo->Xres; - gVesa_Modes[i].height = modeinfo->Yres; - gVesa_Modes[i].bpp = modeinfo->bpp; - - #if DEBUG - Log_Log("VESA", "0x%x - %ix%ix%i", - gVesa_Modes[i].code, gVesa_Modes[i].width, gVesa_Modes[i].height, gVesa_Modes[i].bpp); - #endif - } - -// VM8086_Deallocate( modeinfo ); - - gbVesaModesChecked = 1; - } -} - -/* Read from the framebuffer - */ -Uint64 Vesa_Read(tVFS_Node *Node, Uint64 off, Uint64 len, void *buffer) -{ - #if DEBUG >= 2 - Log("Vesa_Read: () - NULL\n"); - #endif - return 0; -} - -/** - * \brief Write to the framebuffer - */ -Uint64 Vesa_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - if( gVesa_Modes[giVesaCurrentMode].framebuffer == 0 ) { - Log_Warning("VESA", "Vesa_Write - Non-LFB Modes not yet supported."); - return 0; - } - - return DrvUtil_Video_WriteLFB(&gVesa_BufInfo, Offset, Length, Buffer); -} - -const char *csaVESA_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL}; -/** - * \brief Handle messages to the device - */ -int Vesa_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - int ret; - //Log_Debug("VESA", "Vesa_Ioctl: (Node=%p, ID=%i, Data=%p)", Node, ID, Data); - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_VIDEO, "VESA", VERSION, csaVESA_IOCtls); - - case VIDEO_IOCTL_GETSETMODE: - if( !Data ) return giVesaCurrentMode; - return Vesa_Int_SetMode( *(int*)Data ); - - case VIDEO_IOCTL_FINDMODE: - return Vesa_Int_FindMode((tVideo_IOCtl_Mode*)Data); - case VIDEO_IOCTL_MODEINFO: - return Vesa_Int_ModeInfo((tVideo_IOCtl_Mode*)Data); - - case VIDEO_IOCTL_SETBUFFORMAT: - Vesa_int_HideCursor(); - ret = gVesa_BufInfo.BufferFormat; - if(Data) gVesa_BufInfo.BufferFormat = *(int*)Data; - if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) - DrvUtil_Video_SetCursor( &gVesa_BufInfo, &gDrvUtil_TextModeCursor ); - Vesa_int_ShowCursor(); - return ret; - - case VIDEO_IOCTL_SETCURSOR: // Set cursor position - Vesa_int_HideCursor(); - giVesaCursorX = ((tVideo_IOCtl_Pos*)Data)->x; - giVesaCursorY = ((tVideo_IOCtl_Pos*)Data)->y; - Vesa_int_ShowCursor(); - return 0; - - case VIDEO_IOCTL_SETCURSORBITMAP: - DrvUtil_Video_SetCursor( &gVesa_BufInfo, Data ); - return 0; - } - return 0; -} - -/** - * \brief Updates the video mode - */ -int Vesa_Int_SetMode(int mode) -{ - // Sanity Check values - if(mode < 0 || mode > giVesaModeCount) return -1; - - // Check for fast return - if(mode == giVesaCurrentMode) return 1; - - Vesa_int_FillModeList(); - - Time_RemoveTimer(giVesaCursorTimer); - giVesaCursorTimer = -1; - - Mutex_Acquire( &glVesa_Lock ); - - gpVesa_BiosState->AX = 0x4F02; - gpVesa_BiosState->BX = gVesa_Modes[mode].code; - if(gVesa_Modes[mode].flags & FLAG_LFB) { - gpVesa_BiosState->BX |= 0x4000; // Bit 14 - Use LFB - } - - // Set Mode - VM8086_Int(gpVesa_BiosState, 0x10); - - // Map Framebuffer - if( (tVAddr)gpVesa_Framebuffer != VESA_DEFAULT_FRAMEBUFFER ) - MM_UnmapHWPages((tVAddr)gpVesa_Framebuffer, giVesaPageCount); - giVesaPageCount = (gVesa_Modes[mode].fbSize + 0xFFF) >> 12; - gpVesa_Framebuffer = (void*)MM_MapHWPages(gVesa_Modes[mode].framebuffer, giVesaPageCount); - - Log_Log("VESA", "Setting mode to %i (%ix%i %ibpp) %p[0x%x] maps %P", - mode, - gVesa_Modes[mode].width, gVesa_Modes[mode].height, - gVesa_Modes[mode].bpp, - gpVesa_Framebuffer, giVesaPageCount << 12, gVesa_Modes[mode].framebuffer - ); - - // Record Mode Set - giVesaCurrentMode = mode; - gpVesaCurMode = &gVesa_Modes[giVesaCurrentMode]; - - Mutex_Release( &glVesa_Lock ); - - gVesa_BufInfo.Framebuffer = gpVesa_Framebuffer; - gVesa_BufInfo.Pitch = gVesa_Modes[mode].pitch; - gVesa_BufInfo.Width = gVesa_Modes[mode].width; - gVesa_BufInfo.Height = gVesa_Modes[mode].height; - gVesa_BufInfo.Depth = gVesa_Modes[mode].bpp; - - return 1; -} - -int Vesa_Int_FindMode(tVideo_IOCtl_Mode *data) -{ - int i; - int best = -1, bestFactor = 1000; - int factor, tmp; - - ENTER("idata->width idata->height idata->bpp", data->width, data->height, data->bpp); - - Vesa_int_FillModeList(); - - for(i=0;iwidth && gVesa_Modes[i].height == data->height) - { - //if( (data->bpp == 32 || data->bpp == 24) - // && (gVesa_Modes[i].bpp == 32 || gVesa_Modes[i].bpp == 24) ) - if( data->bpp == gVesa_Modes[i].bpp ) - { - LOG("Perfect!"); - best = i; - break; - } - } - - tmp = gVesa_Modes[i].width * gVesa_Modes[i].height; - tmp -= data->width * data->height; - tmp = tmp < 0 ? -tmp : tmp; - factor = tmp * 1000 / (data->width * data->height); - - if( data->bpp == 8 && gVesa_Modes[i].bpp != 8 ) continue; - if( data->bpp == 16 && gVesa_Modes[i].bpp != 16 ) continue; - - if( (data->bpp == 32 || data->bpp == 24) - && (gVesa_Modes[i].bpp == 32 || gVesa_Modes[i].bpp == 24) ) - { - if( data->bpp == gVesa_Modes[i].bpp ) - factor /= 2; - } - else { - if( data->bpp != gVesa_Modes[i].bpp ) - continue ; - } - - LOG("factor = %i", factor); - - if(factor < bestFactor) - { - bestFactor = factor; - best = i; - } - } - data->id = best; - data->width = gVesa_Modes[best].width; - data->height = gVesa_Modes[best].height; - data->bpp = gVesa_Modes[best].bpp; - LEAVE('i', best); - return best; -} - -int Vesa_Int_ModeInfo(tVideo_IOCtl_Mode *data) -{ - if(data->id < 0 || data->id > giVesaModeCount) return -1; - - Vesa_int_FillModeList(); - - data->width = gVesa_Modes[data->id].width; - data->height = gVesa_Modes[data->id].height; - data->bpp = gVesa_Modes[data->id].bpp; - return 1; -} - -void Vesa_int_HideCursor(void) -{ - DrvUtil_Video_RemoveCursor( &gVesa_BufInfo ); - #if BLINKING_CURSOR - if(giVesaCursorTimer != -1) { - Time_RemoveTimer(giVesaCursorTimer); - giVesaCursorTimer = -1; - } - #endif -} - -void Vesa_int_ShowCursor(void) -{ - gbVesa_CursorVisible = (giVesaCursorX >= 0); - if(gVesa_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT) - { - DrvUtil_Video_DrawCursor( - &gVesa_BufInfo, - giVesaCursorX*giVT_CharWidth, - giVesaCursorY*giVT_CharHeight - ); - #if BLINKING_CURSOR - giVesaCursorTimer = Time_CreateTimer(VESA_CURSOR_PERIOD, Vesa_FlipCursor, NULL); - #endif - } - else - DrvUtil_Video_DrawCursor( - &gVesa_BufInfo, - giVesaCursorX, - giVesaCursorY - ); -} - -/** - * \brief Swaps the text cursor on/off - */ -void Vesa_FlipCursor(void *Arg) -{ - if( gVesa_BufInfo.BufferFormat != VIDEO_BUFFMT_TEXT ) - return ; - - if( gbVesa_CursorVisible ) - DrvUtil_Video_RemoveCursor(&gVesa_BufInfo); - else - DrvUtil_Video_DrawCursor(&gVesa_BufInfo, - giVesaCursorX*giVT_CharWidth, - giVesaCursorY*giVT_CharHeight - ); - gbVesa_CursorVisible = !gbVesa_CursorVisible; - - #if BLINKING_CURSOR - giVesaCursorTimer = Time_CreateTimer(VESA_CURSOR_PERIOD, Vesa_FlipCursor, Arg); - #endif -} - diff --git a/Modules/Filesystems/Ext2/Makefile b/Modules/Filesystems/Ext2/Makefile deleted file mode 100644 index 471ba49e..00000000 --- a/Modules/Filesystems/Ext2/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = ext2.o read.o dir.o write.o -NAME = Ext2 - --include ../Makefile.tpl diff --git a/Modules/Filesystems/Ext2/dir.c b/Modules/Filesystems/Ext2/dir.c deleted file mode 100644 index 5eb476f4..00000000 --- a/Modules/Filesystems/Ext2/dir.c +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Acess OS - * Ext2 Driver Version 1 - */ -/** - * \file dir.c - * \brief Second Extended Filesystem Driver - * \todo Implement file full write support - */ -#define DEBUG 1 -#define VERBOSE 0 -#include "ext2_common.h" - -// === MACROS === -#define BLOCK_DIR_OFS(_data, _block) ((Uint16*)(_data)[(_block)]) - -// === PROTOTYPES === -char *Ext2_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *FileName); - int Ext2_MkNod(tVFS_Node *Node, const char *Name, Uint Flags); - int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName); - int Ext2_Link(tVFS_Node *Parent, tVFS_Node *Node, const char *Name); -// --- Helpers --- -tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeId); - -// === GLOBALS === -tVFS_NodeType gExt2_DirType = { - .TypeName = "ext2-dir", - .ReadDir = Ext2_ReadDir, - .FindDir = Ext2_FindDir, - .MkNod = Ext2_MkNod, - .Relink = Ext2_Relink, - .Link = Ext2_Link, - .Close = Ext2_CloseFile - }; -tVFS_NodeType gExt2_FileType = { - .TypeName = "ext2-file", - .Read = Ext2_Read, - .Write = Ext2_Write, - .Close = Ext2_CloseFile - }; - -// === CODE === -/** - * \brief Reads a directory entry - * \param Node Directory node - * \param Pos Position of desired element - */ -char *Ext2_ReadDir(tVFS_Node *Node, int Pos) -{ - tExt2_Inode inode; - tExt2_DirEnt dirent; - Uint64 Base; // Block's Base Address - int block = 0; - Uint ofs = 0; - int entNum = 0; - tExt2_Disk *disk = Node->ImplPtr; - Uint size; - - ENTER("pNode iPos", Node, Pos); - - // Read directory's inode - Ext2_int_ReadInode(disk, Node->Inode, &inode); - size = inode.i_size; - - LOG("inode.i_block[0] = 0x%x", inode.i_block[0]); - - // Find Entry - // Get First Block - // - Do this ourselves as it is a simple operation - Base = inode.i_block[0] * disk->BlockSize; - // Scan directory - while(Pos -- && size > 0) - { - VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent); - ofs += dirent.rec_len; - size -= dirent.rec_len; - entNum ++; - - if(ofs >= disk->BlockSize) { - block ++; - if( ofs > disk->BlockSize ) { - Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring", - entNum-1, Node->Inode); - } - ofs = 0; - Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block ); - } - } - - // Check for the end of the list - if(size <= 0) { - LEAVE('n'); - return NULL; - } - - // Read Entry - VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent ); - //LOG("dirent.inode = %i", dirent.inode); - //LOG("dirent.rec_len = %i", dirent.rec_len); - //LOG("dirent.name_len = %i", dirent.name_len); - dirent.name[ dirent.name_len ] = '\0'; // Cap off string - - - // Ignore . and .. (these are done in the VFS) - if( (dirent.name[0] == '.' && dirent.name[1] == '\0') - || (dirent.name[0] == '.' && dirent.name[1] == '.' && dirent.name[2]=='\0')) { - LEAVE('p', VFS_SKIP); - return VFS_SKIP; // Skip - } - - LEAVE('s', dirent.name); - // Create new node - return strdup(dirent.name); -} - -/** - * \brief Gets information about a file - * \param Node Parent Node - * \param Filename Name of wanted file - * \return VFS Node of file - */ -tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *Filename) -{ - tExt2_Disk *disk = Node->ImplPtr; - tExt2_Inode inode; - tExt2_DirEnt dirent; - Uint64 Base; // Block's Base Address - int block = 0; - Uint ofs = 0; - int entNum = 0; - Uint size; - int filenameLen = strlen(Filename); - - // Read directory's inode - Ext2_int_ReadInode(disk, Node->Inode, &inode); - size = inode.i_size; - - // Get First Block - // - Do this ourselves as it is a simple operation - Base = inode.i_block[0] * disk->BlockSize; - // Find File - while(size > 0) - { - VFS_ReadAt( disk->FD, Base+ofs, sizeof(tExt2_DirEnt), &dirent); - dirent.name[ dirent.name_len ] = '\0'; // Cap off string - // If it matches, create a node and return it - if(dirent.name_len == filenameLen && strcmp(dirent.name, Filename) == 0) - return Ext2_int_CreateNode( disk, dirent.inode ); - // Increment pointers - ofs += dirent.rec_len; - size -= dirent.rec_len; - entNum ++; - - // Check for end of block - if(ofs >= disk->BlockSize) { - block ++; - if( ofs > disk->BlockSize ) { - Log_Warning("EXT2", "Directory Entry %i of inode %i extends over a block boundary, ignoring", - entNum-1, Node->Inode); - } - ofs = 0; - Base = Ext2_int_GetBlockAddr( disk, inode.i_block, block ); - } - } - - return NULL; -} - -/** - * \fn int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags) - * \brief Create a new node - */ -int Ext2_MkNod(tVFS_Node *Parent, const char *Name, Uint Flags) -{ - #if 0 - tVFS_Node *child; - Uint64 inodeNum; - tExt2_Inode inode; - inodeNum = Ext2_int_AllocateInode(Parent->ImplPtr, Parent->Inode); - - memset(&inode, 0, sizeof(tExt2_Inode)); - - // File type - inode.i_mode = 0664; - if( Flags & VFS_FFLAG_READONLY ) - inode.i_mode &= ~0222; - if( Flags & VFS_FFLAG_SYMLINK ) - inode.i_mode |= EXT2_S_IFLNK; - else if( Flags & VFS_FFLAG_DIRECTORY ) - inode.i_mode |= EXT2_S_IFDIR | 0111; - - inode.i_uid = Threads_GetUID(); - inode.i_gid = Threads_GetGID(); - inode.i_ctime = - inode.i_mtime = - inode.i_atime = now() / 1000; - - child = Ext2_int_CreateNode(Parent->ImplPtr, inodeNum); - return Ext2_Link(Parent, child, Name); - #else - return 1; - #endif -} - -/** - * \brief Rename a file - * \param Node This (directory) node - * \param OldName Old name of file - * \param NewName New name for file - * \return Boolean Failure - See ::tVFS_Node.Relink for info - */ -int Ext2_Relink(tVFS_Node *Node, const char *OldName, const char *NewName) -{ - return 1; -} - -/** - * \brief Links an existing node to a new name - * \param Parent Parent (directory) node - * \param Node Node to link - * \param Name New name for the node - * \return Boolean Failure - See ::tVFS_Node.Link for info - */ -int Ext2_Link(tVFS_Node *Node, tVFS_Node *Child, const char *Name) -{ - #if 0 - tExt2_Disk *disk = Node->ImplPtr; - tExt2_Inode inode; - tExt2_DirEnt dirent; - tExt2_DirEnt newEntry; - Uint64 Base; // Block's Base Address - int block = 0, ofs = 0; - Uint size; - void *blockData; - int bestMatch = -1, bestSize, bestBlock, bestOfs; - int nEntries; - - blockData = malloc(disk->BlockSize); - - // Read child inode (get's the file type) - Ext2_int_ReadInode(disk, Child->Inode, &inode); - - // Create a stub entry - newEntry.inode = Child->Inode; - newEntry.name_len = strlen(Name); - newEntry.rec_len = (newEntry.name_len+3+8)&~3; - newEntry.type = inode.i_mode >> 12; - memcpy(newEntry.name, Name, newEntry.name_len); - - // Read directory's inode - Ext2_int_ReadInode(disk, Node->Inode, &inode); - size = inode.i_size; - - // Get a lock on the inode - Ext2_int_LockInode(disk, Node->Inode); - - // Get First Block - // - Do this ourselves as it is a simple operation - base = inode.i_block[0] * disk->BlockSize; - VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData ); - block = 0; - // Find File - while(size > 0) - { - dirent = blockData + ofs; - // Sanity Check the entry - if(ofs + dirent->rec_len > disk->BlockSize) { - Log_Warning("EXT2", - "Directory entry %i of inode 0x%x extends over a block boundary", - nEntries, (Uint)Node->Inode); - } - else { - - // Free entry - if(dirent->type == 0) { - if( dirent->rec_len >= newEntry.rec_len - && (bestMatch == -1 || bestSize > dirent->rec_len) ) - { - bestMatch = nEntries; - bestSize = dirent->rec_len; - bestBlock = block; - bestOfs = ofs; - } - } - // Non free - check name to avoid duplicates - else { - if(strncmp(Name, dirent->name, dirent->name_len) == 0) { - Ext2_int_UnlockInode(disk, Node->Inode); - return 1; // ERR_??? - } - } - } - - // Increment the pointer - nEntries ++; - ofs += dirent->rec_len; - if( ofs >= disk->BlockSize ) { - // Read the next block if needed - BLOCK_DIR_OFS(Node->Data, block) = nEntries; - block ++; - ofs = 0; - base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); - VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData ); - } - } - - // Check if a free slot was found - if( bestMatch >= 0 ) { - // Read-Modify-Write - bestBlock = Ext2_int_GetBlockAddr(disk, inode.i_block, bestBlock); - if( block > 0 ) - bestMatch = BLOCK_DIR_OFS(Node->Data, bestBlock); - VFS_ReadAt( disk->FD, base, disk->BlockSize, blockData ); - dirent = blockData + bestOfs; - memcpy(dirent, newEntry, newEntry.rec_len); - VFS_WriteAt( disk->FD, base, disk->BlockSize, blockData ); - } - else { - // Allocate block, Write - block = Ext2_int_AllocateBlock(Disk, block); - Log_Warning("EXT2", ""); - } - - Ext2_int_UnlockInode(disk, Node->Inode); - return 0; - #else - return 1; - #endif -} - -// ---- INTERNAL FUNCTIONS ---- -/** - * \fn vfs_node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID) - * \brief Create a new VFS Node - */ -tVFS_Node *Ext2_int_CreateNode(tExt2_Disk *Disk, Uint InodeID) -{ - tExt2_Inode inode; - tVFS_Node retNode; - tVFS_Node *tmpNode; - - if( !Ext2_int_ReadInode(Disk, InodeID, &inode) ) - return NULL; - - if( (tmpNode = Inode_GetCache(Disk->CacheID, InodeID)) ) - return tmpNode; - - - // Set identifiers - retNode.Inode = InodeID; - retNode.ImplPtr = Disk; - - // Set file length - retNode.Size = inode.i_size; - retNode.Data = NULL; - - // Set Access Permissions - retNode.UID = inode.i_uid; - retNode.GID = inode.i_gid; - retNode.NumACLs = 3; - retNode.ACLs = VFS_UnixToAcessACL(inode.i_mode & 0777, inode.i_uid, inode.i_gid); - - // Set Function Pointers - retNode.Type = &gExt2_FileType; - - switch(inode.i_mode & EXT2_S_IFMT) - { - // Symbolic Link - case EXT2_S_IFLNK: - retNode.Flags = VFS_FFLAG_SYMLINK; - break; - // Regular File - case EXT2_S_IFREG: - retNode.Flags = 0; - retNode.Size |= (Uint64)inode.i_dir_acl << 32; - break; - // Directory - case EXT2_S_IFDIR: - retNode.Type = &gExt2_DirType; - retNode.Flags = VFS_FFLAG_DIRECTORY; - retNode.Data = calloc( sizeof(Uint16), DivUp(retNode.Size, Disk->BlockSize) ); - break; - // Unknown, Write protect it to be safe - default: - retNode.Flags = VFS_FFLAG_READONLY; - break; - } - - // Set Timestamps - retNode.ATime = inode.i_atime * 1000; - retNode.MTime = inode.i_mtime * 1000; - retNode.CTime = inode.i_ctime * 1000; - - // Save in node cache and return saved node - return Inode_CacheNode(Disk->CacheID, &retNode); -} diff --git a/Modules/Filesystems/Ext2/ext2.c b/Modules/Filesystems/Ext2/ext2.c deleted file mode 100644 index 7c4bf973..00000000 --- a/Modules/Filesystems/Ext2/ext2.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Acess OS - * Ext2 Driver Version 1 - */ -/** - * \file fs/ext2.c - * \brief Second Extended Filesystem Driver - * \todo Implement file full write support - */ -#define DEBUG 1 -#define VERBOSE 0 -#include "ext2_common.h" -#include - -// === IMPORTS === -extern tVFS_NodeType gExt2_DirType; - -// === PROTOTYPES === - int Ext2_Install(char **Arguments); -// Interface Functions -tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options); -void Ext2_Unmount(tVFS_Node *Node); -void Ext2_CloseFile(tVFS_Node *Node); -// Internal Helpers - int Ext2_int_GetInode(tVFS_Node *Node, tExt2_Inode *Inode); -Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum); -Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent); -void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk); - -// === SEMI-GLOBALS === -MODULE_DEFINE(0, 0x5B /*v0.90*/, FS_Ext2, Ext2_Install, NULL); -tExt2_Disk gExt2_disks[6]; - int giExt2_count = 0; -tVFS_Driver gExt2_FSInfo = { - "ext2", 0, Ext2_InitDevice, Ext2_Unmount, NULL - }; - -// === CODE === -/** - * \fn int Ext2_Install(char **Arguments) - * \brief Install the Ext2 Filesystem Driver - */ -int Ext2_Install(char **Arguments) -{ - VFS_AddDriver( &gExt2_FSInfo ); - return MODULE_ERR_OK; -} - -/** - \brief Initializes a device to be read by by the driver - \param Device String - Device to read from - \param Options NULL Terminated array of option strings - \return Root Node -*/ -tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options) -{ - tExt2_Disk *disk; - int fd; - int groupCount; - tExt2_SuperBlock sb; - tExt2_Inode inode; - - ENTER("sDevice pOptions", Device, Options); - - // Open Disk - fd = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE); //Open Device - if(fd == -1) { - Log_Warning("EXT2", "Unable to open '%s'", Device); - LEAVE('n'); - return NULL; - } - - // Read Superblock at offset 1024 - VFS_ReadAt(fd, 1024, 1024, &sb); // Read Superblock - - // Sanity Check Magic value - if(sb.s_magic != 0xEF53) { - Log_Warning("EXT2", "Volume '%s' is not an EXT2 volume (0x%x != 0xEF53)", - Device, sb.s_magic); - VFS_Close(fd); - LEAVE('n'); - return NULL; - } - - // Get Group count - groupCount = DivUp(sb.s_blocks_count, sb.s_blocks_per_group); - LOG("groupCount = %i", groupCount); - - // Allocate Disk Information - disk = malloc(sizeof(tExt2_Disk) + sizeof(tExt2_Group)*groupCount); - if(!disk) { - Log_Warning("EXT2", "Unable to allocate disk structure"); - VFS_Close(fd); - LEAVE('n'); - return NULL; - } - disk->FD = fd; - memcpy(&disk->SuperBlock, &sb, 1024); - disk->GroupCount = groupCount; - - // Get an inode cache handle - disk->CacheID = Inode_GetHandle(); - - // Get Block Size - LOG("s_log_block_size = 0x%x", sb.s_log_block_size); - disk->BlockSize = 1024 << sb.s_log_block_size; - - // Read Group Information - VFS_ReadAt( - disk->FD, - sb.s_first_data_block * disk->BlockSize + 1024, - sizeof(tExt2_Group)*groupCount, - disk->Groups - ); - - #if VERBOSE - LOG("Block Group 0"); - LOG(".bg_block_bitmap = 0x%x", disk->Groups[0].bg_block_bitmap); - LOG(".bg_inode_bitmap = 0x%x", disk->Groups[0].bg_inode_bitmap); - LOG(".bg_inode_table = 0x%x", disk->Groups[0].bg_inode_table); - LOG("Block Group 1"); - LOG(".bg_block_bitmap = 0x%x", disk->Groups[1].bg_block_bitmap); - LOG(".bg_inode_bitmap = 0x%x", disk->Groups[1].bg_inode_bitmap); - LOG(".bg_inode_table = 0x%x", disk->Groups[1].bg_inode_table); - #endif - - // Get root Inode - Ext2_int_ReadInode(disk, 2, &inode); - - // Create Root Node - memset(&disk->RootNode, 0, sizeof(tVFS_Node)); - disk->RootNode.Inode = 2; // Root inode ID - disk->RootNode.ImplPtr = disk; // Save disk pointer - disk->RootNode.Size = -1; // Fill in later (on readdir) - disk->RootNode.Flags = VFS_FFLAG_DIRECTORY; - - disk->RootNode.Type = &gExt2_DirType; - - // Complete root node - disk->RootNode.UID = inode.i_uid; - disk->RootNode.GID = inode.i_gid; - disk->RootNode.NumACLs = 1; - disk->RootNode.ACLs = &gVFS_ACL_EveryoneRW; - - #if DEBUG - LOG("inode.i_size = 0x%x", inode.i_size); - LOG("inode.i_block[0] = 0x%x", inode.i_block[0]); - #endif - - LEAVE('p', &disk->RootNode); - return &disk->RootNode; -} - -/** - * \fn void Ext2_Unmount(tVFS_Node *Node) - * \brief Close a mounted device - */ -void Ext2_Unmount(tVFS_Node *Node) -{ - tExt2_Disk *disk = Node->ImplPtr; - - VFS_Close( disk->FD ); - Inode_ClearCache( disk->CacheID ); - memset(disk, 0, sizeof(tExt2_Disk)+disk->GroupCount*sizeof(tExt2_Group)); - free(disk); -} - -/** - * \fn void Ext2_CloseFile(tVFS_Node *Node) - * \brief Close a file (Remove it from the cache) - */ -void Ext2_CloseFile(tVFS_Node *Node) -{ - tExt2_Disk *disk = Node->ImplPtr; - Inode_UncacheNode(disk->CacheID, Node->Inode); - return ; -} - -//================================== -//= INTERNAL FUNCTIONS = -//================================== -/** - * \fn int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint InodeId, tExt2_Inode *Inode) - * \brief Read an inode into memory - */ -int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode) -{ - int group, subId; - - ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode); - - if(InodeId == 0) return 0; - - InodeId --; // Inodes are numbered starting at 1 - - group = InodeId / Disk->SuperBlock.s_inodes_per_group; - subId = InodeId % Disk->SuperBlock.s_inodes_per_group; - - LOG("group=%i, subId = %i", group, subId); - - // Read Inode - VFS_ReadAt(Disk->FD, - Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId, - sizeof(tExt2_Inode), - Inode); - - LEAVE('i', 1); - return 1; -} - -/** - * \brief Write a modified inode out to disk - */ -int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode) -{ - int group, subId; - ENTER("pDisk iInodeId pInode", Disk, InodeId, Inode); - - if(InodeId == 0) { - LEAVE('i', 0); - return 0; - } - - InodeId --; // Inodes are numbered starting at 1 - - group = InodeId / Disk->SuperBlock.s_inodes_per_group; - subId = InodeId % Disk->SuperBlock.s_inodes_per_group; - - LOG("group=%i, subId = %i", group, subId); - - // Write Inode - VFS_WriteAt(Disk->FD, - Disk->Groups[group].bg_inode_table * Disk->BlockSize + sizeof(tExt2_Inode)*subId, - sizeof(tExt2_Inode), - Inode - ); - - LEAVE('i', 1); - return 1; -} - -/** - * \fn Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum) - * \brief Get the address of a block from an inode's list - * \param Disk Disk information structure - * \param Blocks Pointer to an inode's block list - * \param BlockNum Block index in list - */ -Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum) -{ - Uint32 *iBlocks; - int dwPerBlock = Disk->BlockSize / 4; - - // Direct Blocks - if(BlockNum < 12) - return (Uint64)Blocks[BlockNum] * Disk->BlockSize; - - // Single Indirect Blocks - iBlocks = malloc( Disk->BlockSize ); - VFS_ReadAt(Disk->FD, (Uint64)Blocks[12]*Disk->BlockSize, Disk->BlockSize, iBlocks); - - BlockNum -= 12; - if(BlockNum < dwPerBlock) - { - BlockNum = iBlocks[BlockNum]; - free(iBlocks); - return (Uint64)BlockNum * Disk->BlockSize; - } - - BlockNum -= dwPerBlock; - // Double Indirect Blocks - if(BlockNum < dwPerBlock*dwPerBlock) - { - VFS_ReadAt(Disk->FD, (Uint64)Blocks[13]*Disk->BlockSize, Disk->BlockSize, iBlocks); - VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks); - BlockNum = iBlocks[BlockNum%dwPerBlock]; - free(iBlocks); - return (Uint64)BlockNum * Disk->BlockSize; - } - - BlockNum -= dwPerBlock*dwPerBlock; - // Triple Indirect Blocks - VFS_ReadAt(Disk->FD, (Uint64)Blocks[14]*Disk->BlockSize, Disk->BlockSize, iBlocks); - VFS_ReadAt(Disk->FD, (Uint64)iBlocks[BlockNum/(dwPerBlock*dwPerBlock)]*Disk->BlockSize, Disk->BlockSize, iBlocks); - VFS_ReadAt(Disk->FD, (Uint64)iBlocks[(BlockNum/dwPerBlock)%dwPerBlock]*Disk->BlockSize, Disk->BlockSize, iBlocks); - BlockNum = iBlocks[BlockNum%dwPerBlock]; - free(iBlocks); - return (Uint64)BlockNum * Disk->BlockSize; -} - -/** - * \fn Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent) - * \brief Allocate an inode (from the current group preferably) - * \param Disk EXT2 Disk Information Structure - * \param Parent Inode ID of the parent (used to locate the child nearby) - */ -Uint32 Ext2_int_AllocateInode(tExt2_Disk *Disk, Uint32 Parent) -{ -// Uint block = (Parent - 1) / Disk->SuperBlock.s_inodes_per_group; - Log_Warning("EXT2", "Ext2_int_AllocateInode is unimplemented"); - return 0; -} - -/** - * \fn void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk) - * \brief Updates the superblock - */ -void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk) -{ - int bpg = Disk->SuperBlock.s_blocks_per_group; - int ngrp = Disk->SuperBlock.s_blocks_count / bpg; - int i; - - // Update Primary - VFS_WriteAt(Disk->FD, 1024, 1024, &Disk->SuperBlock); - - // Secondaries - // at Block Group 1, 3^n, 5^n, 7^n - - // 1 - if(ngrp <= 1) return; - VFS_WriteAt(Disk->FD, 1*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock); - - #define INT_MAX (((long long int)1<<(sizeof(int)*8))-1) - - // Powers of 3 - for( i = 3; i < ngrp && i < INT_MAX/3; i *= 3 ) - VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock); - - // Powers of 5 - for( i = 5; i < ngrp && i < INT_MAX/5; i *= 5 ) - VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock); - - // Powers of 7 - for( i = 7; i < ngrp && i < INT_MAX/7; i *= 7 ) - VFS_WriteAt(Disk->FD, i*bpg*Disk->BlockSize, 1024, &Disk->SuperBlock); -} diff --git a/Modules/Filesystems/Ext2/ext2_common.h b/Modules/Filesystems/Ext2/ext2_common.h deleted file mode 100644 index 6ef9d7c8..00000000 --- a/Modules/Filesystems/Ext2/ext2_common.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Acess OS - * Ext2 Driver Version 1 - */ -/** - * \file ext2_common.h - * \brief Second Extended Filesystem Driver - */ -#ifndef _EXT2_COMMON_H -#define _EXT2_COMMON_H -#include -#include -#include "ext2fs.h" - -#define EXT2_UPDATE_WRITEBACK 1 - -// === STRUCTURES === -typedef struct { - int FD; - int CacheID; - tVFS_Node RootNode; - - tExt2_SuperBlock SuperBlock; - Uint BlockSize; - - int GroupCount; - tExt2_Group Groups[]; -} tExt2_Disk; - -// === FUNCTIONS === -// --- Common --- -extern void Ext2_CloseFile(tVFS_Node *Node); -extern Uint64 Ext2_int_GetBlockAddr(tExt2_Disk *Disk, Uint32 *Blocks, int BlockNum); -extern void Ext2_int_UpdateSuperblock(tExt2_Disk *Disk); -extern int Ext2_int_ReadInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode); -extern int Ext2_int_WriteInode(tExt2_Disk *Disk, Uint32 InodeId, tExt2_Inode *Inode); -// --- Dir --- -extern char *Ext2_ReadDir(tVFS_Node *Node, int Pos); -extern tVFS_Node *Ext2_FindDir(tVFS_Node *Node, const char *FileName); -extern int Ext2_MkNod(tVFS_Node *Node, const char *Name, Uint Flags); -extern int Ext2_Link(tVFS_Node *Parent, tVFS_Node *Node, const char *Name); -// --- Read --- -extern Uint64 Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer); -// --- Write --- -extern Uint64 Ext2_Write(tVFS_Node *node, Uint64 offset, Uint64 length, const void *buffer); - -#endif diff --git a/Modules/Filesystems/Ext2/ext2fs.h b/Modules/Filesystems/Ext2/ext2fs.h deleted file mode 100644 index a3aa301e..00000000 --- a/Modules/Filesystems/Ext2/ext2fs.h +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Acess2 - * \file ext2fs.h - * \brief EXT2 Filesystem Driver - */ -#ifndef _EXT2FS_H_ -#define _EXT2FS_H_ - -/** - \name Inode Flag Values - \{ -*/ -#define EXT2_S_IFMT 0xF000 //!< Format Mask -#define EXT2_S_IFSOCK 0xC000 //!< Socket -#define EXT2_S_IFLNK 0xA000 //!< Symbolic Link -#define EXT2_S_IFREG 0x8000 //!< Regular File -#define EXT2_S_IFBLK 0x6000 //!< Block Device -#define EXT2_S_IFDIR 0x4000 //!< Directory -#define EXT2_S_IFCHR 0x2000 //!< Character Device -#define EXT2_S_IFIFO 0x1000 //!< FIFO -#define EXT2_S_ISUID 0x0800 //!< SUID -#define EXT2_S_ISGID 0x0400 //!< SGID -#define EXT2_S_ISVTX 0x0200 //!< sticky bit -#define EXT2_S_IRWXU 0700 //!< user access rights mask -#define EXT2_S_IRUSR 0400 //!< Owner Read -#define EXT2_S_IWUSR 0200 //!< Owner Write -#define EXT2_S_IXUSR 0100 //!< Owner Execute -#define EXT2_S_IRWXG 0070 //!< Group Access rights mask -#define EXT2_S_IRGRP 0040 //!< Group Read -#define EXT2_S_IWGRP 0020 //!< Group Write -#define EXT2_S_IXGRP 0010 //!< Group Execute -#define EXT2_S_IRWXO 0007 //!< Global Access rights mask -#define EXT2_S_IROTH 0004 //!< Global Read -#define EXT2_S_IWOTH 0002 //!< Global Write -#define EXT2_S_IXOTH 0001 //!< Global Execute -//! \} - -#define EXT2_NAME_LEN 255 //!< Maximum Name Length - -// === TYPEDEFS === -typedef struct ext2_inode_s tExt2_Inode; //!< Inode Type -typedef struct ext2_super_block_s tExt2_SuperBlock; //!< Superblock Type -typedef struct ext2_group_desc_s tExt2_Group; //!< Group Descriptor Type -typedef struct ext2_dir_entry_s tExt2_DirEnt; //!< Directory Entry Type - -// === STRUCTURES === -/** - * \brief EXT2 Superblock Structure - */ -struct ext2_super_block_s { - Uint32 s_inodes_count; //!< Inodes count - Uint32 s_blocks_count; //!< Blocks count - Uint32 s_r_blocks_count; //!< Reserved blocks count - Uint32 s_free_blocks_count; //!< Free blocks count - - Uint32 s_free_inodes_count; //!< Free inodes count - Uint32 s_first_data_block; //!< First Data Block - Uint32 s_log_block_size; //!< Block size - Sint32 s_log_frag_size; //!< Fragment size - - Uint32 s_blocks_per_group; //!< Number Blocks per group - Uint32 s_frags_per_group; //!< Number Fragments per group - Uint32 s_inodes_per_group; //!< Number Inodes per group - Uint32 s_mtime; //!< Mount time - - Uint32 s_wtime; //!< Write time - Uint16 s_mnt_count; //!< Mount count - Sint16 s_max_mnt_count; //!< Maximal mount count - Uint16 s_magic; //!< Magic signature - Uint16 s_state; //!< File system state - Uint16 s_errors; //!< Behaviour when detecting errors - Uint16 s_pad; //!< Padding - - Uint32 s_lastcheck; //!< time of last check - Uint32 s_checkinterval; //!< max. time between checks - Uint32 s_creator_os; //!< Formatting OS - Uint32 s_rev_level; //!< Revision level - - Uint16 s_def_resuid; //!< Default uid for reserved blocks - Uint16 s_def_resgid; //!< Default gid for reserved blocks - Uint32 s_reserved[235]; //!< Padding to the end of the block -}; - -/** - * \struct ext2_inode_s - * \brief EXT2 Inode Definition - */ -struct ext2_inode_s { - Uint16 i_mode; //!< File mode - Uint16 i_uid; //!< Owner Uid - Uint32 i_size; //!< Size in bytes - Uint32 i_atime; //!< Access time - Uint32 i_ctime; //!< Creation time - Uint32 i_mtime; //!< Modification time - Uint32 i_dtime; //!< Deletion Time - Uint16 i_gid; //!< Group Id - Uint16 i_links_count; //!< Links count - Uint32 i_blocks; //!< Number of blocks allocated for the file - Uint32 i_flags; //!< File flags - union { - Uint32 linux_reserved1; //!< Linux: Reserved - Uint32 hurd_translator; //!< HURD: Translator - Uint32 masix_reserved1; //!< Masix: Reserved - } osd1; //!< OS dependent 1 - Uint32 i_block[15]; //!< Pointers to blocks - Uint32 i_version; //!< File version (for NFS) - Uint32 i_file_acl; //!< File ACL - Uint32 i_dir_acl; //!< Directory ACL / Extended File Size - Uint32 i_faddr; //!< Fragment address - union { - struct { - Uint8 l_i_frag; //!< Fragment number - Uint8 l_i_fsize; //!< Fragment size - Uint16 i_pad1; //!< Padding - Uint32 l_i_reserved2[2]; //!< Reserved - } linux2; - struct { - Uint8 h_i_frag; //!< Fragment number - Uint8 h_i_fsize; //!< Fragment size - Uint16 h_i_mode_high; //!< Mode High Bits - Uint16 h_i_uid_high; //!< UID High Bits - Uint16 h_i_gid_high; //!< GID High Bits - Uint32 h_i_author; //!< Creator ID - } hurd2; - struct { - Uint8 m_i_frag; //!< Fragment number - Uint8 m_i_fsize; //!< Fragment size - Uint16 m_pad1; //!< Padding - Uint32 m_i_reserved2[2]; //!< reserved - } masix2; - } osd2; //!< OS dependent 2 -}; - -/** - * \struct ext2_group_desc_s - * \brief EXT2 Group Descriptor - */ -struct ext2_group_desc_s { - Uint32 bg_block_bitmap; //!< Blocks bitmap block - Uint32 bg_inode_bitmap; //!< Inodes bitmap block - Uint32 bg_inode_table; //!< Inodes table block - Uint16 bg_free_blocks_count; //!< Free blocks count - Uint16 bg_free_inodes_count; //!< Free inodes count - Uint16 bg_used_dirs_count; //!< Directories count - Uint16 bg_pad; //!< Padding - Uint32 bg_reserved[3]; //!< Reserved -}; - -/** - * \brief EXT2 Directory Entry - * \note The name may take up less than 255 characters - */ -struct ext2_dir_entry_s { - Uint32 inode; //!< Inode number - Uint16 rec_len; //!< Directory entry length - Uint8 name_len; //!< Short Name Length - Uint8 type; //!< File Type (Duplicate of ext2_inode_s.i_mode) - char name[EXT2_NAME_LEN+1]; //!< File name -}; -#define EXT2_DIRENT_SIZE (sizeof(struct ext2_dir_entry_s)-EXT2_NAME_LEN+1) - -#endif diff --git a/Modules/Filesystems/Ext2/read.c b/Modules/Filesystems/Ext2/read.c deleted file mode 100644 index 81f5ee64..00000000 --- a/Modules/Filesystems/Ext2/read.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Acess OS - * Ext2 Driver Version 1 - */ -/** - * \file read.c - * \brief Second Extended Filesystem Driver - * \todo Implement file full write support - */ -#define DEBUG 1 -#define VERBOSE 0 -#include "ext2_common.h" - -// === PROTOTYPES === -Uint64 Ext2_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer); - -// === CODE === -/** - * \fn Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Read from a file - */ -Uint64 Ext2_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - tExt2_Disk *disk = Node->ImplPtr; - tExt2_Inode inode; - Uint64 base; - Uint block; - Uint64 remLen; - - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - - // Get Inode - Ext2_int_ReadInode(disk, Node->Inode, &inode); - - // Sanity Checks - if(Offset >= inode.i_size) { - LEAVE('i', 0); - return 0; - } - if(Offset + Length > inode.i_size) - Length = inode.i_size - Offset; - - block = Offset / disk->BlockSize; - Offset = Offset / disk->BlockSize; - base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); - if(base == 0) { - Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode); - LEAVE('i', 0); - return 0; - } - - // Read only block - if(Length <= disk->BlockSize - Offset) - { - VFS_ReadAt( disk->FD, base+Offset, Length, Buffer); - LEAVE('X', Length); - return Length; - } - - // Read first block - remLen = Length; - VFS_ReadAt( disk->FD, base + Offset, disk->BlockSize - Offset, Buffer); - remLen -= disk->BlockSize - Offset; - Buffer += disk->BlockSize - Offset; - block ++; - - // Read middle blocks - while(remLen > disk->BlockSize) - { - base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); - if(base == 0) { - Warning("[EXT2 ] NULL Block Detected in INode 0x%llx", Node->Inode); - LEAVE('i', 0); - return 0; - } - VFS_ReadAt( disk->FD, base, disk->BlockSize, Buffer); - Buffer += disk->BlockSize; - remLen -= disk->BlockSize; - block ++; - } - - // Read last block - base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); - VFS_ReadAt( disk->FD, base, remLen, Buffer); - - LEAVE('X', Length); - return Length; -} diff --git a/Modules/Filesystems/Ext2/write.c b/Modules/Filesystems/Ext2/write.c deleted file mode 100644 index 03109e8a..00000000 --- a/Modules/Filesystems/Ext2/write.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Acess OS - * Ext2 Driver Version 1 - */ -/** - * \file write.c - * \brief Second Extended Filesystem Driver - * \todo Implement file full write support - */ -#define DEBUG 1 -#define VERBOSE 0 -#include "ext2_common.h" - -// === PROTOYPES === -Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock); -void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block); - int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block); - -// === CODE === -/** - * \fn Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Write to a file - */ -Uint64 Ext2_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - tExt2_Disk *disk = Node->ImplPtr; - tExt2_Inode inode; - Uint64 base; - Uint64 retLen; - Uint block; - Uint64 allocSize; - int bNewBlocks = 0; - - Debug_HexDump("Ext2_Write", Buffer, Length); - - Ext2_int_ReadInode(disk, Node->Inode, &inode); - - // Get the ammount of space already allocated - // - Round size up to block size - // - block size is a power of two, so this will work - allocSize = (inode.i_size + disk->BlockSize-1) & ~(disk->BlockSize-1); - - // Are we writing to inside the allocated space? - if( Offset > allocSize ) return 0; - - if( Offset < allocSize ) - { - // Will we go out of it? - if(Offset + Length > allocSize) { - bNewBlocks = 1; - retLen = allocSize - Offset; - } else - retLen = Length; - - // Within the allocated space - block = Offset / disk->BlockSize; - Offset %= disk->BlockSize; - base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); - - // Write only block (if only one) - if(Offset + retLen <= disk->BlockSize) { - VFS_WriteAt(disk->FD, base+Offset, retLen, Buffer); - if(!bNewBlocks) return Length; - goto addBlocks; // Ugh! A goto, but it seems unavoidable - } - - // Write First Block - VFS_WriteAt(disk->FD, base+Offset, disk->BlockSize-Offset, Buffer); - Buffer += disk->BlockSize-Offset; - retLen -= disk->BlockSize-Offset; - block ++; - - // Write middle blocks - while(retLen > disk->BlockSize) - { - base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); - VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer); - Buffer += disk->BlockSize; - retLen -= disk->BlockSize; - block ++; - } - - // Write last block - base = Ext2_int_GetBlockAddr(disk, inode.i_block, block); - VFS_WriteAt(disk->FD, base, retLen, Buffer); - if(!bNewBlocks) return Length; // Writing in only allocated space - } - else - base = Ext2_int_GetBlockAddr(disk, inode.i_block, allocSize/disk->BlockSize-1); - -addBlocks: - Log_Notice("EXT2", "File extending is untested"); - - // Allocate blocks and copy data to them - retLen = Length - (allocSize-Offset); - while( retLen > disk->BlockSize ) - { - // Allocate a block - block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize); - if(!block) return Length - retLen; - // Add it to this inode - if( !Ext2_int_AppendBlock(disk, &inode, block) ) { - Ext2_int_DeallocateBlock(disk, block); - goto ret; - } - // Copy data to the node - base = block * disk->BlockSize; - VFS_WriteAt(disk->FD, base, disk->BlockSize, Buffer); - // Update pointer and size remaining - inode.i_size += disk->BlockSize; - Buffer += disk->BlockSize; - retLen -= disk->BlockSize; - } - // Last block :D - block = Ext2_int_AllocateBlock(disk, base/disk->BlockSize); - if(!block) goto ret; - if( !Ext2_int_AppendBlock(disk, &inode, block) ) { - Ext2_int_DeallocateBlock(disk, block); - goto ret; - } - base = block * disk->BlockSize; - VFS_WriteAt(disk->FD, base, retLen, Buffer); - inode.i_size += retLen; - retLen = 0; - -ret: // Makes sure the changes to the inode are committed - Ext2_int_WriteInode(disk, Node->Inode, &inode); - return Length - retLen; -} - -/** - * \fn Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock) - * \brief Allocate a block from the best possible location - * \param Disk EXT2 Disk Information Structure - * \param PrevBlock Previous block ID in the file - */ -Uint32 Ext2_int_AllocateBlock(tExt2_Disk *Disk, Uint32 PrevBlock) -{ - int bpg = Disk->SuperBlock.s_blocks_per_group; - Uint blockgroup = PrevBlock / bpg; - Uint bitmap[Disk->BlockSize/sizeof(Uint)]; - Uint bitsperblock = 8*Disk->BlockSize; - int i, j = 0; - Uint block; - - // Are there any free blocks? - if(Disk->SuperBlock.s_free_blocks_count == 0) return 0; - - if(Disk->Groups[blockgroup].bg_free_blocks_count > 0) - { - // Search block group's bitmap - for(i = 0; i < bpg; i++) - { - // Get the block in the bitmap block - j = i & (bitsperblock-1); - - // Read in if needed - if(j == 0) { - VFS_ReadAt( - Disk->FD, - (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock, - Disk->BlockSize, - bitmap - ); - } - - // Fast Check - if( bitmap[j/32] == 0xFFFFFFFF ) { - j = (j + 31) & ~31; - continue; - } - - // Is the bit set? - if( bitmap[j/32] & (1 << (j%32)) ) - continue; - - // Ooh! We found one - break; - } - if( i < bpg ) { - Warning("[EXT2 ] Inconsistency detected, Group Free Block count is non-zero when no free blocks exist"); - goto checkAll; // Search the entire filesystem for a free block - // Goto needed for neatness - } - - // Mark as used - bitmap[j/32] |= (1 << (j%32)); - VFS_WriteAt( - Disk->FD, - (Uint64)Disk->Groups[blockgroup].bg_block_bitmap + i / bitsperblock, - Disk->BlockSize, - bitmap - ); - block = i; - Disk->Groups[blockgroup].bg_free_blocks_count --; - #if EXT2_UPDATE_WRITEBACK - //Ext2_int_UpdateBlockGroup(Disk, blockgroup); - #endif - } - else - { - checkAll: - Log_Warning("EXT2", "TODO - Implement using blocks outside the current block group"); - return 0; - } - - // Reduce global count - Disk->SuperBlock.s_free_blocks_count --; - #if EXT2_UPDATE_WRITEBACK - Ext2_int_UpdateSuperblock(Disk); - #endif - - return block; -} - -/** - * \brief Deallocates a block - */ -void Ext2_int_DeallocateBlock(tExt2_Disk *Disk, Uint32 Block) -{ -} - -/** - * \brief Append a block to an inode - */ -int Ext2_int_AppendBlock(tExt2_Disk *Disk, tExt2_Inode *Inode, Uint32 Block) -{ - int nBlocks; - int dwPerBlock = Disk->BlockSize / 4; - Uint32 *blocks; - Uint32 id1, id2; - - nBlocks = (Inode->i_size + Disk->BlockSize - 1) / Disk->BlockSize; - - // Direct Blocks - if( nBlocks < 12 ) { - Inode->i_block[nBlocks] = Block; - return 0; - } - - blocks = malloc( Disk->BlockSize ); - if(!blocks) return 1; - - nBlocks -= 12; - // Single Indirect - if( nBlocks < dwPerBlock) - { - // Allocate/Get Indirect block - if( nBlocks == 0 ) { - Inode->i_block[12] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]); - if( !Inode->i_block[12] ) { - free(blocks); - return 1; - } - memset(blocks, 0, Disk->BlockSize); - } - else - VFS_ReadAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks); - - blocks[nBlocks] = Block; - - VFS_WriteAt(Disk->FD, Inode->i_block[12]*Disk->BlockSize, Disk->BlockSize, blocks); - free(blocks); - return 0; - } - - nBlocks += dwPerBlock; - // Double Indirect - if( nBlocks < dwPerBlock*dwPerBlock ) - { - // Allocate/Get Indirect block - if( nBlocks == 0 ) { - Inode->i_block[13] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]); - if( !Inode->i_block[13] ) { - free(blocks); - return 1; - } - memset(blocks, 0, Disk->BlockSize); - } - else - VFS_ReadAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks); - - // Allocate / Get Indirect lvl2 Block - if( nBlocks % dwPerBlock == 0 ) { - id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]); - if( !id1 ) { - free(blocks); - return 1; - } - blocks[nBlocks/dwPerBlock] = id1; - // Write back indirect 1 block - VFS_WriteAt(Disk->FD, Inode->i_block[13]*Disk->BlockSize, Disk->BlockSize, blocks); - memset(blocks, 0, Disk->BlockSize); - } - else { - id1 = blocks[nBlocks / dwPerBlock]; - VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks); - } - - blocks[nBlocks % dwPerBlock] = Block; - - VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks); - free(blocks); - return 0; - } - - nBlocks -= dwPerBlock*dwPerBlock; - // Triple Indirect - if( nBlocks < dwPerBlock*dwPerBlock*dwPerBlock ) - { - // Allocate/Get Indirect block - if( nBlocks == 0 ) { - Inode->i_block[14] = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]); - if( !Inode->i_block[14] ) { - free(blocks); - return 1; - } - memset(blocks, 0, Disk->BlockSize); - } - else - VFS_ReadAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks); - - // Allocate / Get Indirect lvl2 Block - if( (nBlocks/dwPerBlock) % dwPerBlock == 0 && nBlocks % dwPerBlock == 0 ) - { - id1 = Ext2_int_AllocateBlock(Disk, Inode->i_block[0]); - if( !id1 ) { - free(blocks); - return 1; - } - blocks[nBlocks/dwPerBlock] = id1; - // Write back indirect 1 block - VFS_WriteAt(Disk->FD, Inode->i_block[14]*Disk->BlockSize, Disk->BlockSize, blocks); - memset(blocks, 0, Disk->BlockSize); - } - else { - id1 = blocks[nBlocks / (dwPerBlock*dwPerBlock)]; - VFS_ReadAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks); - } - - // Allocate / Get Indirect Level 3 Block - if( nBlocks % dwPerBlock == 0 ) { - id2 = Ext2_int_AllocateBlock(Disk, id1); - if( !id2 ) { - free(blocks); - return 1; - } - blocks[(nBlocks/dwPerBlock)%dwPerBlock] = id2; - // Write back indirect 1 block - VFS_WriteAt(Disk->FD, id1*Disk->BlockSize, Disk->BlockSize, blocks); - memset(blocks, 0, Disk->BlockSize); - } - else { - id2 = blocks[(nBlocks/dwPerBlock)%dwPerBlock]; - VFS_ReadAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks); - } - - blocks[nBlocks % dwPerBlock] = Block; - - VFS_WriteAt(Disk->FD, id2*Disk->BlockSize, Disk->BlockSize, blocks); - free(blocks); - return 0; - } - - Warning("[EXT2 ] Inode %i cannot have a block appended to it, all indirects used"); - free(blocks); - return 1; -} diff --git a/Modules/Filesystems/FAT/Makefile b/Modules/Filesystems/FAT/Makefile deleted file mode 100644 index dfa290b8..00000000 --- a/Modules/Filesystems/FAT/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = fat.o -NAME = FAT - --include ../Makefile.tpl diff --git a/Modules/Filesystems/FAT/fat.c b/Modules/Filesystems/FAT/fat.c deleted file mode 100644 index f5d19161..00000000 --- a/Modules/Filesystems/FAT/fat.c +++ /dev/null @@ -1,1538 +0,0 @@ -/* - * Acess 2 - * FAT12/16/32 Driver Version (Incl LFN) - * - * NOTE: This driver will only support _reading_ long file names, not - * writing. I don't even know why I'm adding write-support. FAT sucks. - * - * Known Bugs: - * - LFN Is buggy in FAT_ReadDir - * - * Notes: - * - There's hard-coded 512 byte sectors everywhere, that needs to be - * cleaned. - * - Thread safety is out the window with the write and LFN code - */ -/** - * \todo Implement changing of the parent directory when a file is written to - * \todo Implement file creation / deletion - */ -#define DEBUG 0 -#define VERBOSE 1 - -#define CACHE_FAT 0 //!< Caches the FAT in memory -#define USE_LFN 1 //!< Enables the use of Long File Names -#define SUPPORT_WRITE 0 //!< Enables write support - -#include -#include -#include -#include "fs_fat.h" - -#define FAT_FLAG_DIRTY 0x10000 -#define FAT_FLAG_DELETE 0x20000 - -// === TYPES === -#if USE_LFN -/** - * \brief Long-Filename cache entry - */ -typedef struct sFAT_LFNCacheEnt -{ - int ID; - // TODO: Handle UTF16 names correctly - char Data[256]; -} tFAT_LFNCacheEnt; -/** - * \brief Long-Filename cache - */ -typedef struct sFAT_LFNCache -{ - int NumEntries; - tFAT_LFNCacheEnt Entries[]; -} tFAT_LFNCache; -#endif - -// === PROTOTYPES === -// --- Driver Core - int FAT_Install(char **Arguments); -tVFS_Node *FAT_InitDevice(const char *device, const char **options); -void FAT_Unmount(tVFS_Node *Node); -// --- Helpers - int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster); -Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 Cluster); -#if SUPPORT_WRITE -Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous); -Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster); -#endif -void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer); -// --- File IO -Uint64 FAT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -#if SUPPORT_WRITE -void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer); -Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -#endif -// --- Directory IO -char *FAT_ReadDir(tVFS_Node *Node, int ID); -tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name); -tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode); -#if SUPPORT_WRITE - int FAT_Mknod(tVFS_Node *Node, const char *Name, Uint Flags); - int FAT_Relink(tVFS_Node *node, const char *OldName, const char *NewName); -#endif -void FAT_CloseFile(tVFS_Node *node); - -// === Options === - int giFAT_MaxCachedClusters = 1024*512/4; - -// === SEMI-GLOBALS === -MODULE_DEFINE(0, (0<<8)|50 /*v0.50*/, VFAT, FAT_Install, NULL, NULL); -tFAT_VolInfo gFAT_Disks[8]; - int giFAT_PartCount = 0; -tVFS_Driver gFAT_FSInfo = {"fat", 0, FAT_InitDevice, FAT_Unmount, FAT_GetNodeFromINode, NULL}; -tVFS_NodeType gFAT_DirType = { - .TypeName = "FAT-Dir", - .ReadDir = FAT_ReadDir, - .FindDir = FAT_FindDir, - #if SUPPORT_WRITE - .MkNod = FAT_Mknod, - .Relink = FAT_Relink, - #endif - .Close = FAT_CloseFile - }; -tVFS_NodeType gFAT_FileType = { - .TypeName = "FAT-File", - .Read = FAT_Read, - #if SUPPORT_WRITE - .Write = FAT_Write, - #endif - .Close = FAT_CloseFile - }; - -// === CODE === -/** - * \fn int FAT_Install(char **Arguments) - * \brief Install the FAT Driver - */ -int FAT_Install(char **Arguments) -{ - VFS_AddDriver( &gFAT_FSInfo ); - return MODULE_ERR_OK; -} - -/** - * \brief Reads the boot sector of a disk and prepares the structures for it - */ -tVFS_Node *FAT_InitDevice(const char *Device, const char **Options) -{ - fat_bootsect *bs; - int i; - Uint32 FATSz, RootDirSectors, TotSec; - tVFS_Node *node = NULL; - tFAT_VolInfo *diskInfo = &gFAT_Disks[giFAT_PartCount]; - - // Temporary Pointer - bs = &diskInfo->bootsect; - - // Open device and read boot sector - diskInfo->fileHandle = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE); - if(diskInfo->fileHandle == -1) { - Log_Notice("FAT", "Unable to open device '%s'", Device); - return NULL; - } - - VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs); - - if(bs->bps == 0 || bs->spc == 0) { - Log_Notice("FAT", "Error in FAT Boot Sector"); - return NULL; - } - - // FAT Type Determining - // - From Microsoft FAT Specifcation - RootDirSectors = ((bs->files_in_root*32) + (bs->bps - 1)) / bs->bps; - - if(bs->fatSz16 != 0) - FATSz = bs->fatSz16; - else - FATSz = bs->spec.fat32.fatSz32; - - if(bs->totalSect16 != 0) - TotSec = bs->totalSect16; - else - TotSec = bs->totalSect32; - - diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc; - - if(diskInfo->ClusterCount < 4085) - diskInfo->type = FAT12; - else if(diskInfo->ClusterCount < 65525) - diskInfo->type = FAT16; - else - diskInfo->type = FAT32; - - #if VERBOSE - { - char *sFatType, *sSize; - Uint iSize = diskInfo->ClusterCount * bs->spc * bs->bps / 1024; - - switch(diskInfo->type) - { - case FAT12: sFatType = "FAT12"; break; - case FAT16: sFatType = "FAT16"; break; - case FAT32: sFatType = "FAT32"; break; - default: sFatType = "UNKNOWN"; break; - } - if(iSize <= 2*1024) { - sSize = "KiB"; - } - else if(iSize <= 2*1024*1024) { - sSize = "MiB"; - iSize >>= 10; - } - else { - sSize = "GiB"; - iSize >>= 20; - } - Log_Notice("FAT", "'%s' %s, %i %s", Device, sFatType, iSize, sSize); - } - #endif - - // Get Name - if(diskInfo->type == FAT32) { - for(i=0;i<11;i++) - diskInfo->name[i] = (bs->spec.fat32.label[i] == ' ' ? '\0' : bs->spec.fat32.label[i]); - } - else { - for(i=0;i<11;i++) - diskInfo->name[i] = (bs->spec.fat16.label[i] == ' ' ? '\0' : bs->spec.fat16.label[i]); - } - diskInfo->name[11] = '\0'; - - // Compute Root directory offset - if(diskInfo->type == FAT32) - diskInfo->rootOffset = bs->spec.fat32.rootClust; - else - diskInfo->rootOffset = (FATSz * bs->fatCount) / bs->spc; - - diskInfo->firstDataSect = bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors; - - //Allow for Caching the FAT - #if CACHE_FAT - if( diskInfo->ClusterCount <= giFAT_MaxCachedClusters ) - { - Uint32 Ofs; - diskInfo->FATCache = (Uint32*)malloc(sizeof(Uint32)*diskInfo->ClusterCount); - if(diskInfo->FATCache == NULL) { - Log_Warning("FAT", "Heap Exhausted"); - return NULL; - } - Ofs = bs->resvSectCount*512; - if(diskInfo->type == FAT12) - { - Uint32 val; - int j; - char buf[1536]; - for(i = 0; i < diskInfo->ClusterCount/2; i++) { - j = i & 511; //%512 - if( j == 0 ) { - VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf); - Ofs += 3*512; - } - val = *((int*)(buf+j*3)); - diskInfo->FATCache[i*2] = val & 0xFFF; - diskInfo->FATCache[i*2+1] = (val>>12) & 0xFFF; - } - } - else if(diskInfo->type == FAT16) - { - Uint16 buf[256]; - for(i=0;iClusterCount;i++) { - if( (i & 255) == 0 ) { - VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf); - Ofs += 512; - } - diskInfo->FATCache[i] = buf[i&255]; - } - } - else if(diskInfo->type == FAT32) - { - Uint32 buf[128]; - for(i=0;iClusterCount;i++) { - if( (i & 127) == 0 ) { - VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf); - Ofs += 512; - } - diskInfo->FATCache[i] = buf[i&127]; - } - } - LOG("FAT Fully Cached"); - } - #endif /*CACHE_FAT*/ - - diskInfo->BytesPerCluster = bs->spc * bs->bps; - - // Initalise inode cache for filesystem - diskInfo->inodeHandle = Inode_GetHandle(); - LOG("Inode Cache handle is %i", diskInfo->inodeHandle); - - // == VFS Interface - node = &diskInfo->rootNode; - //node->Size = bs->files_in_root; - node->Size = -1; - node->Inode = diskInfo->rootOffset; // 0:31 - Cluster, 32:63 - Parent Directory Cluster - node->ImplPtr = diskInfo; // Disk info pointer - node->ImplInt = 0; // 0:15 - Directory Index, 16: Dirty Flag, 17: Deletion Flag - - node->ReferenceCount = 1; - - node->UID = 0; node->GID = 0; - node->NumACLs = 1; - node->ACLs = &gVFS_ACL_EveryoneRWX; - node->Flags = VFS_FFLAG_DIRECTORY; - node->CTime = node->MTime = node->ATime = now(); - - node->Type = &gFAT_DirType; - - giFAT_PartCount ++; - return node; -} - -/** - * \brief Closes a mount and marks it as free - * \param Node Mount Root - * - * \todo Remove FAT Cache - * \todo Clear LFN Cache - * \todo Check that all files are closed and flushed - */ -void FAT_Unmount(tVFS_Node *Node) -{ - tFAT_VolInfo *disk = Node->ImplPtr; - - // Close Disk Handle - VFS_Close( disk->fileHandle ); - // Clear Node Cache - Inode_ClearCache(disk->inodeHandle); - // Mark as unused - disk->fileHandle = -2; - return; -} - -/** - * \brief Converts an offset in a file into a disk address - * \param Node File (or directory) node - * \param Offset Offset in the file - * \param Addr Return Address - * \param Cluster Set to the current cluster (or the last one if \a Offset - * is past EOC) - Not touched if the node is the root - * directory. - * \return Zero on success, non-zero on error - */ -int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster) -{ - Uint32 cluster; - Uint64 addr; - int skip; - tFAT_VolInfo *disk = Node->ImplPtr; - - ENTER("pNode XOffset", Node, Offset); - - cluster = Node->Inode & 0xFFFFFFF; // Cluster ID - LOG("cluster = 0x%07x", cluster); - - // Do Cluster Skip - // - Pre FAT32 had a reserved area for the root. - if( disk->type == FAT32 || cluster != disk->rootOffset ) - { - skip = Offset / disk->BytesPerCluster; - LOG("skip = %i", skip); - // Skip previous clusters - for(; skip-- ; ) - { - if(Cluster) *Cluster = cluster; - cluster = FAT_int_GetFatValue(disk, cluster); - // Check for end of cluster chain - if(cluster == 0xFFFFFFFF) { LEAVE('i', 1); return 1;} - } - if(Cluster) *Cluster = cluster; - } - else { - // Increment by clusters in offset - cluster += Offset / disk->BytesPerCluster; - } - - LOG("cluster = %08x", cluster); - - // Bounds Checking (Used to spot corruption) - if(cluster > disk->ClusterCount + 2) - { - Log_Warning("FAT", "Cluster ID is over cluster count (0x%x>0x%x)", - cluster, disk->ClusterCount+2); - LEAVE('i', 1); - return 1; - } - - // Compute Offsets - // - Pre FAT32 cluster base (in sectors) - if( cluster == disk->rootOffset && disk->type != FAT32 ) { - addr = disk->bootsect.resvSectCount * disk->bootsect.bps; - addr += cluster * disk->BytesPerCluster; - } - else { - addr = disk->firstDataSect * disk->bootsect.bps; - addr += (cluster - 2) * disk->BytesPerCluster; - } - // In-cluster offset - addr += Offset % disk->BytesPerCluster; - - LOG("addr = 0x%08x", addr); - *Addr = addr; - LEAVE('i', 0); - return 0; -} - -/* - * ==================== - * FAT Manipulation - * ==================== - */ -/** - * \fn Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster) - * \brief Fetches a value from the FAT - */ -Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster) -{ - Uint32 val = 0; - Uint32 ofs; - ENTER("pDisk xCluster", Disk, cluster); - Mutex_Acquire( &Disk->lFAT ); - #if CACHE_FAT - if( Disk->ClusterCount <= giFAT_MaxCachedClusters ) - { - val = Disk->FATCache[cluster]; - if(Disk->type == FAT12 && val == EOC_FAT12) val = -1; - if(Disk->type == FAT16 && val == EOC_FAT16) val = -1; - if(Disk->type == FAT32 && val == EOC_FAT32) val = -1; - } - else - { - #endif - ofs = Disk->bootsect.resvSectCount*512; - if(Disk->type == FAT12) { - VFS_ReadAt(Disk->fileHandle, ofs+(cluster/2)*3, 3, &val); - val = (cluster & 1 ? val>>12 : val & 0xFFF); - if(val == EOC_FAT12) val = -1; - } else if(Disk->type == FAT16) { - VFS_ReadAt(Disk->fileHandle, ofs+cluster*2, 2, &val); - if(val == EOC_FAT16) val = -1; - } else { - VFS_ReadAt(Disk->fileHandle, ofs+cluster*4, 4, &val); - if(val == EOC_FAT32) val = -1; - } - #if CACHE_FAT - } - #endif /*CACHE_FAT*/ - Mutex_Release( &Disk->lFAT ); - LEAVE('x', val); - return val; -} - -#if SUPPORT_WRITE -/** - * \brief Allocate a new cluster - */ -Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous) -{ - Uint32 ret = Previous; - #if CACHE_FAT - if( Disk->ClusterCount <= giFAT_MaxCachedClusters ) - { - Uint32 eoc; - - LOCK(Disk->lFAT); - for(ret = Previous; ret < Disk->ClusterCount; ret++) - { - if(Disk->FATCache[ret] == 0) - goto append; - } - for(ret = 0; ret < Previous; ret++) - { - if(Disk->FATCache[ret] == 0) - goto append; - } - - RELEASE(Disk->lFAT); - return 0; - - append: - switch(Disk->type) - { - case FAT12: eoc = EOC_FAT12; break; - case FAT16: eoc = EOC_FAT16; break; - case FAT32: eoc = EOC_FAT32; break; - default: return 0; - } - - Disk->FATCache[ret] = eoc; - Disk->FATCache[Previous] = ret; - - RELEASE(Disk->lFAT); - return ret; - } - else - { - #endif - Uint32 val; - Uint32 ofs = Disk->bootsect.resvSectCount*512; - Log_Warning("FAT", "TODO: Implement cluster allocation with non cached FAT"); - return 0; - - switch(Disk->type) - { - case FAT12: - VFS_ReadAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val); - if( Previous & 1 ) { - val &= 0xFFF000; - val |= ret; - } - else { - val &= 0xFFF; - val |= ret<<12; - } - VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val); - - VFS_ReadAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val); - if( Cluster & 1 ) { - val &= 0xFFF000; - val |= eoc; - } - else { - val &= 0x000FFF; - val |= eoc<<12; - } - VFS_WriteAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val); - break; - case FAT16: - VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret); - VFS_WriteAt(Disk->fileHandle, ofs+ret*2, 2, &eoc); - break; - case FAT32: - VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret); - VFS_WriteAt(Disk->fileHandle, ofs+ret*4, 4, &eoc); - break; - } - return ret; - #if CACHE_FAT - } - #endif -} - -/** - * \brief Free's a cluster - * \return The original contents of the cluster - */ -Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster) -{ - Uint32 ret; - #if CACHE_FAT - if( Disk->ClusterCount <= giFAT_MaxCachedClusters ) - { - LOCK(Disk->lFAT); - - ret = Disk->FATCache[Cluster]; - Disk->FATCache[Cluster] = 0; - - RELEASE(Disk->lFAT); - } - else - { - #endif - Uint32 val; - Uint32 ofs = Disk->bootsect.resvSectCount*512; - LOCK(Disk->lFAT); - switch(Disk->type) - { - case FAT12: - VFS_ReadAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val); - if( Cluster & 1 ) { - ret = val & 0xFFF0000; - val &= 0xFFF; - } - else { - ret = val & 0xFFF; - val &= 0xFFF000; - } - VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val); - break; - case FAT16: - VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret); - val = 0; - VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val); - break; - case FAT32: - VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret); - val = 0; - VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val); - break; - } - RELEASE(Disk->lFAT); - #if CACHE_FAT - } - #endif - if(Disk->type == FAT12 && ret == EOC_FAT12) ret = -1; - if(Disk->type == FAT16 && ret == EOC_FAT16) ret = -1; - if(Disk->type == FAT32 && ret == EOC_FAT32) ret = -1; - return ret; -} -#endif - -/* - * ==================== - * Cluster IO - * ==================== - */ -/** - * \brief Read a cluster - * \param Disk Disk (Volume) to read from - * \param Length Length to read - * \param Buffer Destination for read data - */ -void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer) -{ - ENTER("pDisk xCluster iLength pBuffer", Disk, Cluster, Length, Buffer); - VFS_ReadAt( - Disk->fileHandle, - (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc ) - * Disk->bootsect.bps, - Length, - Buffer - ); - LEAVE('-'); -} - -/* ==================== - * File IO - * ==================== - */ -/** - * \fn Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer) - * \brief Reads data from a specified file - */ -Uint64 FAT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - int preSkip, count; - Uint64 final_bytes; - int i, cluster, pos; - tFAT_VolInfo *disk = Node->ImplPtr; - char tmpBuf[disk->BytesPerCluster]; - int bpc = disk->BytesPerCluster; - - ENTER("pNode Xoffset Xlength pbuffer", Node, Offset, Length, Buffer); - - // Sanity Check offset - if(Offset > Node->Size) { - LOG("Seek past EOF (%i > %i)", Offset, Node->Size); - LEAVE('i', 0); - return 0; - } - - // Cluster is stored in the low 32-bits of the Inode field - cluster = Node->Inode & 0xFFFFFFFF; - - // Clamp Size - if(Offset + Length > Node->Size) { - LOG("Reading past EOF (%lli + %lli > %lli), clamped to %lli", - Offset, Length, Node->Size, Node->Size - Offset); - Length = Node->Size - Offset; - } - - // Skip previous clusters - preSkip = Offset / bpc; - Offset %= bpc; - LOG("preSkip = %i, Offset = %i", preSkip, (int)Offset); - for(i = preSkip; i--; ) - { - cluster = FAT_int_GetFatValue(disk, cluster); - if(cluster == -1) { - Log_Warning("FAT", "Offset is past end of cluster chain mark"); - LEAVE('i', 0); - return 0; - } - } - - // Reading from within one cluster - if((int)Offset + (int)Length <= bpc) - { - LOG("single cluster only"); - FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf); - memcpy( Buffer, (void*)( tmpBuf + Offset%bpc ), Length ); - LEAVE('X', Length); - return Length; - } - - // Align read to a cluster - if( Offset > 0 ) - { - pos = bpc - Offset; - FAT_int_ReadCluster(disk, cluster, bpc, tmpBuf); - memcpy( Buffer, (void*)( tmpBuf + Offset ), pos ); - LOG("pos = %i, Reading the rest of the clusters"); - // Get next cluster in the chain - cluster = FAT_int_GetFatValue(disk, cluster); - if(cluster == -1) { - Log_Warning("FAT", "Read past End of Cluster Chain (Align)"); - LEAVE('X', pos); - return pos; - } - } - else - pos = 0; - - // Get Count of Clusters to read -// count = DivMod64U(Length - pos, bpc, &final_bytes); - count = (Length - pos) / bpc; - final_bytes = (Length - pos) % bpc; - LOG("Offset = %i, Length = %i, count = %i, final_bytes = %i", (int)Offset, (int)Length, count, final_bytes); - - // Read the rest of the cluster data - for( ; count; count -- ) - { - if(cluster == -1) { - Log_Warning("FAT", "Read past End of Cluster Chain (Bulk)"); - LEAVE('X', pos); - return pos; - } - // Read cluster - FAT_int_ReadCluster(disk, cluster, bpc, (void*)(Buffer+pos)); - pos += bpc; - // Get next cluster in the chain - cluster = FAT_int_GetFatValue(disk, cluster); - } - - if( final_bytes > 0 ) - { - if(cluster == -1) { - Log_Warning("FAT", "Read past End of Cluster Chain (Final)"); - LEAVE('X', pos); - return pos; - } - // Read final cluster - FAT_int_ReadCluster( disk, cluster, bpc, tmpBuf ); - memcpy( (void*)(Buffer+pos), tmpBuf, Length-pos ); - } - - #if DEBUG - //Debug_HexDump("FAT_Read", Buffer, Length); - #endif - - LEAVE('X', Length); - return Length; -} - -#if SUPPORT_WRITE -/** - * \brief Write a cluster to disk - */ -void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer) -{ - ENTER("pDisk xCluster pBuffer", Disk, Cluster, Buffer); - VFS_ReadAt( - Disk->fileHandle, - (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc ) - * Disk->bootsect.bps, - Disk->BytesPerCluster, - Buffer - ); - LEAVE('-'); -} - -/** - * \brief Write to a file - * \param Node File Node - * \param Offset Offset within file - * \param Length Size of data to write - * \param Buffer Data source - */ -Uint64 FAT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - tFAT_VolInfo *disk = Node->ImplPtr; - char tmpBuf[disk->BytesPerCluster]; - int remLength = Length; - Uint32 cluster, tmpCluster; - int bNewCluster = 0; - - if(Offset > Node->Size) return 0; - - // Seek Clusters - cluster = Node->Inode & 0xFFFFFFFF; - while( Offset > disk->BytesPerCluster ) - { - cluster = FAT_int_GetFatValue( disk, cluster ); - if(cluster == -1) { - Log_Warning("FAT", "EOC Unexpectedly Reached"); - return 0; - } - Offset -= disk->BytesPerCluster; - } - if( Offset == disk->BytesPerCluster ) - { - Uint32 tmp = FAT_int_AllocateCluster(disk, cluster); - if(!tmp) return 0; - cluster = tmp; - Offset -= disk->BytesPerCluster; - } - - if( Offset + Length < disk->BytesPerCluster ) - { - char tmpBuf[disk->BytesPerCluster]; - - // Read-Modify-Write - FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf ); - memcpy( tmpBuf + Offset, Buffer, Length ); - FAT_int_WriteCluster( disk, cluster, tmpBuf ); - - return Length; - } - - // Clean up changes within a cluster - if( Offset ) - { - // Read-Modify-Write - FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf ); - memcpy( tmpBuf + Offset, Buffer, disk->BytesPerCluster - Offset ); - FAT_int_WriteCluster( disk, cluster, tmpBuf ); - - remLength -= disk->BytesPerCluster - Offset; - Buffer += disk->BytesPerCluster - Offset; - - // Get next cluster (allocating if needed) - tmpCluster = FAT_int_GetFatValue(disk, cluster); - if(tmpCluster == -1) { - tmpCluster = FAT_int_AllocateCluster(disk, cluster); - if( tmpCluster == 0 ) { - return Length - remLength; - } - } - cluster = tmpCluster; - } - - while( remLength > disk->BytesPerCluster ) - { - FAT_int_WriteCluster( disk, cluster, Buffer ); - Buffer += disk->BytesPerCluster; - - // Get next cluster (allocating if needed) - tmpCluster = FAT_int_GetFatValue(disk, cluster); - if(tmpCluster == -1) { - bNewCluster = 1; - tmpCluster = FAT_int_AllocateCluster(disk, cluster); - if( tmpCluster == 0 ) { - return Length - remLength; - } - } - cluster = tmpCluster; - } - - // Finish off - tmpBuf = malloc( disk->BytesPerCluster ); - if( bNewCluster ) - memset(tmpBuf, 0, disk->BytesPerCluster); - else - FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf ); - memcpy( tmpBuf, Buffer, remLength ); - FAT_int_WriteCluster( disk, cluster, tmpBuf ); - free( tmpBuf ); - - return Length; -} -#endif - -/* ==================== - * File Names & Nodes - * ==================== - */ -/** - * \brief Converts a FAT directory entry name into a proper filename - * \param dest Destination array (must be at least 13 bytes in size) - * \param src 8.3 filename (concatenated, e.g 'FILE1 TXT') - */ -void FAT_int_ProperFilename(char *dest, const char *src) -{ - int inpos, outpos; - - // Name - outpos = 0; - for( inpos = 0; inpos < 8; inpos++ ) { - if(src[inpos] == ' ') break; - dest[outpos++] = src[inpos]; - } - inpos = 8; - // Check for empty extensions - if(src[8] != ' ') - { - dest[outpos++] = '.'; - for( ; inpos < 11; inpos++) { - if(src[inpos] == ' ') break; - dest[outpos++] = src[inpos]; - } - } - dest[outpos++] = '\0'; - - //LOG("dest='%s'", dest); -} - -/** - * \fn char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName) - * \brief Converts either a LFN or a 8.3 Name into a proper name - * \param ft Pointer to the file's entry in the parent directory - * \param LongFileName Long file name pointer - * \return Filename as a heap string - */ -char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName) -{ - char *ret; - ENTER("pft sLongFileName", ft, LongFileName); - //Log_Debug("FAT", "FAT_int_CreateName(ft=%p, LongFileName=%p'%s')", ft, LongFileName); - #if USE_LFN - if(LongFileName && LongFileName[0] != '\0') - { - ret = strdup(LongFileName); - } - else - { - #endif - ret = (char*) malloc(13); - if( !ret ) { - Log_Warning("FAT", "FAT_int_CreateName: malloc(13) failed"); - return NULL; - } - FAT_int_ProperFilename(ret, ft->name); - #if USE_LFN - } - #endif - LEAVE('s', ret); - return ret; -} - -/** - * \brief Creates a tVFS_Node structure for a given file entry - * \param Parent Parent directory VFS node - * \param Entry File table entry for the new node - * \param Pos Position in the parent of the new node - */ -tVFS_Node *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry, int Pos) -{ - tVFS_Node node; - tVFS_Node *ret; - tFAT_VolInfo *disk = Parent->ImplPtr; - - ENTER("pParent pFT", Parent, Entry); - LOG("disk = %p", disk); - - memset(&node, 0, sizeof(tVFS_Node)); - - // Set Other Data - // 0-27: Cluster, 32-59: Parent Cluster - node.Inode = Entry->cluster | (Entry->clusterHi<<16) | (Parent->Inode << 32); - LOG("node.Inode = %llx", node.Inode); - // Position in parent directory - node.ImplInt = Pos & 0xFFFF; - // Disk Pointer - node.ImplPtr = disk; - node.Size = Entry->size; - LOG("Entry->size = %i", Entry->size); - // root:root - node.UID = 0; node.GID = 0; - node.NumACLs = 1; - - node.Flags = 0; - if(Entry->attrib & ATTR_DIRECTORY) node.Flags |= VFS_FFLAG_DIRECTORY; - if(Entry->attrib & ATTR_READONLY) { - node.Flags |= VFS_FFLAG_READONLY; - node.ACLs = &gVFS_ACL_EveryoneRX; // R-XR-XR-X - } - else { - node.ACLs = &gVFS_ACL_EveryoneRWX; // RWXRWXRWX - } - - // Create timestamps - node.ATime = timestamp(0,0,0, - ((Entry->adate&0x1F) - 1), // Days - ((Entry->adate&0x1E0) - 1), // Months - 1980+((Entry->adate&0xFF00)>>8) // Years - ); - - node.CTime = Entry->ctimems * 10; // Miliseconds - node.CTime += timestamp( - ((Entry->ctime&0x1F)<<1), // Seconds - ((Entry->ctime&0x3F0)>>5), // Minutes - ((Entry->ctime&0xF800)>>11), // Hours - ((Entry->cdate&0x1F)-1), // Days - ((Entry->cdate&0x1E0)-1), // Months - 1980+((Entry->cdate&0xFF00)>>8) // Years - ); - - node.MTime = timestamp( - ((Entry->mtime&0x1F)<<1), // Seconds - ((Entry->mtime&0x3F0)>>5), // Minutes - ((Entry->mtime&0xF800)>>11), // Hours - ((Entry->mdate&0x1F)-1), // Days - ((Entry->mdate&0x1E0)-1), // Months - 1980+((Entry->mdate&0xFF00)>>8) // Years - ); - - // Set pointers - if(node.Flags & VFS_FFLAG_DIRECTORY) { - //Log_Debug("FAT", "Directory %08x has size 0x%x", node.Inode, node.Size); - node.Type = &gFAT_DirType; - node.Size = -1; - } - else { - node.Type = &gFAT_FileType; - } - - ret = Inode_CacheNode(disk->inodeHandle, &node); - LEAVE('p', ret); - return ret; -} - -/* - * ==================== - * Directory IO - * ==================== - */ - -/** - * \brief Reads a sector from the disk - * \param Node Directory node to read - * \param Sector Sector number in the directory to read - * \param Buffer Destination buffer for the read data - */ -int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer) -{ - Uint64 addr; - tFAT_VolInfo *disk = Node->ImplPtr; - - ENTER("pNode iSector pEntry", Node, Sector, Buffer); - - // Parse address - if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL)) - { - LEAVE('i', 1); - return 1; - } - - LOG("addr = 0x%llx", addr); - // Read Sector - if(VFS_ReadAt(disk->fileHandle, addr, 512, Buffer) != 512) - { - LEAVE('i', 1); - return 1; - } - - LEAVE('i', 0); - return 0; -} - -#if SUPPORT_WRITE -/** - * \brief Writes an entry to the disk - * \todo Support expanding a directory - * \param Node Directory node - * \param ID ID of entry to update - * \param Entry Entry data - * \return Zero on success, non-zero on error - */ -int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry) -{ - Uint64 addr = 0; - int tmp; - Uint32 cluster = 0; - tFAT_VolInfo *disk = Node->ImplPtr; - - ENTER("pNode iID pEntry", Node, ID, Entry); - - tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster); - if( tmp ) - { - //TODO: Allocate a cluster - cluster = FAT_int_AllocateCluster(Node->ImplPtr, cluster); - if(cluster == -1) { - Log_Warning("FAT", "Unable to allocate an other cluster for %p", Node); - LEAVE('i', 1); - return 1; - } - FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster); - } - - - LOG("addr = 0x%llx", addr); - - // Read Sector - VFS_WriteAt(disk->fileHandle, addr, sizeof(fat_filetable), Entry); // Read Dir Data - - LEAVE('i', 0); - return 0; -} -#endif - -#if USE_LFN -/** - * \fn char *FAT_int_GetLFN(tVFS_Node *node) - * \brief Return pointer to LFN cache entry - * \param Node Directory node - * \param ID ID of the short name - * \return Pointer to the LFN cache entry - */ -char *FAT_int_GetLFN(tVFS_Node *Node, int ID) -{ - tFAT_LFNCache *cache; - int i, firstFree; - - Mutex_Acquire( &Node->Lock ); - - // TODO: Thread Safety (Lock things) - cache = Node->Data; - - // Create a cache if it isn't there - if(!cache) { - cache = Node->Data = malloc( sizeof(tFAT_LFNCache) + sizeof(tFAT_LFNCacheEnt) ); - cache->NumEntries = 1; - cache->Entries[0].ID = ID; - cache->Entries[0].Data[0] = '\0'; - Mutex_Release( &Node->Lock ); - //Log_Debug("FAT", "Return = %p (new)", cache->Entries[0].Data); - return cache->Entries[0].Data; - } - - // Scan for this entry - firstFree = -1; - for( i = 0; i < cache->NumEntries; i++ ) - { - if( cache->Entries[i].ID == ID ) { - Mutex_Release( &Node->Lock ); - //Log_Debug("FAT", "Return = %p (match)", cache->Entries[i].Data); - return cache->Entries[i].Data; - } - if( cache->Entries[i].ID == -1 && firstFree == -1 ) - firstFree = i; - } - - if(firstFree == -1) { - // Use `i` for temp length - i = sizeof(tFAT_LFNCache) + (cache->NumEntries+1)*sizeof(tFAT_LFNCacheEnt); - Node->Data = realloc( Node->Data, i ); - if( !Node->Data ) { - Log_Error("FAT", "realloc() fail, unable to allocate %i for LFN cache", i); - Mutex_Release( &Node->Lock ); - return NULL; - } - //Log_Debug("FAT", "Realloc (%i)\n", i); - cache = Node->Data; - i = cache->NumEntries; - cache->NumEntries ++; - } - else { - i = firstFree; - } - - // Create new entry - cache->Entries[ i ].ID = ID; - cache->Entries[ i ].Data[0] = '\0'; - - Mutex_Release( &Node->Lock ); - //Log_Debug("FAT", "Return = %p (firstFree, i = %i)", cache->Entries[i].Data, i); - return cache->Entries[ i ].Data; -} - -/** - * \fn void FAT_int_DelLFN(tVFS_Node *node) - * \brief Delete a LFN cache entry - * \param Node Directory node - * \param ID File Entry ID - */ -void FAT_int_DelLFN(tVFS_Node *Node, int ID) -{ - tFAT_LFNCache *cache = Node->Data; - int i; - - // Fast return - if(!cache) return; - - // Scan for a current entry - for( i = 0; i < cache->NumEntries; i++ ) - { - if( cache->Entries[i].ID == ID ) - cache->Entries[i].ID = -1; - } - return ; -} -#endif - -/** - * \fn char *FAT_ReadDir(tVFS_Node *Node, int ID) - * \param Node Node structure of directory - * \param ID Directory position - * \return Filename as a heap string, NULL or VFS_SKIP - */ -char *FAT_ReadDir(tVFS_Node *Node, int ID) -{ - fat_filetable fileinfo[16]; // sizeof(fat_filetable)=32, so 16 per sector - int a = 0; - char *ret; - #if USE_LFN - char *lfn = NULL; - #endif - - ENTER("pNode iID", Node, ID); - - if(FAT_int_ReadDirSector(Node, ID/16, fileinfo)) - { - LOG("End of chain, end of dir"); - LEAVE('n'); - return NULL; - } - - // Offset in sector - a = ID % 16; - - LOG("fileinfo[%i].name[0] = 0x%x", a, (Uint8)fileinfo[a].name[0]); - - // Check if this is the last entry - if( fileinfo[a].name[0] == '\0' ) { - Node->Size = ID; - LOG("End of list"); - LEAVE('n'); - return NULL; // break - } - - // Check for empty entry - if( (Uint8)fileinfo[a].name[0] == 0xE5 ) { - LOG("Empty Entry"); - #if 0 // Stop on empty entry? - LEAVE('n'); - return NULL; // Stop - #else - LEAVE('p', VFS_SKIP); - return VFS_SKIP; // Skip - #endif - } - - #if USE_LFN - // Get Long File Name Cache - if(fileinfo[a].attrib == ATTR_LFN) - { - fat_longfilename *lfnInfo; - - lfnInfo = (fat_longfilename *) &fileinfo[a]; - - // Get cache for corresponding file - // > ID + Index gets the corresponding short node - lfn = FAT_int_GetLFN( Node, ID + (lfnInfo->id & 0x3F) ); - - // Bit 6 indicates the start of an entry - if(lfnInfo->id & 0x40) memset(lfn, 0, 256); - - a = ((lfnInfo->id & 0x3F) - 1) * 13; - //Log_Debug("FAT", "ID = 0x%02x, a = %i", lfnInfo->id, a); - - // Sanity Check (FAT implementations should not allow >255 character names) - if(a > 255) return VFS_SKIP; - - // Append new bytes - lfn[a+ 0] = lfnInfo->name1[0]; lfn[a+ 1] = lfnInfo->name1[1]; - lfn[a+ 2] = lfnInfo->name1[2]; lfn[a+ 3] = lfnInfo->name1[3]; - lfn[a+ 4] = lfnInfo->name1[4]; - lfn[a+ 5] = lfnInfo->name2[0]; lfn[a+ 6] = lfnInfo->name2[1]; - lfn[a+ 7] = lfnInfo->name2[2]; lfn[a+ 8] = lfnInfo->name2[3]; - lfn[a+ 9] = lfnInfo->name2[4]; lfn[a+10] = lfnInfo->name2[5]; - lfn[a+11] = lfnInfo->name3[0]; lfn[a+12] = lfnInfo->name3[1]; - LOG("lfn = '%s'", lfn); - //Log_Debug("FAT", "lfn = '%s'", lfn); - LEAVE('p', VFS_SKIP); - return VFS_SKIP; - } - #endif - - // Check if it is a volume entry - if(fileinfo[a].attrib & 0x08) { - LEAVE('p', VFS_SKIP); - return VFS_SKIP; - } - // Ignore . - if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == ' ') { - LEAVE('p', VFS_SKIP); - return VFS_SKIP; - } - // and .. - if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == '.' && fileinfo[a].name[2] == ' ') { - LEAVE('p', VFS_SKIP); - return VFS_SKIP; - } - - LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'", - fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3], - fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7], - fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] ); - - #if USE_LFN - lfn = FAT_int_GetLFN(Node, ID); - //Log_Debug("FAT", "lfn = %p'%s'", lfn, lfn); - ret = FAT_int_CreateName(&fileinfo[a], lfn); - #else - ret = FAT_int_CreateName(&fileinfo[a], NULL); - #endif - - LEAVE('s', ret); - return ret; -} - -/** - * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name) - * \brief Finds an entry in the current directory - */ -tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name) -{ - fat_filetable fileinfo[16]; - char tmpName[13]; - #if USE_LFN - fat_longfilename *lfnInfo; - char lfn[256]; - int lfnPos=255, lfnId = -1; - #endif - int i; - tVFS_Node *tmpNode; - tFAT_VolInfo *disk = Node->ImplPtr; - Uint32 cluster; - - ENTER("pNode sname", Node, Name); - - // Fast Returns - if(!Name || Name[0] == '\0') { - LEAVE('n'); - return NULL; - } - - for( i = 0; ; i++ ) - { - if((i & 0xF) == 0) { - if(FAT_int_ReadDirSector(Node, i/16, fileinfo)) - { - LEAVE('n'); - return NULL; - } - } - - //Check if the files are free - if(fileinfo[i&0xF].name[0] == '\0') break; // End of List marker - if(fileinfo[i&0xF].name[0] == '\xE5') continue; // Free entry - - - #if USE_LFN - // Long File Name Entry - if(fileinfo[i & 0xF].attrib == ATTR_LFN) - { - lfnInfo = (fat_longfilename *) &fileinfo[i&0xF]; - if(lfnInfo->id & 0x40) { - memset(lfn, 0, 256); - lfnPos = (lfnInfo->id & 0x3F) * 13 - 1; - } - // Sanity check the position so we don't overflow - if( lfnPos < 12 ) - continue ; - lfn[lfnPos--] = lfnInfo->name3[1]; lfn[lfnPos--] = lfnInfo->name3[0]; - lfn[lfnPos--] = lfnInfo->name2[5]; lfn[lfnPos--] = lfnInfo->name2[4]; - lfn[lfnPos--] = lfnInfo->name2[3]; lfn[lfnPos--] = lfnInfo->name2[2]; - lfn[lfnPos--] = lfnInfo->name2[1]; lfn[lfnPos--] = lfnInfo->name2[0]; - lfn[lfnPos--] = lfnInfo->name1[4]; lfn[lfnPos--] = lfnInfo->name1[3]; - lfn[lfnPos--] = lfnInfo->name1[2]; lfn[lfnPos--] = lfnInfo->name1[1]; - lfn[lfnPos--] = lfnInfo->name1[0]; - if((lfnInfo->id&0x3F) == 1) - { - lfnId = i+1; - } - } - else - { - // Remove LFN if it does not apply - if(lfnId != i) lfn[0] = '\0'; - #else - if(fileinfo[i&0xF].attrib == ATTR_LFN) continue; - #endif - // Get Real Filename - FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name); - LOG("tmpName = '%s'", tmpName); - - // Only the long name is case sensitive, 8.3 is not - #if USE_LFN - if(strucmp(tmpName, Name) == 0 || strcmp(lfn, Name) == 0) - #else - if(strucmp(tmpName, Name) == 0) - #endif - { - cluster = fileinfo[i&0xF].cluster | (fileinfo[i&0xF].clusterHi << 16); - tmpNode = Inode_GetCache(disk->inodeHandle, cluster); - if(tmpNode == NULL) // Node is not cached - { - tmpNode = FAT_int_CreateNode(Node, &fileinfo[i&0xF], i); - } - LEAVE('p', tmpNode); - return tmpNode; - } - #if USE_LFN - } - #endif - } - - LEAVE('n'); - return NULL; -} - -tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode) -{ - tFAT_VolInfo *disk = Root->ImplPtr; - int ents_per_sector = 512 / sizeof(fat_filetable); - fat_filetable fileinfo[ents_per_sector]; - int sector = 0, i; - tVFS_Node stub_node; - - ENTER("pRoot XInode", Root, Inode); - - stub_node.ImplPtr = disk; - stub_node.Size = -1; - stub_node.Inode = Inode >> 32; - - for( i = 0; ; i ++ ) - { - if( i == 0 || i == ents_per_sector ) - { - if(FAT_int_ReadDirSector(&stub_node, sector, fileinfo)) - { - LOG("ReadDirSector failed"); - LEAVE('n'); - return NULL; - } - i = 0; - sector ++; - } - - // Check for free/end of list - if(fileinfo[i].name[0] == '\0') break; // End of List marker - if(fileinfo[i].name[0] == '\xE5') continue; // Free entry - - if(fileinfo[i].attrib == ATTR_LFN) continue; - - LOG("fileinfo[i].cluster = %x %04x", fileinfo[i].clusterHi, fileinfo[i].cluster); - #if DEBUG - { - char tmpName[13]; - FAT_int_ProperFilename(tmpName, fileinfo[i].name); - LOG("tmpName = '%s'", tmpName); - } - #endif - - - if(fileinfo[i].cluster != (Inode & 0xFFFF)) continue; - if(fileinfo[i].clusterHi != ((Inode >> 16) & 0xFFFF)) continue; - - LEAVE_RET('p', FAT_int_CreateNode(&stub_node, &fileinfo[i], sector*ents_per_sector+i)); - } - LOG("sector = %i, i = %i", sector, i); - LEAVE('n'); - return NULL; -} - -#if SUPPORT_WRITE -/** - * \fn int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags) - * \brief Create a new node - */ -int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags) -{ - return 0; -} - -/** - * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName) - * \brief Rename / Delete a file - */ -int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName) -{ - tVFS_Node *child; - fat_filetable ft = {0}; - int ret; - - child = FAT_FindDir(Node, OldName); - if(!child) return ENOTFOUND; - - // Delete? - if( NewName == NULL ) - { - child->ImplInt |= FAT_FLAG_DELETE; // Mark for deletion on close - - // Delete from the directory - ft.name[0] = '\xE9'; - FAT_int_WriteDirEntry(Node, child->ImplInt & 0xFFFF, &ft); - - // Return success - ret = EOK; - } - // Rename - else - { - Log_Warning("FAT", "Renaming no yet supported %p ('%s' => '%s')", - Node, OldName, NewName); - ret = ENOTIMPL; - } - - // Close child - child->Close( child ); - return ret; -} -#endif - -/** - * \fn void FAT_CloseFile(tVFS_Node *Node) - * \brief Close an open file - */ -void FAT_CloseFile(tVFS_Node *Node) -{ - tFAT_VolInfo *disk = Node->ImplPtr; - if(Node == NULL) return ; - - #if SUPPORT_WRITE - // Update the node if it's dirty (don't bother if it's marked for - // deletion) - if( (Node->ImplInt & FAT_FLAG_DIRTY) && !(Node->ImplInt & FAT_FLAG_DELETE) ) - { - tFAT_VolInfo buf[16]; - tFAT_VolInfo *ft = &buf[ (Node->ImplInt & 0xFFFF) % 16 ]; - - FAT_int_ReadDirSector(Node, (Node->ImplInt & 0xFFFF)/16, buf); - ft->size = Node->Size; - // TODO: update adate, mtime, mdate - FAT_int_WriteDirEntry(Node, Node->ImplInt & 0xFFFF, ft); - - Node->ImplInt &= ~FAT_FLAG_DIRTY; - } - #endif - - // TODO: Make this more thread safe somehow, probably by moving the - // Inode_UncacheNode higher up and saving the cluster value somewhere - if( Node->ReferenceCount == 1 ) - { - #if SUPPORT_WRITE - // Delete File - if( Node->ImplInt & FAT_FLAG_DELETE ) { - // Since the node is marked, we only need to remove it's data - Uint32 cluster = Node->Inode & 0xFFFFFFFF; - while( cluster != -1 ) - cluster = FAT_int_FreeCluster(Node->ImplPtr, cluster); - } - #endif - } - - Inode_UncacheNode(disk->inodeHandle, Node->Inode); - return ; -} diff --git a/Modules/Filesystems/FAT/fs_fat.h b/Modules/Filesystems/FAT/fs_fat.h deleted file mode 100644 index 6896945f..00000000 --- a/Modules/Filesystems/FAT/fs_fat.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Acess2 - * FAT12/16/32 Driver - * vfs/fs/fs_fat.h - */ -#ifndef _FS_FAT_H_ -#define _FS_FAT_H_ - -// === On Disk Structures === -/** - * \struct fat_bootsect_s - * \brief Bootsector format - */ -struct fat_bootsect_s -{ - Uint8 jmp[3]; //!< Jump Instruction - char oemname[8]; //!< OEM Name. Typically MSDOS1.1 - Uint16 bps; //!< Bytes per Sector. Assumed to be 512 - Uint8 spc; //!< Sectors per Cluster - Uint16 resvSectCount; //!< Number of reserved sectors at beginning of volume - // +0x10 - Uint8 fatCount; //!< Number of copies of the FAT - Uint16 files_in_root; //!< Count of files in the root directory - Uint16 totalSect16; //!< Total sector count (FAT12/16) - Uint8 mediaDesc; //!< Media Desctiptor - Uint16 fatSz16; //!< FAT Size (FAT12/16) - // +0x18 - Uint16 spt; //!< Sectors per track. Ignored (Acess uses LBA) - Uint16 heads; //!< Heads. Ignored (Acess uses LBA) - Uint32 hiddenCount; //!< ??? - Uint32 totalSect32; //!< Total sector count (FAT32) - union { - struct { - Uint8 drvNum; //!< Drive Number. BIOS Drive ID (E.g. 0x80) - Uint8 resv; //!< Reserved byte - Uint8 bootSig; //!< Boot Signature. ??? - Uint32 volId; //!< Volume ID - char label[11]; //!< Disk Label - char fsType[8]; //!< FS Type. ??? - } __attribute__((packed)) fat16; //!< FAT16 Specific information - struct { - Uint32 fatSz32; //!< 32-Bit FAT Size - Uint16 extFlags; //!< Extended flags - Uint16 fsVer; //!< Filesystem Version - Uint32 rootClust; //!< Root Cluster ID - Uint16 fsInfo; //!< FS Info. ??? - Uint16 backupBS; //!< Backup Bootsector Sector Offset - char resv[12]; //!< Reserved Data - Uint8 drvNum; //!< Drive Number - char resv2; //!< Reserved Data - Uint8 bootSig; //!< Boot Signature. ??? - Uint32 volId; //!< Volume ID - char label[11]; //!< Disk Label - char fsType[8]; //!< Filesystem Type. ??? - } __attribute__((packed)) fat32; //!< FAT32 Specific Information - }__attribute__((packed)) spec; //!< Non Shared Data - char pad[512-90]; //!< Bootsector Data (Code/Boot Signature 0xAA55) -} __attribute__((packed)); - -/** - \struct fat_filetable_s - \brief Format of a 8.3 file entry on disk -*/ -struct fat_filetable_s { - char name[11]; //!< 8.3 Name - Uint8 attrib; //!< File Attributes. - Uint8 ntres; //!< Reserved for NT - Set to 0 - Uint8 ctimems; //!< 10ths of a second ranging from 0-199 (2 seconds) - Uint16 ctime; //!< Creation Time - Uint16 cdate; //!< Creation Date - Uint16 adate; //!< Accessed Date. No Time feild though - Uint16 clusterHi; //!< High Cluster. 0 for FAT12 and FAT16 - Uint16 mtime; //!< Last Modified Time - Uint16 mdate; //!< Last Modified Date - Uint16 cluster; //!< Low Word of First cluster - Uint32 size; //!< Size of file -} __attribute__((packed)); - -/** - * \struct fat_longfilename_s - * \brief Format of a long file name entry on disk - */ -struct fat_longfilename_s { - Uint8 id; //!< ID of entry. Bit 6 is set for last entry - Uint16 name1[5]; //!< 5 characters of name - Uint8 attrib; //!< Attributes. Must be ATTR_LFN - Uint8 type; //!< Type. ??? - Uint8 checksum; //!< Checksum - Uint16 name2[6]; //!< 6 characters of name - Uint16 firstCluster; //!< Used for non LFN compatability. Set to 0 - Uint16 name3[2]; //!< Last 2 characters of name -} __attribute__((packed)); - -/** - * \name File Attributes - * \brief Flag values for ::fat_filetable_s.attrib - * \{ - */ -#define ATTR_READONLY 0x01 //!< Read-only file -#define ATTR_HIDDEN 0x02 //!< Hidden File -#define ATTR_SYSTEM 0x04 //!< System File -#define ATTR_VOLUMEID 0x08 //!< Volume ID (Deprecated) -#define ATTR_DIRECTORY 0x10 //!< Directory -/** - * \brief File needs archiving - * \note User set flag, no significance to the FS driver - */ -#define ATTR_ARCHIVE 0x20 -/** - * \brief Meta Attribute - * - * If ::fat_filetable_s.attrib equals ATTR_LFN the file is a LFN entry - */ -#define ATTR_LFN (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUMEID) -/** - * \} - */ - -/** - * \brief Internal IDs for FAT types - */ -enum eFatType -{ - FAT12, //!< FAT12 Volume - FAT16, //!< FAT16 Volume - FAT32, //!< FAT32 Volume -}; - -/** - * \name End of Cluster marks - * \brief FAT values that indicate the end of a cluster chain in - * different versions. - * \{ - */ -#define EOC_FAT12 0x0FFF //!< FAT-12 Mark -#define EOC_FAT16 0xFFFF //!< FAT-16 Mark -#define EOC_FAT32 0x00FFFFFF //!< FAT-32 Mark -/** - * \} - */ - -typedef struct fat_bootsect_s fat_bootsect; -typedef struct fat_filetable_s fat_filetable; -typedef struct fat_longfilename_s fat_longfilename; - -// === Memory Structures === -/** - * \struct drv_fat_volinfo_s - * \brief Representation of a volume in memory - */ -struct drv_fat_volinfo_s -{ - int fileHandle; //!< File Handle - int type; //!< FAT Type. See eFatType - char name[12]; //!< Volume Name (With NULL Terminator) - tMutex lFAT; //!< Lock to prevent double-writing to the FAT - Uint32 firstDataSect; //!< First data sector - Uint32 rootOffset; //!< Root Offset (clusters) - Uint32 ClusterCount; //!< Total Cluster Count - fat_bootsect bootsect; //!< Boot Sector - tVFS_Node rootNode; //!< Root Node - int BytesPerCluster; - int inodeHandle; //!< Inode Cache Handle - #if CACHE_FAT - Uint32 *FATCache; //!< FAT Cache - #endif -}; - -typedef struct drv_fat_volinfo_s tFAT_VolInfo; - -#endif diff --git a/Modules/Filesystems/InitRD/.gitignore b/Modules/Filesystems/InitRD/.gitignore deleted file mode 100644 index 85e33a6c..00000000 --- a/Modules/Filesystems/InitRD/.gitignore +++ /dev/null @@ -1 +0,0 @@ -files.*.c* diff --git a/Modules/Filesystems/InitRD/GenerateInitRD.php b/Modules/Filesystems/InitRD/GenerateInitRD.php deleted file mode 100644 index e7cd4f7e..00000000 --- a/Modules/Filesystems/InitRD/GenerateInitRD.php +++ /dev/null @@ -1,245 +0,0 @@ - 4 ? $argv[4] : false); - -$gDependencies = array(); - -$lines = file($argv[1]); - -$lDepth = 0; -$lTree = array(); -$lStack = array( array("",array()) ); -foreach($lines as $line) -{ - $line = trim($line); - // Directory - if(preg_match('/^Dir\s+"([^"]+)"\s+{$/', $line, $matches)) - { - $new = array($matches[1], array()); - array_push($lStack, $new); - $lDepth ++; - continue; - } - // End of a block - if($line == "}") - { - $lDepth --; - $lStack[$lDepth][1][] = array_pop($lStack); - continue; - } - // File - if(preg_match('/^File\s+"([^"]+)"\s+"([^"]+)"$/', $line, $matches)) - { - $lStack[$lDepth][1][] = array($matches[1], $matches[2]); - continue; - } - echo "ERROR: $line\n"; - exit(0); -} - -function hd($fp) -{ - //return "0x".str_pad( dechex(ord(fgetc($fp))), 8, "0", STR_PAD_LEFT ); - $val = unpack("I", fread($fp, 4)); - //print_r($val); exit -1; - return "0x".dechex($val[1]); -} - -function hd8($fp) -{ - return "0x".str_pad( dechex(ord(fgetc($fp))), 2, "0", STR_PAD_LEFT ); -} - -$inode = 0; -$gSymFiles = array(); -function ProcessFolder($prefix, $items) -{ - global $gOutput, $gDependencies; - global $ACESSDIR, $ARCH; - global $inode; - global $gSymFiles; - foreach($items as $i=>$item) - { - $inode ++; - if(is_array($item[1])) - { - ProcessFolder("{$prefix}_{$i}", $item[1]); - - $gOutput .= "tInitRD_File {$prefix}_{$i}_entries[] = {\n"; - foreach($item[1] as $j=>$child) - { - if($j) $gOutput .= ",\n"; - $gOutput .= "\t{\"".addslashes($child[0])."\",&{$prefix}_{$i}_{$j}}"; - } - $gOutput .= "\n};\n"; - - $size = count($item[1]); - $gOutput .= <<$child) -{ - if($j) $gOutput .= ",\n"; - $gOutput .= "\t{\"".addslashes($child[0])."\",&gInitRD_Files_{$j}}"; -} -$gOutput .= "\n};\n"; -$nRootFiles = count($lStack[0][1]); -$gOutput .= <<$item) - { - $gOutput .= ",&{$prefix}_{$i}"; - if(is_array($item[1])) - { - PutNodePointers("{$prefix}_{$i}", $item[1]); - } - } -} - -PutNodePointers("gInitRD_Files", $lStack[0][1]); - -$gOutput .= <<$file) -{ - fputs($fp, "$file\n"); -// fputs($fp, "--defsym $sym=_binary_".$sym_filename."_start\n"); -} -fclose($fp); - -if($gDepFile !== false) -{ - $fp = fopen($gDepFile, "w"); - $line = $gOutputFile.":\t".implode(" ", $gDependencies); - fputs($fp, $line); - fclose($fp); -} - -?> diff --git a/Modules/Filesystems/InitRD/Makefile b/Modules/Filesystems/InitRD/Makefile deleted file mode 100644 index 5aa0e653..00000000 --- a/Modules/Filesystems/InitRD/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# InitRD Filesystem Driver -# - -OBJ = main.o files.$(ARCH).o -EXTRA = files.c -NAME = InitRD -EXTRA = files.$(ARCH).c files.$(ARCH).c.dep files.$(ARCH).c.ldopts -LDFLAGS += @files.$(ARCH).c.ldopts - --include ../Makefile.tpl - - -files.$(ARCH).c: GenerateInitRD.php files.lst - ARCH=$(ARCH) ACESSDIR=$(ACESSDIR) php GenerateInitRD.php files.lst $@ $@.ldopts $@.dep - --include files.$(ARCH).c.dep diff --git a/Modules/Filesystems/InitRD/files.lst b/Modules/Filesystems/InitRD/files.lst deleted file mode 100644 index 16d647f2..00000000 --- a/Modules/Filesystems/InitRD/files.lst +++ /dev/null @@ -1,36 +0,0 @@ -Dir "SBin" { - File "init" "__BIN__/SBin/init" - File "login" "__BIN__/SBin/login" -} -Dir "Bin" { - File "CLIShell" "__BIN__/Bin/CLIShell" - File "ls" "__BIN__/Bin/ls" - File "cat" "__BIN__/Bin/cat" - File "mount" "__BIN__/Bin/mount" - File "ifconfig" "__BIN__/Bin/ifconfig" - File "telnet" "__BIN__/Bin/telnet" - File "irc" "__BIN__/Bin/irc" -} -Dir "Libs" { - File "ld-acess.so" "__BIN__/Libs/ld-acess.so" - File "libld-acess.so" "__BIN__/Libs/libld-acess.so" - File "libc.so" "__BIN__/Libs/libc.so" - File "libgcc.so" "__BIN__/Libs/libgcc.so" - File "libreadline.so" "__BIN__/Libs/libreadline.so" - File "libnet.so" "__BIN__/Libs/libnet.so" - File "liburi.so" "__BIN__/Libs/liburi.so" - File "libimage_sif.so" "__BIN__/Libs/libimage_sif.so" - File "libaxwin3.so" "__BIN__/Libs/libaxwin3.so" -} -Dir "Conf" { - File "BootConf.cfg" "__FS__/Conf/BootConf.cfg" -} -Dir "Apps" { - Dir "AxWin" { - Dir "3.0" { - File "AxWinWM" "__BIN__/Apps/AxWin/3.0/AxWinWM" - File "AxWinUI" "__BIN__/Apps/AxWin/3.0/AxWinUI" - File "AcessLogoSmall.sif" "__SRC__/Usermode/Applications/axwin3_src/AcessLogoSmall.sif" - } - } -} diff --git a/Modules/Filesystems/InitRD/initrd.h b/Modules/Filesystems/InitRD/initrd.h deleted file mode 100644 index 479a8418..00000000 --- a/Modules/Filesystems/InitRD/initrd.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - */ -#ifndef _INITRD_H_ -#define _INITRD_H_ - -#include -#include - -typedef struct sInitRD_File -{ - char *Name; - tVFS_Node *Node; -} tInitRD_File; - - -// === Functions === -extern Uint64 InitRD_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Size, void *Buffer); -extern char *InitRD_ReadDir(tVFS_Node *Node, int ID); -extern tVFS_Node *InitRD_FindDir(tVFS_Node *Node, const char *Name); - -// === Globals === -tVFS_NodeType gInitRD_DirType; -tVFS_NodeType gInitRD_FileType; - -#endif diff --git a/Modules/Filesystems/InitRD/main.c b/Modules/Filesystems/InitRD/main.c deleted file mode 100644 index b52ab8de..00000000 --- a/Modules/Filesystems/InitRD/main.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Acess OS - * InitRD Driver Version 1 - */ -#include "initrd.h" -#include - -#define DUMP_ON_MOUNT 1 - -// === IMPORTS == -extern tVFS_Node gInitRD_RootNode; -extern const int giInitRD_NumFiles; -extern tVFS_Node * const gInitRD_FileList[]; - -// === PROTOTYPES === - int InitRD_Install(char **Arguments); -tVFS_Node *InitRD_InitDevice(const char *Device, const char **Arguments); -void InitRD_Unmount(tVFS_Node *Node); -tVFS_Node *InitRD_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode); -Uint64 InitRD_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Size, void *Buffer); -char *InitRD_ReadDir(tVFS_Node *Node, int ID); -tVFS_Node *InitRD_FindDir(tVFS_Node *Node, const char *Name); -void InitRD_DumpDir(tVFS_Node *Node, int Indent); - -// === GLOBALS === -MODULE_DEFINE(0, 0x0A, FS_InitRD, InitRD_Install, NULL); -tVFS_Driver gInitRD_FSInfo = { - "initrd", 0, InitRD_InitDevice, InitRD_Unmount, InitRD_GetNodeFromINode - }; -tVFS_NodeType gInitRD_DirType = { - .ReadDir = InitRD_ReadFile, - .FindDir = InitRD_FindDir - }; -tVFS_NodeType gInitRD_FileType = { - .Read = InitRD_ReadFile - }; - -/** - * \brief Register initrd with the kernel - */ -int InitRD_Install(char **Arguments) -{ - Log_Notice("InitRD", "Installed"); - VFS_AddDriver( &gInitRD_FSInfo ); - - return MODULE_ERR_OK; -} - -/** - * \brief Mount the InitRD - */ -tVFS_Node *InitRD_InitDevice(const char *Device, const char **Arguments) -{ - #if DUMP_ON_MOUNT - InitRD_DumpDir( &gInitRD_RootNode, 0 ); - #endif - Log_Notice("InitRD", "Mounted (%i files)", giInitRD_NumFiles); - return &gInitRD_RootNode; -} - -/** - * \brief Unmount the InitRD - */ -void InitRD_Unmount(tVFS_Node *Node) -{ -} - -/** - */ -tVFS_Node *InitRD_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode) -{ - if( Inode >= giInitRD_NumFiles ) return NULL; - return gInitRD_FileList[Inode]; -} - -/** - * \brief Read from a file - */ -Uint64 InitRD_ReadFile(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - if(Offset > Node->Size) - return 0; - if(Offset + Length > Node->Size) - Length = Node->Size - Offset; - - memcpy(Buffer, Node->ImplPtr+Offset, Length); - - return Length; -} - -/** - * \brief Read from a directory - */ -char *InitRD_ReadDir(tVFS_Node *Node, int ID) -{ - tInitRD_File *dir = Node->ImplPtr; - - if(ID >= Node->Size) - return NULL; - - return strdup(dir[ID].Name); -} - -/** - * \brief Find an element in a directory - */ -tVFS_Node *InitRD_FindDir(tVFS_Node *Node, const char *Name) -{ - int i; - tInitRD_File *dir = Node->ImplPtr; - - LOG("Name = '%s'", Name); - - for( i = 0; i < Node->Size; i++ ) - { - if(strcmp(Name, dir[i].Name) == 0) - return dir[i].Node; - } - - return NULL; -} - -void InitRD_DumpDir(tVFS_Node *Node, int Indent) -{ - int i; - char indent[Indent+1]; - tInitRD_File *dir = Node->ImplPtr; - - for( i = 0; i < Indent; i++ ) indent[i] = ' '; - indent[i] = '\0'; - - for( i = 0; i < Node->Size; i++ ) - { - Log_Debug("InitRD", "%s- %p %s", indent, dir[i].Node, dir[i].Name); - if(dir[i].Node->Flags & VFS_FFLAG_DIRECTORY) - InitRD_DumpDir(dir[i].Node, Indent+1); - } -} diff --git a/Modules/Filesystems/Makefile.tpl b/Modules/Filesystems/Makefile.tpl deleted file mode 100644 index 77dfc2c9..00000000 --- a/Modules/Filesystems/Makefile.tpl +++ /dev/null @@ -1,2 +0,0 @@ -CATEGORY = FS --include ../../Makefile.tpl diff --git a/Modules/Filesystems/NFS/Makefile b/Modules/Filesystems/NFS/Makefile deleted file mode 100644 index 0b2019f0..00000000 --- a/Modules/Filesystems/NFS/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = main.o -NAME = NFS - --include ../Makefile.tpl diff --git a/Modules/Filesystems/NFS/common.h b/Modules/Filesystems/NFS/common.h deleted file mode 100644 index d73592fe..00000000 --- a/Modules/Filesystems/NFS/common.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Acess2 - NFS Driver - * By John Hodge (thePowersGang) - * This file is published under the terms of the Acess licence. See the - * file COPYING for details. - * - * common.h - Common definitions - */ -#ifndef _COMMON_H_ -#define _COMMON_H_ - -typedef struct sNFS_Connection -{ - int FD; - tIPAddr Host; - char *Base; - tVFS_Node Node; -} tNFS_Connection; - -#endif diff --git a/Modules/Filesystems/NFS/main.c b/Modules/Filesystems/NFS/main.c deleted file mode 100644 index 459945fa..00000000 --- a/Modules/Filesystems/NFS/main.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Acess2 - NFS Driver - * By John Hodge (thePowersGang) - * This file is published under the terms of the Acess licence. See the - * file COPYING for details. - * - * main.c - Driver core - */ -#define DEBUG 1 -#define VERBOSE 0 -#include "common.h" -#include - -// === PROTOTYPES === - int NFS_Install(char **Arguments); -tVFS_Node *NFS_InitDevice(char *Devices, char **Options); -void NFS_Unmount(tVFS_Node *Node); - -// === GLOBALS === -MODULE_DEFINE(0, 0x32 /*v0.5*/, FS_NFS, NFS_Install, NULL); -tVFS_Driver gNFS_FSInfo = {"nfs", 0, NFS_InitDevice, NFS_Unmount, NULL}; - -tNFS_Connection *gpNFS_Connections; - -// === CODE === -/** - * \brief Installs the NFS driver - */ -int NFS_Install(char **Arguments) -{ - VFS_AddDriver( &gNFS_FSInfo ); - return 1; -} - -/** - * \brief Mount a NFS share - */ -tVFS_Node *NFS_InitDevice(char *Device, char **Options) -{ - char *path, *host; - tNFS_Connection *conn; - - path = strchr( Device, ':' ) + 1; - host = strndup( Device, (int)(path-Device)-1 ); - - conn = malloc( sizeof(tNFS_Connection) ); - - if( !IPTools_GetAddress(host, &conn->IP) ) { - free(conn); - return NULL; - } - free(host); - - conn->FD = IPTools_OpenUdpClient( &conn->Host ); - if(conn->FD == -1) { - free(conn); - return NULL; - } - - conn->Base = strdup( path ); - conn->RootNode.ImplPtr = conn; - conn->RootNode.Flags = VFS_FFLAG_DIRECTORY; - - conn->RootNode.ReadDir = NFS_ReadDir; - conn->RootNode.FindDir = NFS_FindDir; - conn->RootNode.Close = NULL; - - return &conn->RootNode; -} - -void NFS_Unmount(tVFS_Node *Node) -{ - -} diff --git a/Modules/Filesystems/NTFS/Makefile b/Modules/Filesystems/NTFS/Makefile deleted file mode 100644 index 22c16773..00000000 --- a/Modules/Filesystems/NTFS/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = main.o dir.o -NAME = NTFS - --include ../Makefile.tpl diff --git a/Modules/Filesystems/NTFS/attributes.h b/Modules/Filesystems/NTFS/attributes.h deleted file mode 100644 index 671a36e7..00000000 --- a/Modules/Filesystems/NTFS/attributes.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Acess2 - NTFS Driver - * By John Hodge (thePowersGang) - * This file is published under the terms of the Acess licence. See the - * file COPYING for details. - * - * attributes.h - MFT Attribute Types - */ -#ifndef _ATTRIBUTES_H_ -#define _ATTRIBUTES_H_ - -typedef struct -{ - Uint64 ParentDirectory; //!< Parent directory MFT entry - Sint64 CreationTime; //!< Time the file was created - Sint64 LastDataModTime; //!< Last change time for the data - Sint64 LastMtfModTime; //!< Last change time for the MFT entry - Sint64 LastAccessTime; //!< Last Access Time (unreliable on most systems) - - Uint64 AllocatedSize; //!< Allocated data size for $DATA unnamed stream - Uint64 DataSize; //!< Actual size of $DATA unnamed stream - Uint32 Flags; //!< File attribute files - - union { - struct { - Uint16 PackedSize; //!< Size of buffer needed for extended attributes - Uint16 _reserved; - } PACKED ExtAttrib; - struct { - Uint32 Tag; //!< Type of reparse point - } PACKED ReparsePoint; - } PACKED Type; - - Uint8 FilenameType; //!< Filename namespace (DOS, Windows, Unix) - WCHAR Filename[0]; -} PACKED tNTFS_Attrib_Filename; - -#endif diff --git a/Modules/Filesystems/NTFS/common.h b/Modules/Filesystems/NTFS/common.h deleted file mode 100644 index 598120d2..00000000 --- a/Modules/Filesystems/NTFS/common.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Acess2 - NTFS Driver - * By John Hodge (thePowersGang) - * This file is published under the terms of the Acess licence. See the - * file COPYING for details. - * - * common.h - Common Types and Definitions - */ -#ifndef _COMMON_H_ -#define _COMMON_H_ - -#include -#include - -typedef Uint16 WCHAR; - -// === STRUCTURES === -/** - * In-memory representation of an NTFS Disk - */ -typedef struct sNTFS_Disk -{ - int FD; - int CacheHandle; - - int ClusterSize; - Uint64 MFTBase; - Uint32 MFTRecSize; - - tVFS_Node RootNode; -} tNTFS_Disk; - -typedef struct sNTFS_BootSector -{ - // 0 - Uint8 Jump[3]; - Uint8 SystemID[8]; // = "NTFS " - Uint16 BytesPerSector; - Uint8 SectorsPerCluster; - - // 0xE - Uint8 Unused[7]; - Uint8 MediaDescriptor; - Uint16 Unused2; - Uint16 SectorsPerTrack; - Uint16 Heads; - - // 0x1C - Uint64 Unused3; - Uint32 Unkown; // Usually 0x00800080 (according to Linux docs) - - // 0x28 - Uint64 TotalSectorCount; // Size of volume in sectors - Uint64 MFTStart; // Logical Cluster Number of Cluster 0 of MFT - Uint64 MFTMirrorStart; // Logical Cluster Number of Cluster 0 of MFT Backup - - // 0x40 - // If either of these are -ve, the size can be obtained via - // SizeInBytes = 2^(-1 * Value) - Sint8 ClustersPerMFTRecord; - Uint8 Unused4[3]; - Sint8 ClustersPerIndexRecord; - Uint8 Unused5[3]; - - Uint64 SerialNumber; - - Uint8 Padding[512-0x50]; - -} PACKED tNTFS_BootSector; - -/** - * FILE header, an entry in the MFT - */ -typedef struct sNTFS_FILE_Header -{ - Uint32 Magic; // 'FILE' - Uint16 UpdateSequenceOfs; - Uint16 UpdateSequenceSize; // Size in words of the UpdateSequenceArray - - Uint64 LSN; // $LogFile Sequence Number - - Uint16 SequenceNumber; - Uint16 HardLinkCount; - Uint16 FirstAttribOfs; // Size of header? - Uint16 Flags; // 0: In Use, 1: Directory - - Uint32 RecordSize; // Real Size of FILE Record - Uint32 RecordSpace; // Allocated Size for FILE Record - - /** - * Base address of the MFT containing this record - */ - Uint64 Reference; // "File reference to the base FILE record" ??? - - Uint16 NextAttribID; - union - { - // Only in XP - struct { - Uint16 AlignTo4Byte; - Uint16 RecordNumber; // Number of this MFT Record - Uint16 UpdateSequenceNumber; - Uint16 UpdateSequenceArray[]; - } XP; - struct { - Uint16 UpdateSequenceNumber; - Uint16 UpdateSequenceArray[]; - } All; - } OSDep; - -} PACKED tNTFS_FILE_Header; - -/** - * File Attribute, follows the FILE header - */ -typedef struct sNTFS_FILE_Attrib -{ - Uint32 Type; // See eNTFS_FILE_Attribs - Uint32 Size; // Includes header - - Uint8 ResidentFlag; // (What does this mean?) - Uint8 NameLength; - Uint16 NameOffset; - Uint16 Flags; // 0: Compressed, 14: Encrypted, 15: Sparse - Uint16 AttributeID; - - union - { - struct { - Uint32 AttribLen; // In words - Uint16 AttribOfs; - Uint8 IndexedFlag; - Uint8 Padding; - - Uint16 Name[]; // UTF-16 - // Attribute Data - } Resident; - struct { - Uint64 StartingVCN; - Uint64 LastVCN; - Uint16 DataRunOfs; - Uint16 CompressionUnitSize; - Uint32 Padding; - Uint64 AllocatedSize; - Uint64 RealSize; - Uint64 InitiatedSize; // One assumes, ammount of actual data stored - Uint16 Name[]; // UTF-16 - // Data Runs - } NonResident; - }; -} PACKED tNTFS_FILE_Attrib; - -#endif diff --git a/Modules/Filesystems/NTFS/dir.c b/Modules/Filesystems/NTFS/dir.c deleted file mode 100644 index 37661e82..00000000 --- a/Modules/Filesystems/NTFS/dir.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Acess2 - NTFS Driver - * By John Hodge (thePowersGang) - * This file is published under the terms of the Acess licence. See the - * file COPYING for details. - * - * dir.c - Directory Handling - */ -#include "common.h" -#include "index.h" - -// === PROTOTYPES === -char *NTFS_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name); -Uint64 NTFS_int_IndexLookup(Uint64 Inode, const char *IndexName, const char *Str); - -// === CODE === -/** - * \brief Get the name of an indexed directory entry - */ -char *NTFS_ReadDir(tVFS_Node *Node, int Pos) -{ - return NULL; -} - -/** - * \brief Get an entry from a directory by name - */ -tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name) -{ - tNTFS_Disk *disk = Node->ImplPtr; - Uint64 inode = NTFS_int_IndexLookup(Node->Inode, "$I30", Name); - tVFS_Node node; - - if(!inode) return NULL; - - node.Inode = inode; - - return Inode_CacheNode(disk->CacheHandle, &node); -} - -/** - * \brief Scans an index for the requested value and returns the associated ID - */ -Uint64 NTFS_int_IndexLookup(Uint64 Inode, const char *IndexName, const char *Str) -{ - return 0; -} diff --git a/Modules/Filesystems/NTFS/index.h b/Modules/Filesystems/NTFS/index.h deleted file mode 100644 index f45d8975..00000000 --- a/Modules/Filesystems/NTFS/index.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Acess2 - NTFS Driver - * By John Hodge (thePowersGang) - * This file is published under the terms of the Acess licence. See the - * file COPYING for details. - * - * index.h - Index Types - */ -#ifndef _INDEX_H_ -#define _INDEX_H_ - -#include "attributes.h" - -typedef struct -{ - Uint32 EntryOffset; - Uint32 IndexLength; - Uint32 AllocateSize; - Uint8 Flags; - Uint8 _reserved[3]; -} PACKED tNTFS_IndexHeader; - -typedef struct -{ - Uint32 Type; - Uint32 CollationRule; - Uint32 IndexBlockSize; - Uint8 ClustersPerIndexBlock; - Uint8 _reserved[3]; - tNTFS_IndexHeader Header; -} PACKED tNTFS_IndexRoot; - -typedef struct -{ - union { - struct { - Uint64 File; // MFT Index of file - } PACKED Dir; - /** - * Views/Indexes - */ - struct { - Uint16 DataOffset; - Uint16 DataLength; - Uint32 _reserved; - } PACKED ViewIndex; - } PACKED Header; - - Uint16 Length; //!< Size of the index entry (multiple of 8 bytes) - Uint16 KeyLength; //!< Size of key value - Uint16 Flags; //!< Flags Bitfield - Uint16 _reserved; - - /** - * \brief Key Data - * \note Only valid if \a Flags does not have \a INDEX_ENTRY_END set - * \note In NTFS3 only \a Filename is used - */ - union { - tNTFS_Attrib_Filename Filename; - //TODO: more key types - } PACKED Key; -} PACKED tNTFS_IndexEntry; - -#endif diff --git a/Modules/Filesystems/NTFS/main.c b/Modules/Filesystems/NTFS/main.c deleted file mode 100644 index dbda06e1..00000000 --- a/Modules/Filesystems/NTFS/main.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Acess2 - NTFS Driver - * By John Hodge (thePowersGang) - * - * main.c - Driver core - */ -#define DEBUG 1 -#define VERBOSE 0 -#include -#include -#include "common.h" -#include - -// === IMPORTS === -extern char *NTFS_ReadDir(tVFS_Node *Node, int Pos); -extern tVFS_Node *NTFS_FindDir(tVFS_Node *Node, const char *Name); - -// === PROTOTYPES === - int NTFS_Install(char **Arguments); -tVFS_Node *NTFS_InitDevice(const char *Devices, const char **Options); -void NTFS_Unmount(tVFS_Node *Node); -void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry); - -// === GLOBALS === -MODULE_DEFINE(0, 0x0A /*v0.1*/, FS_NTFS, NTFS_Install, NULL); -tVFS_Driver gNTFS_FSInfo = {"ntfs", 0, NTFS_InitDevice, NTFS_Unmount, NULL}; -tVFS_NodeType gNTFS_DirType = { - .TypeName = "NTFS-File", - .ReadDir = NTFS_ReadDir, - .FindDir = NTFS_FindDir, - .Close = NULL - }; - -tNTFS_Disk gNTFS_Disks; - -// === CODE === -/** - * \brief Installs the NTFS driver - */ -int NTFS_Install(char **Arguments) -{ - VFS_AddDriver( &gNTFS_FSInfo ); - return 0; -} - -/** - * \brief Mount a NTFS volume - */ -tVFS_Node *NTFS_InitDevice(const char *Device, const char **Options) -{ - tNTFS_Disk *disk; - tNTFS_BootSector bs; - - disk = malloc( sizeof(tNTFS_Disk) ); - - disk->FD = VFS_Open(Device, VFS_OPENFLAG_READ); - if(!disk->FD) { - free(disk); - return NULL; - } - - Log_Debug("FS_NTFS", "&bs = %p", &bs); - VFS_ReadAt(disk->FD, 0, 512, &bs); - - Log_Debug("FS_NTFS", "Jump = %02x%02x%02x", - bs.Jump[0], - bs.Jump[1], - bs.Jump[2]); - Log_Debug("FS_NTFS", "SystemID = %02x%02x%02x%02x%02x%02x%02x%02x (%8C)", - bs.SystemID[0], bs.SystemID[1], bs.SystemID[2], bs.SystemID[3], - bs.SystemID[4], bs.SystemID[5], bs.SystemID[6], bs.SystemID[7], - bs.SystemID - ); - Log_Debug("FS_NTFS", "BytesPerSector = %i", bs.BytesPerSector); - Log_Debug("FS_NTFS", "SectorsPerCluster = %i", bs.SectorsPerCluster); - Log_Debug("FS_NTFS", "MediaDescriptor = 0x%x", bs.MediaDescriptor); - Log_Debug("FS_NTFS", "SectorsPerTrack = %i", bs.SectorsPerTrack); - Log_Debug("FS_NTFS", "Heads = %i", bs.Heads); - Log_Debug("FS_NTFS", "TotalSectorCount = 0x%llx", bs.TotalSectorCount); - Log_Debug("FS_NTFS", "MFTStart = 0x%llx", bs.MFTStart); - Log_Debug("FS_NTFS", "MFTMirrorStart = 0x%llx", bs.MFTMirrorStart); - Log_Debug("FS_NTFS", "ClustersPerMFTRecord = %i", bs.ClustersPerMFTRecord); - Log_Debug("FS_NTFS", "ClustersPerIndexRecord = %i", bs.ClustersPerIndexRecord); - Log_Debug("FS_NTFS", "SerialNumber = 0x%llx", bs.SerialNumber); - - disk->ClusterSize = bs.BytesPerSector * bs.SectorsPerCluster; - Log_Debug("NTFS", "Cluster Size = %i KiB", disk->ClusterSize/1024); - disk->MFTBase = bs.MFTStart; - Log_Debug("NTFS", "MFT Base = %i", disk->MFTBase); - Log_Debug("NTFS", "TotalSectorCount = 0x%x", bs.TotalSectorCount); - - if( bs.ClustersPerMFTRecord < 0 ) { - disk->MFTRecSize = 1 << (-bs.ClustersPerMFTRecord); - } - else { - disk->MFTRecSize = bs.ClustersPerMFTRecord * disk->ClusterSize; - } - - disk->RootNode.Inode = 5; // MFT Ent #5 is filesystem root - disk->RootNode.ImplPtr = disk; - - disk->RootNode.UID = 0; - disk->RootNode.GID = 0; - - disk->RootNode.NumACLs = 1; - disk->RootNode.ACLs = &gVFS_ACL_EveryoneRX; - - disk->RootNode.Type = &gNTFS_DirType; - - - NTFS_DumpEntry(disk, 5); - - return &disk->RootNode; -} - -/** - * \brief Unmount an NTFS Disk - */ -void NTFS_Unmount(tVFS_Node *Node) -{ - -} - -/** - * \brief Dumps a MFT Entry - */ -void NTFS_DumpEntry(tNTFS_Disk *Disk, Uint32 Entry) -{ - void *buf = malloc( Disk->MFTRecSize ); - tNTFS_FILE_Header *hdr = buf; - tNTFS_FILE_Attrib *attr; - int i; - - if(!buf) { - Log_Warning("FS_NTFS", "malloc() fail!"); - return ; - } - - VFS_ReadAt( Disk->FD, - Disk->MFTBase * Disk->ClusterSize + Entry * Disk->MFTRecSize, - Disk->MFTRecSize, - buf); - - Log_Debug("FS_NTFS", "MFT Entry #%i", Entry); - Log_Debug("FS_NTFS", "- Magic = 0x%08x (%4C)", hdr->Magic, &hdr->Magic); - Log_Debug("FS_NTFS", "- UpdateSequenceOfs = 0x%x", hdr->UpdateSequenceOfs); - Log_Debug("FS_NTFS", "- UpdateSequenceSize = 0x%x", hdr->UpdateSequenceSize); - Log_Debug("FS_NTFS", "- LSN = 0x%x", hdr->LSN); - Log_Debug("FS_NTFS", "- SequenceNumber = %i", hdr->SequenceNumber); - Log_Debug("FS_NTFS", "- HardLinkCount = %i", hdr->HardLinkCount); - Log_Debug("FS_NTFS", "- FirstAttribOfs = 0x%x", hdr->FirstAttribOfs); - Log_Debug("FS_NTFS", "- Flags = 0x%x", hdr->Flags); - Log_Debug("FS_NTFS", "- RecordSize = 0x%x", hdr->RecordSize); - Log_Debug("FS_NTFS", "- RecordSpace = 0x%x", hdr->RecordSpace); - Log_Debug("FS_NTFS", "- Reference = 0x%llx", hdr->Reference); - Log_Debug("FS_NTFS", "- NextAttribID = 0x%04x", hdr->NextAttribID); - - attr = (void*)( (char*)hdr + hdr->FirstAttribOfs ); - i = 0; - while( (tVAddr)attr < (tVAddr)hdr + hdr->RecordSize ) - { - if(attr->Type == 0xFFFFFFFF) break; - Log_Debug("FS_NTFS", "- Attribute %i", i ++); - Log_Debug("FS_NTFS", " > Type = 0x%x", attr->Type); - Log_Debug("FS_NTFS", " > Size = 0x%x", attr->Size); - Log_Debug("FS_NTFS", " > ResidentFlag = 0x%x", attr->ResidentFlag); - Log_Debug("FS_NTFS", " > NameLength = %i", attr->NameLength); - Log_Debug("FS_NTFS", " > NameOffset = 0x%x", attr->NameOffset); - Log_Debug("FS_NTFS", " > Flags = 0x%x", attr->Flags); - Log_Debug("FS_NTFS", " > AttributeID = 0x%x", attr->AttributeID); - if( !attr->ResidentFlag ) { - Log_Debug("FS_NTFS", " > AttribLen = 0x%x", attr->Resident.AttribLen); - Log_Debug("FS_NTFS", " > AttribOfs = 0x%x", attr->Resident.AttribOfs); - Log_Debug("FS_NTFS", " > IndexedFlag = 0x%x", attr->Resident.IndexedFlag); - Log_Debug("FS_NTFS", " > Name = '%*C'", attr->NameLength, attr->Resident.Name); - Debug_HexDump("FS_NTFS", - (void*)( (tVAddr)attr + attr->Resident.AttribOfs ), - attr->Resident.AttribLen - ); - } - else { - Log_Debug("FS_NTFS", " > StartingVCN = 0x%llx", attr->NonResident.StartingVCN); - Log_Debug("FS_NTFS", " > LastVCN = 0x%llx", attr->NonResident.LastVCN); - Log_Debug("FS_NTFS", " > DataRunOfs = 0x%x", attr->NonResident.DataRunOfs); - Log_Debug("FS_NTFS", " > CompressionUnitSize = 0x%x", attr->NonResident.CompressionUnitSize); - Log_Debug("FS_NTFS", " > AllocatedSize = 0x%llx", attr->NonResident.AllocatedSize); - Log_Debug("FS_NTFS", " > RealSize = 0x%llx", attr->NonResident.RealSize); - Log_Debug("FS_NTFS", " > InitiatedSize = 0x%llx", attr->NonResident.InitiatedSize); - Log_Debug("FS_NTFS", " > Name = '%*C'", attr->NameLength, attr->NonResident.Name); - } - - attr = (void*)( (tVAddr)attr + attr->Size ); - } - - free(buf); -} diff --git a/Modules/IPStack/Makefile b/Modules/IPStack/Makefile deleted file mode 100644 index 0838a343..00000000 --- a/Modules/IPStack/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# -# - -OBJ := main.o interface.o -OBJ += link.o arp.o -OBJ += ipv4.o icmp.o -OBJ += ipv6.o -OBJ += firewall.o routing.o -OBJ += udp.o tcp.o -NAME := IPStack -CATEGORY := - --include ../Makefile.tpl diff --git a/Modules/IPStack/arp.c b/Modules/IPStack/arp.c deleted file mode 100644 index abaea275..00000000 --- a/Modules/IPStack/arp.c +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Acess2 IP Stack - * - Address Resolution Protocol - * - Part of the IPv4 protocol - */ -#define DEBUG 0 -#include "ipstack.h" -#include "arp.h" -#include "link.h" - -#define ARPv6 0 -#define ARP_CACHE_SIZE 64 -#define ARP_MAX_AGE (60*60*1000) // 1Hr - -// === IMPORTS === -extern tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast); -#if ARPv6 -extern tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast); -#endif - -// === PROTOTYPES === - int ARP_Initialise(); -tMacAddr ARP_Resolve4(tInterface *Interface, tIPv4 Address); -void ARP_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer); - -// === GLOBALS === -struct sARP_Cache4 { - tIPv4 IP; - tMacAddr MAC; - Sint64 LastUpdate; - Sint64 LastUsed; -} *gaARP_Cache4; - int giARP_Cache4Space; -tMutex glARP_Cache4; -#if ARPv6 -struct sARP_Cache6 { - tIPv6 IP; - tMacAddr MAC; - Sint64 LastUpdate; - Sint64 LastUsed; -} *gaARP_Cache6; - int giARP_Cache6Space; -tMutex glARP_Cache6; -#endif -volatile int giARP_LastUpdateID = 0; - -// === CODE === -/** - * \fn int ARP_Initialise() - * \brief Initalise the ARP section - */ -int ARP_Initialise() -{ - gaARP_Cache4 = malloc( ARP_CACHE_SIZE * sizeof(struct sARP_Cache4) ); - memset( gaARP_Cache4, 0, ARP_CACHE_SIZE * sizeof(struct sARP_Cache4) ); - giARP_Cache4Space = ARP_CACHE_SIZE; - - #if ARPv6 - gaARP_Cache6 = malloc( ARP_CACHE_SIZE * sizeof(struct sARP_Cache6) ); - memset( gaARP_Cache6, 0, ARP_CACHE_SIZE * sizeof(struct sARP_Cache6) ); - giARP_Cache6Space = ARP_CACHE_SIZE; - #endif - - Link_RegisterType(0x0806, ARP_int_GetPacket); - return 1; -} - -/** - * \brief Resolves a MAC address from an IPv4 address - */ -tMacAddr ARP_Resolve4(tInterface *Interface, tIPv4 Address) -{ - int lastID; - int i; - struct sArpRequest4 req; - Sint64 timeout; - - ENTER("pInterface xAddress", Interface, Address); - - // Check for broadcast - if( Address.L == -1 ) - { - LOG("Broadcast"); - LEAVE('-'); - return cMAC_BROADCAST; - } - - // Check routing tables if not on this subnet - if( IPStack_CompareAddress(4, &Address, Interface->Address, Interface->SubnetBits) == 0 ) - { - tRoute *route = IPStack_FindRoute(4, Interface, &Address); - // If the next hop is defined, use it - // - 0.0.0.0 as the next hop means "no next hop / direct" - if( route && ((tIPv4*)route->NextHop)->L != 0 ) - { - // Recursion: see /Recursion/ - LOG("Recursing with %s", IPStack_PrintAddress(4, route->NextHop)); - LEAVE('-'); - return ARP_Resolve4(Interface, *(tIPv4*)route->NextHop); - } - // No route, fall though - } - else - { - Uint32 netmask; - // Check for broadcast - netmask = IPv4_Netmask(Interface->SubnetBits); - if( (Address.L & ~netmask) == (0xFFFFFFFF & ~netmask) ) - { - LOG("Local Broadcast"); - LEAVE('-'); - return cMAC_BROADCAST; - } - } - - // Check ARP Cache - Mutex_Acquire( &glARP_Cache4 ); - for( i = 0; i < giARP_Cache4Space; i++ ) - { - if(gaARP_Cache4[i].IP.L != Address.L) continue; - - // Check if the entry needs to be refreshed - if( now() - gaARP_Cache4[i].LastUpdate > ARP_MAX_AGE ) break; - - Mutex_Release( &glARP_Cache4 ); - LOG("Return %x:%x:%x:%x:%x:%x", - gaARP_Cache4[i].MAC.B[0], gaARP_Cache4[i].MAC.B[1], - gaARP_Cache4[i].MAC.B[2], gaARP_Cache4[i].MAC.B[3], - gaARP_Cache4[i].MAC.B[4], gaARP_Cache4[i].MAC.B[5] - ); - LEAVE('-'); - return gaARP_Cache4[i].MAC; - } - Mutex_Release( &glARP_Cache4 ); - - lastID = giARP_LastUpdateID; - - // Create request - Log_Log("ARP4", "Asking for address %i.%i.%i.%i", - Address.B[0], Address.B[1], Address.B[2], Address.B[3] - ); - req.HWType = htons(0x0001); // Ethernet - req.Type = htons(0x0800); - req.HWSize = 6; - req.SWSize = 4; - req.Request = htons(1); - req.SourceMac = Interface->Adapter->MacAddr; - req.SourceIP = *(tIPv4*)Interface->Address; - req.DestMac = cMAC_BROADCAST; - req.DestIP = Address; - - // Send Request - Link_SendPacket(Interface->Adapter, 0x0806, req.DestMac, sizeof(struct sArpRequest4), &req); - - timeout = now() + Interface->TimeoutDelay; - - // Wait for a reply - for(;;) - { - while(lastID == giARP_LastUpdateID && now() < timeout) { - Threads_Yield(); - } - - if( now() >= timeout ) { - Log_Log("ARP4", "Timeout"); - break; // Timeout - } - - lastID = giARP_LastUpdateID; - - Mutex_Acquire( &glARP_Cache4 ); - for( i = 0; i < giARP_Cache4Space; i++ ) - { - if(gaARP_Cache4[i].IP.L != Address.L) continue; - - Mutex_Release( &glARP_Cache4 ); - Log_Debug("ARP4", "Return %02x:%02x:%02x:%02x:%02x:%02x", - gaARP_Cache4[i].MAC.B[0], gaARP_Cache4[i].MAC.B[1], - gaARP_Cache4[i].MAC.B[2], gaARP_Cache4[i].MAC.B[3], - gaARP_Cache4[i].MAC.B[4], gaARP_Cache4[i].MAC.B[5]); - return gaARP_Cache4[i].MAC; - } - Mutex_Release( &glARP_Cache4 ); - } - { - tMacAddr ret = {{0,0,0,0,0,0}}; - return ret; - } -} - -/** - * \brief Updates the ARP Cache entry for an IPv4 Address - */ -void ARP_UpdateCache4(tIPv4 SWAddr, tMacAddr HWAddr) -{ - int i; - int free = -1; - int oldest = 0; - - // Find an entry for the IP address in the cache - Mutex_Acquire(&glARP_Cache4); - for( i = giARP_Cache4Space; i--; ) - { - if(gaARP_Cache4[oldest].LastUpdate > gaARP_Cache4[i].LastUpdate) { - oldest = i; - } - if( gaARP_Cache4[i].IP.L == SWAddr.L ) break; - if( gaARP_Cache4[i].LastUpdate == 0 && free == -1 ) free = i; - } - // If there was no match, we need to make one - if(i == -1) { - if(free != -1) - i = free; - else - i = oldest; - } - - Log_Log("ARP4", "Caching %i.%i.%i.%i (%02x:%02x:%02x:%02x:%02x:%02x) in %i", - SWAddr.B[0], SWAddr.B[1], SWAddr.B[2], SWAddr.B[3], - HWAddr.B[0], HWAddr.B[1], HWAddr.B[2], HWAddr.B[3], HWAddr.B[4], HWAddr.B[5], - i - ); - - gaARP_Cache4[i].IP = SWAddr; - gaARP_Cache4[i].MAC = HWAddr; - gaARP_Cache4[i].LastUpdate = now(); - giARP_LastUpdateID ++; - Mutex_Release(&glARP_Cache4); -} - -#if ARPv6 -/** - * \brief Updates the ARP Cache entry for an IPv6 Address - */ -void ARP_UpdateCache6(tIPv6 SWAddr, tMacAddr HWAddr) -{ - int i; - int free = -1; - int oldest = 0; - - // Find an entry for the MAC address in the cache - Mutex_Acquire(&glARP_Cache6); - for( i = giARP_Cache6Space; i--; ) - { - if(gaARP_Cache6[oldest].LastUpdate > gaARP_Cache6[i].LastUpdate) { - oldest = i; - } - if( MAC_EQU(gaARP_Cache6[i].MAC, HWAddr) ) break; - if( gaARP_Cache6[i].LastUpdate == 0 && free == -1 ) free = i; - } - // If there was no match, we need to make one - if(i == -1) { - if(free != -1) - i = free; - else - i = oldest; - gaARP_Cache6[i].MAC = HWAddr; - } - - gaARP_Cache6[i].IP = SWAddr; - gaARP_Cache6[i].LastUpdate = now(); - giARP_LastUpdateID ++; - Mutex_Release(&glARP_Cache6); -} -#endif - -/** - * \fn void ARP_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer) - * \brief Called when an ARP packet is recieved - */ -void ARP_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer) -{ - tArpRequest4 *req4 = Buffer; - #if ARPv6 - tArpRequest6 *req6 = Buffer; - #endif - tInterface *iface; - - // Sanity Check Packet - if( Length < (int)sizeof(tArpRequest4) ) { - Log_Log("ARP", "Recieved undersized packet"); - return ; - } - if( ntohs(req4->Type) != 0x0800 ) { - Log_Log("ARP", "Recieved a packet with a bad type (0x%x)", ntohs(req4->Type)); - return ; - } - if( req4->HWSize != 6 ) { - Log_Log("ARP", "Recieved a packet with HWSize != 6 (%i)", req4->HWSize); - return; - } - #if ARP_DETECT_SPOOFS - if( !MAC_EQU(req4->SourceMac, From) ) { - Log_Log("ARP", "ARP spoofing detected " - "(%02x%02x:%02x%02x:%02x%02x != %02x%02x:%02x%02x:%02x%02x)", - req4->SourceMac.B[0], req4->SourceMac.B[1], req4->SourceMac.B[2], - req4->SourceMac.B[3], req4->SourceMac.B[4], req4->SourceMac.B[5], - From.B[0], From.B[1], From.B[2], - From.B[3], From.B[4], From.B[5] - ); - return; - } - #endif - - switch( ntohs(req4->Request) ) - { - case 1: // You want my IP? - // Check what type of IP it is - switch( req4->SWSize ) - { - case 4: - Log_Debug("ARP", "ARP Request IPv4 Address %i.%i.%i.%i from %i.%i.%i.%i", - req4->DestIP.B[0], req4->DestIP.B[1], req4->DestIP.B[2], - req4->DestIP.B[3], - req4->SourceIP.B[0], req4->SourceIP.B[1], - req4->SourceIP.B[2], req4->SourceIP.B[3]); - Log_Debug("ARP", " from MAC %02x:%02x:%02x:%02x:%02x:%02x", - req4->SourceMac.B[0], req4->SourceMac.B[1], - req4->SourceMac.B[2], req4->SourceMac.B[3], - req4->SourceMac.B[4], req4->SourceMac.B[5]); - iface = IPv4_GetInterface(Adapter, req4->DestIP, 0); - if( iface ) - { - ARP_UpdateCache4(req4->SourceIP, req4->SourceMac); - - req4->DestIP = req4->SourceIP; - req4->DestMac = req4->SourceMac; - req4->SourceIP = *(tIPv4*)iface->Address;; - req4->SourceMac = Adapter->MacAddr; - req4->Request = htons(2); - Log_Debug("ARP", "Sending back us (%02x:%02x:%02x:%02x:%02x:%02x)", - req4->SourceMac.B[0], req4->SourceMac.B[1], - req4->SourceMac.B[2], req4->SourceMac.B[3], - req4->SourceMac.B[4], req4->SourceMac.B[5]); - Link_SendPacket(Adapter, 0x0806, req4->DestMac, sizeof(tArpRequest4), req4); - } - break; - #if ARPv6 - case 6: - if( Length < (int)sizeof(tArpRequest6) ) { - Log_Log("ARP", "Recieved undersized packet (IPv6)"); - return ; - } - Log_Debug("ARP", "ARP Request IPv6 Address %08x:%08x:%08x:%08x", - ntohl(req6->DestIP.L[0]), ntohl(req6->DestIP.L[1]), - ntohl(req6->DestIP.L[2]), ntohl(req6->DestIP.L[3]) - ); - iface = IPv6_GetInterface(Adapter, req6->DestIP, 0); - if( iface ) - { - req6->DestIP = req6->SourceIP; - req6->DestMac = req6->SourceMac; - req6->SourceIP = *(tIPv6*)iface->Address; - req6->SourceMac = Adapter->MacAddr; - req6->Request = htons(2); - Log_Debug("ARP", "Sending back us (%02x:%02x:%02x:%02x:%02x:%02x)", - req4->SourceMac.B[0], req4->SourceMac.B[1], - req4->SourceMac.B[2], req4->SourceMac.B[3], - req4->SourceMac.B[4], req4->SourceMac.B[5]); - Link_SendPacket(Adapter, 0x0806, req6->DestMac, sizeof(tArpRequest6), req6); - } - break; - #endif - default: - Log_Debug("ARP", "Unknown Protocol Address size (%i)", req4->SWSize); - return ; - } - - break; - - case 2: // Ooh! A response! - // Check what type of IP it is - switch( req4->SWSize ) - { - case 4: - ARP_UpdateCache4( req4->SourceIP, From ); - break; - #if ARPv6 - case 6: - if( Length < (int)sizeof(tArpRequest6) ) { - Log_Debug("ARP", "Recieved undersized packet (IPv6)"); - return ; - } - ARP_UpdateCache6( req6->SourceIP, From ); - break; - #endif - default: - Log_Debug("ARP", "Unknown Protocol Address size (%i)", req4->SWSize); - return ; - } - - break; - - default: - Log_Warning("ARP", "Unknown Request ID %i", ntohs(req4->Request)); - break; - } -} diff --git a/Modules/IPStack/arp.h b/Modules/IPStack/arp.h deleted file mode 100644 index 07cdb64d..00000000 --- a/Modules/IPStack/arp.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Acess2 IP Stack - * - Common Header - */ -#ifndef _ARP_H_ -#define _ARP_H_ - -#include "ipstack.h" - -typedef struct sArpRequest4 tArpRequest4; -typedef struct sArpRequest6 tArpRequest6; - -struct sArpRequest4 { - Uint16 HWType; - Uint16 Type; - Uint8 HWSize, SWSize; - Uint16 Request; - tMacAddr SourceMac; - tIPv4 SourceIP; - tMacAddr DestMac; - tIPv4 DestIP; -} __attribute__((packed)); - -struct sArpRequest6 { - Uint16 HWType; - Uint16 Type; - Uint8 HWSize, SWSize; - Uint16 Request; - tMacAddr SourceMac; - tIPv6 SourceIP; - tMacAddr DestMac; - tIPv6 DestIP; -} __attribute__((packed)); - -#endif diff --git a/Modules/IPStack/firewall.c b/Modules/IPStack/firewall.c deleted file mode 100644 index b0390816..00000000 --- a/Modules/IPStack/firewall.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Acess2 IP Stack - * - Firewall Rules - */ -#include "ipstack.h" -#include "firewall.h" - -#define MAX_ADDRTYPE 9 - -// === IMPORTS === - -// === TYPES === -typedef struct sKeyValue tKeyValue; -typedef struct sFirewallMod tFirewallMod; -typedef struct sModuleRule tModuleRule; -typedef struct sRule tRule; -typedef struct sChain tChain; - -// === STRUCTURES === -struct sKeyValue -{ - const char *Key; - const char *Value; -}; - -struct sFirewallMod -{ - const char *Name; - - int (*Match)(tModuleRule *Rule, int AddrType, - const void *Src, const void *Dest, - Uint8 Type, Uint32 Flags, - size_t Length, const void *Data); - - tModuleRule *(*Create)(tKeyValue *Params); -}; - -struct sModuleRule -{ - tModuleRule *Next; - - tFirewallMod *Mod; - - char Data[]; -}; - -struct sRule -{ - tRule *Next; - - int PacketCount; // Number of packets seen - int ByteCount; // Number of bytes seen (IP Payload bytes) - - int bInvertSource; // Boolean NOT flag on source - void *Source; // Source address bytes - int SourceMask; // Source address mask bits - - int bInvertDest; // Boolean NOT flag on destination - void *Dest; // Destination address bytes - int DestMask; // Destination address mask bits - - tModuleRule *Modules; // Modules loaded for this rule - - char Target[]; // Target rule name -}; - -struct sChain -{ - tChain *Next; - - tRule *FirstRule; - tRule *LastRule; - - char Name[]; -}; - -// === PROTOTYPES === - int IPTables_TestChain( - const char *RuleName, - const int AddressType, - const void *Src, const void *Dest, - Uint8 Type, Uint32 Flags, - size_t Length, const void *Data - ); - -// === GLOBALS === -tChain *gapFirewall_Chains[MAX_ADDRTYPE+1]; -tChain gFirewall_DROP = {.Name="DROP"}; -tChain gFirewall_ACCEPT = {.Name="ACCEPT"}; -tChain gFirewall_RETURN = {.Name="RETURN"}; - -// === CODE === -/** - * \brief Apply a rule to a packet - * \return -1 for no match, -2 for RETURN, eFirewallAction otherwise - */ -int IPTables_DoRule( - tRule *Rule, int AddrType, - const void *Src, const void *Dest, - Uint8 Type, Uint32 Flags, - size_t Length, const void *Data) -{ - int rv; - // Check if source doesn't match - if( !IPStack_CompareAddress(AddrType, Src, Rule->Source, Rule->SourceMask) == !Rule->bInvertSource ) - return -1; - // Check if destination doesn't match - if( !IPStack_CompareAddress(AddrType, Dest, Rule->Dest, Rule->DestMask) == !Rule->bInvertDest ) - return -1; - - // TODO: Handle modules (UDP/TCP/etc) - tModuleRule *modrule; - for( modrule = Rule->Modules; modrule; modrule = modrule->Next ) - { - if( !modrule->Mod->Match ) continue; - rv = modrule->Mod->Match(modrule, AddrType, Src, Dest, Type, Flags, Length, Data); - if(rv != 0) return rv; // No match / action - } - - // Update statistics - Rule->PacketCount ++; - Rule->ByteCount += Length; - - return IPTables_TestChain(Rule->Target, AddrType, Src, Dest, Type, Flags, Length, Data); -} - -/** - * \brief Tests an IPv4 chain on a packet - * \return Boolean Disallow (0: Packet Allowed, 1: Drop, 2: Reject, 3: Continue, -1 no match) - */ -int IPTables_TestChain( - const char *RuleName, - const int AddressType, - const void *Src, const void *Dest, - Uint8 Type, Uint32 Flags, - size_t Length, const void *Data - ) -{ - int rv; - tChain *chain; - tRule *rule; - - if( AddressType >= MAX_ADDRTYPE ) return -1; // Bad address type - - // Catch builtin targets - if(strcmp(RuleName, "") == 0) return -1; // No action - if(strcmp(RuleName, "ACCEPT") == 0) return 0; // Accept packet - if(strcmp(RuleName, "DROP") == 0) return 1; // Drop packet - if(strcmp(RuleName, "RETURN") == 0) return -2; // Return from rule - - // Find the rule - for( chain = gapFirewall_Chains[AddressType]; chain; chain = chain->Next ) - { - if( strcmp(chain->Name, RuleName) == 0 ) - break; - } - if( !chain ) return -1; // Bad rule name - - // Check the rules - for( rule = chain->FirstRule; rule; rule = rule->Next ) - { - rv = IPTables_DoRule(rule, AddressType, Src, Dest, Type, Flags, Length, Data); - if( rv == -1 ) - continue ; - if( rv == -2 ) // -2 = Return from a chain/table, pretend no match - return -1; - - return rv; - } - - - return 0; // Accept all for now -} diff --git a/Modules/IPStack/firewall.h b/Modules/IPStack/firewall.h deleted file mode 100644 index 5a623803..00000000 --- a/Modules/IPStack/firewall.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - */ -#ifndef _FIREWALL_H_ -#define _FIREWALL_H_ - -enum eFirewallActions -{ - FIREWALL_ACCEPT, - FIREWALL_DROP -}; - -/** - * \brief Tests a packet on a chain - */ -extern int IPTables_TestChain( - const char *RuleName, - const int AddressType, - const void *Src, const void *Dest, - Uint8 Type, Uint32 Flags, - size_t Length, const void *Data - ); - -#endif diff --git a/Modules/IPStack/icmp.c b/Modules/IPStack/icmp.c deleted file mode 100644 index 16037199..00000000 --- a/Modules/IPStack/icmp.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Acess2 IP Stack - * - ICMP Handling - */ -#include "ipstack.h" -#include "ipv4.h" -#include "icmp.h" - -// === CONSTANTS === -#define PING_SLOTS 64 - -// === PROTOTYPES === -void ICMP_Initialise(); -void ICMP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer); - -// === GLOBALS === -struct { - tInterface *Interface; - int bArrived; -} gICMP_PingSlots[PING_SLOTS]; - -// === CODE === -/** - * \fn void ICMP_Initialise() - * \brief Initialise the ICMP Layer - */ -void ICMP_Initialise() -{ - IPv4_RegisterCallback(IP4PROT_ICMP, ICMP_GetPacket); -} - -/** - * \fn void ICMP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) - * \brief Handles a packet from the IP Layer - */ -void ICMP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) -{ - tICMPHeader *hdr = Buffer; - - //Log_Debug("ICMPv4", "Length = %i", Length); - Log_Debug("ICMPv4", "hdr->Type, hdr->Code = %i, %i", hdr->Type, hdr->Code); - //Log_Debug("ICMPv4", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum)); - Log_Debug("ICMPv4", "hdr->ID = 0x%x", ntohs(hdr->ID)); - Log_Debug("ICMPv4", "hdr->Sequence = 0x%x", ntohs(hdr->Sequence)); - - switch(hdr->Type) - { - // -- 0: Echo Reply - case ICMP_ECHOREPLY: - if(hdr->Code != 0) { - Log_Warning("ICMPv4", "Code == %i for ICMP Echo Reply, should be 0", hdr->Code); - return ; - } - if(hdr->ID != (Uint16)~hdr->Sequence) { - Log_Warning("ICMPv4", "ID and Sequence values do not match"); - //return ; - } - gICMP_PingSlots[hdr->ID].bArrived = 1; - break; - - // -- 3: Destination Unreachable - case ICMP_UNREACHABLE: - switch(hdr->Code) - { - case 3: // Port Unreachable - Log_Debug("ICMPv4", "Destination Unreachable (Port Unreachable)"); - break; - default: - Log_Debug("ICMPv4", "Destination Unreachable (Code %i)", hdr->Code); - break; - } -// IPv4_Unreachable( Interface, hdr->Code, htons(hdr->Length)-sizeof(tICMPHeader), hdr->Data ); - break; - - // -- 8: Echo Request - case ICMP_ECHOREQ: - if(hdr->Code != 0) { - Log_Warning("ICMPv4", "Code == %i for ICMP Echo Request, should be 0", hdr->Code); - return ; - } - //Log_Debug("ICMPv4", "Replying"); - hdr->Type = ICMP_ECHOREPLY; - hdr->Checksum = 0; - hdr->Checksum = htons( IPv4_Checksum( (Uint16*)hdr, Length/2 ) ); - //Log_Debug("ICMPv4", "Checksum = 0x%04x", hdr->Checksum); - IPv4_SendPacket(Interface, *(tIPv4*)Address, 1, ntohs(hdr->Sequence), Length, hdr); - break; - default: - break; - } - -} - -/** - * \brief Sends ICMP Echo and waits for the reply - * \note Times out after \a Interface->TimeoutDelay has elapsed - */ -int ICMP_Ping(tInterface *Interface, tIPv4 Addr) -{ - Sint64 ts; - Sint64 end; - char buf[32] = "\x8\0\0\0\0\0\0\0Acess2 I" - "P/TCP Stack 1.0\0"; - tICMPHeader *hdr = (void*)buf; - int i; - - for(;;) - { - for(i=0;iID = i; - hdr->Sequence = ~i; - hdr->Checksum = htons( IPv4_Checksum((Uint16*)hdr, sizeof(buf)/2) ); - - ts = now(); - - IPv4_SendPacket(Interface, Addr, 1, i, sizeof(buf), buf); - - end = ts + Interface->TimeoutDelay; - while( !gICMP_PingSlots[i].bArrived && now() < end) Threads_Yield(); - - if(now() > end) - return -1; - - return (int)( now() - ts ); -} diff --git a/Modules/IPStack/icmp.h b/Modules/IPStack/icmp.h deleted file mode 100644 index 8e787224..00000000 --- a/Modules/IPStack/icmp.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Acess2 IP Stack - * - ICMP Handling - */ -#ifndef _ICMP_H_ -#define _ICMP_H_ - -// === TYPEDEFS === -typedef struct sICMPHeader tICMPHeader; - -// === STRUCTURES === -struct sICMPHeader -{ - Uint8 Type; - Uint8 Code; - Uint16 Checksum; - Uint16 ID; - Uint16 Sequence; - Uint8 Data[]; -}; - -// === CONSTANTS === -enum eICMPTypes -{ - ICMP_ECHOREPLY = 0, - ICMP_UNREACHABLE = 3, - ICMP_QUENCH = 4, - ICMP_REDIRECT = 5, - ICMP_ALTADDR = 6, - ICMP_ECHOREQ = 8, - ICMP_TRACE = 30 // Information Request -}; - -#endif diff --git a/Modules/IPStack/interface.c b/Modules/IPStack/interface.c deleted file mode 100644 index b0b63b9a..00000000 --- a/Modules/IPStack/interface.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - * Acess2 IP Stack - * - Interface Control - */ -#define DEBUG 0 -#define VERSION VER2(0,10) -#include "ipstack.h" -#include "link.h" -#include -#include - -// === CONSTANTS === -//! Default timeout value, 30 seconds -#define DEFAULT_TIMEOUT (30*1000) - -// === IMPORTS === -extern int IPv4_Ping(tInterface *Iface, tIPv4 Addr); -//extern int IPv6_Ping(tInterface *Iface, tIPv6 Addr); -extern tVFS_Node gIP_RouteNode; - -// === PROTOTYPES === -char *IPStack_Root_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Name); - int IPStack_Root_IOCtl(tVFS_Node *Node, int ID, void *Data); - - int IPStack_AddFile(tSocketFile *File); -tInterface *IPStack_AddInterface(const char *Device, const char *Name); -tAdapter *IPStack_GetAdapter(const char *Path); - -char *IPStack_Iface_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *IPStack_Iface_FindDir(tVFS_Node *Node, const char *Name); - int IPStack_Iface_IOCtl(tVFS_Node *Node, int ID, void *Data); - -// === GLOBALS === -tVFS_NodeType gIP_InterfaceNodeType = { - .ReadDir = IPStack_Iface_ReadDir, - .FindDir = IPStack_Iface_FindDir, - .IOCtl = IPStack_Iface_IOCtl -}; -//! Loopback (127.0.0.0/8, ::1) Pseudo-Interface -tInterface gIP_LoopInterface = { - .Node = { - .ImplPtr = &gIP_LoopInterface, - .Flags = VFS_FFLAG_DIRECTORY, - .Size = -1, - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Type = &gIP_InterfaceNodeType - }, - .Adapter = NULL, - .Type = 0 -}; -tShortSpinlock glIP_Interfaces; -tInterface *gIP_Interfaces = NULL; -tInterface *gIP_Interfaces_Last = NULL; - -tSocketFile *gIP_FileTemplates; - -tAdapter gIP_LoopAdapter = { - .DeviceLen = 8, - .Device = "LOOPBACK" - }; -tMutex glIP_Adapters; -tAdapter *gIP_Adapters = NULL; - int giIP_NextIfaceId = 1; - -// === CODE === - -/** - * \brief Read from the IP Stack's Device Directory - */ -char *IPStack_Root_ReadDir(tVFS_Node *Node, int Pos) -{ - tInterface *iface; - char *name; - ENTER("pNode iPos", Node, Pos); - - - // Routing Subdir - if( Pos == 0 ) { - LEAVE('s', "routes"); - return strdup("routes"); - } - // Pseudo Interfaces - if( Pos == 1 ) { - LEAVE('s', "lo"); - return strdup("lo"); - } - Pos -= 2; - - // Traverse the list - for( iface = gIP_Interfaces; iface && Pos--; iface = iface->Next ) ; - - // Did we run off the end? - if(!iface) { - LEAVE('n'); - return NULL; - } - - name = malloc(4); - if(!name) { - Log_Warning("IPStack", "IPStack_Root_ReadDir - malloc error"); - LEAVE('n'); - return NULL; - } - - // Create the name - Pos = iface->Node.ImplInt; - if(Pos < 10) { - name[0] = '0' + Pos; - name[1] = '\0'; - } - else if(Pos < 100) { - name[0] = '0' + Pos/10; - name[1] = '0' + Pos%10; - name[2] = '\0'; - } - else { - name[0] = '0' + Pos/100; - name[1] = '0' + (Pos/10)%10; - name[2] = '0' + Pos%10; - name[3] = '\0'; - } - - LEAVE('s', name); - // Return the pre-generated name - return name; -} - -/** - * \brief Get the node of an interface - */ -tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Name) -{ - #if 0 - int i, num; - #endif - tInterface *iface; - - ENTER("pNode sName", Node, Name); - - // Routing subdir - if( strcmp(Name, "routes") == 0 ) { - LEAVE('p', &gIP_RouteNode); - return &gIP_RouteNode; - } - - // Loopback - if( strcmp(Name, "lo") == 0 ) { - LEAVE('p', &gIP_LoopInterface.Node); - return &gIP_LoopInterface.Node; - } - - for( iface = gIP_Interfaces; iface; iface = iface->Next ) - { - if( strcmp(iface->Name, Name) == 0 ) - { - LEAVE('p', &iface->Node); - return &iface->Node; - } - } - - LEAVE('p', NULL); - return NULL; -} - -static const char *casIOCtls_Root[] = { DRV_IOCTLNAMES, "add_interface", NULL }; -/** - * \brief Handles IOCtls for the IPStack root - */ -int IPStack_Root_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - int tmp; - ENTER("pNode iID pData", Node, ID, Data); - - switch(ID) - { - // --- Standard IOCtls (0-3) --- - BASE_IOCTLS(DRV_TYPE_MISC, "IPStack", VERSION, casIOCtls_Root) - - /* - * add_interface - * - Adds a new IP interface and binds it to a device - */ - case 4: - if( Threads_GetUID() != 0 ) LEAVE_RET('i', -1); - if( !CheckString( Data ) ) LEAVE_RET('i', -1); - LOG("New interface for '%s'", Data); - { - char name[4] = ""; - tInterface *iface = IPStack_AddInterface(Data, name); - if(iface == NULL) LEAVE_RET('i', -1); - tmp = iface->Node.ImplInt; - } - LEAVE_RET('i', tmp); - } - LEAVE('i', 0); - return 0; -} - -/** - * \fn tInterface *IPStack_AddInterface(char *Device) - * \brief Adds an interface to the list - */ -tInterface *IPStack_AddInterface(const char *Device, const char *Name) -{ - tInterface *iface; - tAdapter *card; - int nameLen; - - ENTER("sDevice", Device); - - card = IPStack_GetAdapter(Device); - if( !card ) { - Log_Debug("IPStack", "Unable to open card '%s'", Device); - LEAVE('n'); - return NULL; // ERR_YOURBAD - } - - nameLen = sprintf(NULL, "%i", giIP_NextIfaceId); - - iface = malloc( - sizeof(tInterface) - + nameLen + 1 - + IPStack_GetAddressSize(-1)*3 // Address, Route->Network, Route->NextHop - ); - if(!iface) { - Log_Warning("IPStack", "AddInterface - malloc() failed"); - LEAVE('n'); - return NULL; // Return ERR_MYBAD - } - - iface->Next = NULL; - iface->Type = 0; // Unset type - iface->Address = iface->Name + nameLen + 1; // Address - iface->Route.Network = iface->Address + IPStack_GetAddressSize(-1); - iface->Route.NextHop = iface->Route.Network + IPStack_GetAddressSize(-1); - - // Create Node - iface->Node.ImplPtr = iface; - iface->Node.Flags = VFS_FFLAG_DIRECTORY; - iface->Node.Size = -1; - iface->Node.NumACLs = 1; - iface->Node.ACLs = &gVFS_ACL_EveryoneRX; - iface->Node.Type = &gIP_InterfaceNodeType; - - // Set Defaults - iface->TimeoutDelay = DEFAULT_TIMEOUT; - - // Get adapter handle - iface->Adapter = card; - - // Delay setting ImplInt until after the adapter is opened - // Keeps things simple - iface->Node.ImplInt = giIP_NextIfaceId++; - sprintf(iface->Name, "%i", (int)iface->Node.ImplInt); - - // Append to list - SHORTLOCK( &glIP_Interfaces ); - if( gIP_Interfaces ) { - gIP_Interfaces_Last->Next = iface; - gIP_Interfaces_Last = iface; - } - else { - gIP_Interfaces = iface; - gIP_Interfaces_Last = iface; - } - SHORTREL( &glIP_Interfaces ); - -// gIP_DriverInfo.RootNode.Size ++; - - // Success! - LEAVE('p', iface); - return iface; -} - -/** - * \brief Adds a file to the socket list - */ -int IPStack_AddFile(tSocketFile *File) -{ - Log_Log("IPStack", "Added file '%s'", File->Name); - File->Next = gIP_FileTemplates; - gIP_FileTemplates = File; - return 0; -} - -// --- -// VFS Functions -// --- -/** - * \brief Read from an interface's directory - */ -char *IPStack_Iface_ReadDir(tVFS_Node *Node, int Pos) -{ - tSocketFile *file = gIP_FileTemplates; - while(Pos-- && file) { - file = file->Next; - } - - if(!file) return NULL; - - return strdup(file->Name); -} - -/** - * \brief Gets a named node from an interface directory - */ -tVFS_Node *IPStack_Iface_FindDir(tVFS_Node *Node, const char *Name) -{ - tSocketFile *file = gIP_FileTemplates; - - // Get file definition - for(;file;file = file->Next) - { - if( strcmp(file->Name, Name) == 0 ) break; - } - if(!file) return NULL; - - // Pass the buck! - return file->Init(Node->ImplPtr); -} - -/** - * \brief Names for interface IOCtl Calls - */ -static const char *casIOCtls_Iface[] = { - DRV_IOCTLNAMES, - "getset_type", - "get_address", "set_address", - "getset_subnet", - "get_device", - "ping", - NULL - }; -/** - * \brief Handles IOCtls for the IPStack interfaces - */ -int IPStack_Iface_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - int tmp, size; - tInterface *iface = (tInterface*)Node->ImplPtr; - ENTER("pNode iID pData", Node, ID, Data); - - switch(ID) - { - // --- Standard IOCtls (0-3) --- - BASE_IOCTLS(DRV_TYPE_MISC, "IPStack", VERSION, casIOCtls_Iface) - - /* - * getset_type - * - Get/Set the interface type - */ - case 4: - // Set Type? - if( Data ) - { - // Ok, it's set type - if( Threads_GetUID() != 0 ) { - LOG("Attempt by non-root to alter an interface (%i)", Threads_GetUID()); - LEAVE('i', -1); - return -1; - } - if( !CheckMem( Data, sizeof(int) ) ) { - LOG("Invalid pointer %p", Data); - LEAVE('i', -1); - return -1; - } - - // Set type - iface->Type = *(int*)Data; - LOG("Interface type set to %i", iface->Type); - size = IPStack_GetAddressSize(iface->Type); - // Check it's actually valid - if( iface->Type != 0 && size == 0 ) { - iface->Type = 0; - LEAVE('i', -1); - return -1; - } - - // Clear address - memset(iface->Address, 0, size); - } - LEAVE('i', iface->Type); - return iface->Type; - - /* - * get_address - * - Get the interface's address - */ - case 5: - size = IPStack_GetAddressSize(iface->Type); - if( !CheckMem( Data, size ) ) LEAVE_RET('i', -1); - memcpy( Data, iface->Address, size ); - LEAVE('i', 1); - return 1; - - /* - * set_address - * - Set the interface's address - */ - case 6: - if( Threads_GetUID() != 0 ) LEAVE_RET('i', -1); - - size = IPStack_GetAddressSize(iface->Type); - if( !CheckMem( Data, size ) ) LEAVE_RET('i', -1); - // TODO: Protect against trashing - LOG("Interface address set to '%s'", IPStack_PrintAddress(iface->Type, Data)); - memcpy( iface->Address, Data, size ); - LEAVE_RET('i', 1); - - /* - * getset_subnet - * - Get/Set the bits in the address subnet - */ - case 7: - // Do we want to set the value? - if( Data ) - { - // Are we root? (TODO: Check Owner/Group) - if( Threads_GetUID() != 0 ) LEAVE_RET('i', -1); - // Is the memory valid - if( !CheckMem(Data, sizeof(int)) ) LEAVE_RET('i', -1); - - // Is the mask sane? - if( *(int*)Data < 0 || *(int*)Data > IPStack_GetAddressSize(iface->Type)*8-1 ) - LEAVE_RET('i', -1); - LOG("Set subnet bits to %i", *(int*)Data); - // Ok, set it - iface->SubnetBits = *(int*)Data; - } - LEAVE_RET('i', iface->SubnetBits); - - /* - * get_device - * - Gets the name of the attached device - */ - case 8: - if( iface->Adapter == NULL ) - LEAVE_RET('i', 0); - if( Data == NULL ) - LEAVE_RET('i', iface->Adapter->DeviceLen); - if( !CheckMem( Data, iface->Adapter->DeviceLen+1 ) ) - LEAVE_RET('i', -1); - strcpy( Data, iface->Adapter->Device ); - LEAVE_RET('i', iface->Adapter->DeviceLen); - - /* - * ping - * - Send an ICMP Echo - */ - case 9: - switch(iface->Type) - { - case 0: - LEAVE_RET('i', 1); - - case 4: - if( !CheckMem( Data, sizeof(tIPv4) ) ) LEAVE_RET('i', -1); - tmp = IPv4_Ping(iface, *(tIPv4*)Data); - LEAVE_RET('i', tmp); - - case 6: - LEAVE_RET('i', 1); - } - break; - - } - - LEAVE('i', 0); - return 0; -} - -// --- Internal --- -/** - * \fn tAdapter *IPStack_GetAdapter(const char *Path) - * \brief Gets/opens an adapter given the path - */ -tAdapter *IPStack_GetAdapter(const char *Path) -{ - tAdapter *dev; - int tmp; - - ENTER("sPath", Path); - - // Check for loopback - if( strcmp(Path, "LOOPBACK") == 0 ) - { - // Initialise if required - if( gIP_LoopAdapter.DeviceFD == 0 ) - { - dev = &gIP_LoopAdapter; - - dev->NRef = 1; - dev->DeviceLen = 8; - - dev->DeviceFD = VFS_Open( "/Devices/fifo/anon", VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE ); - if( dev->DeviceFD == -1 ) { - Log_Warning("IPStack", "Unable to open FIFO '/Devices/fifo/anon' for loopback"); - return NULL; - } - - dev->MacAddr.B[0] = 'A'; - dev->MacAddr.B[1] = 'c'; - dev->MacAddr.B[2] = 'e'; - dev->MacAddr.B[3] = 's'; - dev->MacAddr.B[4] = 's'; - dev->MacAddr.B[5] = '2'; - - // Start watcher - Link_WatchDevice( dev ); - } - LEAVE('p', &gIP_LoopAdapter); - return &gIP_LoopAdapter; - } - - Mutex_Acquire( &glIP_Adapters ); - - // Check if this adapter is already open - for( dev = gIP_Adapters; dev; dev = dev->Next ) - { - if( strcmp(dev->Device, Path) == 0 ) { - dev->NRef ++; - Mutex_Release( &glIP_Adapters ); - LEAVE('p', dev); - return dev; - } - } - - // Ok, so let's open it - dev = malloc( sizeof(tAdapter) + strlen(Path) + 1 ); - if(!dev) { - Log_Warning("IPStack", "GetAdapter - malloc() failed"); - Mutex_Release( &glIP_Adapters ); - LEAVE('n'); - return NULL; - } - - // Fill Structure - strcpy( dev->Device, Path ); - dev->NRef = 1; - dev->DeviceLen = strlen(Path); - - // Open Device - dev->DeviceFD = VFS_Open( dev->Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE ); - if( dev->DeviceFD == -1 ) { - free( dev ); - Mutex_Release( &glIP_Adapters ); - LEAVE('n'); - return NULL; - } - - // Check that it is a network interface - tmp = VFS_IOCtl(dev->DeviceFD, 0, NULL); - LOG("Device type = %i", tmp); - if( tmp != DRV_TYPE_NETWORK ) { - Log_Warning("IPStack", "IPStack_GetAdapter: '%s' is not a network interface", dev->Device); - VFS_Close( dev->DeviceFD ); - free( dev ); - Mutex_Release( &glIP_Adapters ); - LEAVE('n'); - return NULL; - } - - // Get MAC Address - VFS_IOCtl(dev->DeviceFD, NET_IOCTL_GETMAC, &dev->MacAddr); - - // Add to list - dev->Next = gIP_Adapters; - gIP_Adapters = dev; - - Mutex_Release( &glIP_Adapters ); - - // Start watcher - Link_WatchDevice( dev ); - - LEAVE('p', dev); - return dev; -} diff --git a/Modules/IPStack/ipstack.h b/Modules/IPStack/ipstack.h deleted file mode 100644 index e51fded9..00000000 --- a/Modules/IPStack/ipstack.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Acess2 IP Stack - * - Common Header - */ -#ifndef _IPSTACK_H_ -#define _IPSTACK_H_ - -#include -#include - -typedef union uIPv4 tIPv4; -typedef union uIPv6 tIPv6; -typedef struct sMacAddr tMacAddr; -typedef struct sAdapter tAdapter; -typedef struct sInterface tInterface; -typedef struct sSocketFile tSocketFile; - -typedef void (*tIPCallback)(tInterface *Interface, void *Address, int Length, void *Buffer); - -enum eInterfaceTypes { - AF_NULL, - AF_INET4 = 4, // tIPv4 - AF_INET6 = 6 // tIPv6 -}; - -union uIPv4 { - Uint32 L; - Uint8 B[4]; -} __attribute__((packed)); - -union uIPv6 { - Uint16 W[8]; - Uint32 L[4]; - Uint8 B[16]; -} __attribute__((packed)); - -struct sMacAddr { - Uint8 B[6]; -} __attribute__((packed)); - -/** - * \brief Route definition structure - */ -typedef struct sRoute { - struct sRoute *Next; - - tVFS_Node Node; //!< Node for route manipulation - - tInterface *Interface; //!< Interface for this route - int AddressType; //!< 0: Invalid, 4: IPv4, 6: IPv4 - void *Network; //!< Network - Pointer to tIPv4/tIPv6/... at end of structure - int SubnetBits; //!< Number of bits in \a Network that are valid - void *NextHop; //!< Next Hop address - Pointer to tIPv4/tIPv6/... at end of structure - int Metric; //!< Route priority -} tRoute; - -struct sInterface { - struct sInterface *Next; //!< Next interface in list - - tVFS_Node Node; //!< Node to use the interface - - tAdapter *Adapter; //!< Adapter the interface is associated with - int TimeoutDelay; //!< Time in miliseconds before a packet times out - int Type; //!< Interface type, see ::eInterfaceTypes - - void *Address; //!< IP address (stored after the Name) - int SubnetBits; //!< Number of bits that denote the address network - - tRoute Route; //!< Interface route - - char Name[]; -}; - -/** - * \brief Represents a network adapter - */ -struct sAdapter { - struct sAdapter *Next; - - int DeviceFD; //!< File descriptor of the device - int NRef; //!< Number of times it's been referenced - - tMacAddr MacAddr; //!< Physical address of the adapter - int DeviceLen; //!< Device name length - char Device[]; //!< Device name -}; - -/** - * \brief Describes a socket file definition - */ -struct sSocketFile -{ - struct sSocketFile *Next; - const char *Name; - - tVFS_Node *(*Init)(tInterface *Interface); -}; - -static const tMacAddr cMAC_BROADCAST = {{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}; -static const tMacAddr cMAC_ZERO = {{0x00,0x00,0x00,0x00,0x00,0x00}}; - -#define MAC_SET(t,v) memcpy(&(t),&(v),sizeof(tMacAddr)) -#define IP4_SET(t,v) (t).L = (v).L; -#define IP6_SET(t,v) memcpy(&(t),&(v),sizeof(tIPv6)) - -#define MAC_EQU(a,b) (memcmp(&(a),&(b),sizeof(tMacAddr))==0) -#define IP4_EQU(a,b) ((a).L==(b).L) -#define IP6_EQU(a,b) (memcmp(&(a),&(b),sizeof(tIPv6))==0) - -// === FUNCTIONS === -#define htonb(v) (v) -#define htons(v) BigEndian16(v) -#define htonl(v) BigEndian32(v) -#define ntonb(v) (v) -#define ntohs(v) BigEndian16(v) -#define ntohl(v) BigEndian32(v) - -extern int IPStack_AddFile(tSocketFile *File); -extern int IPStack_GetAddressSize(int AddressType); -extern int IPStack_CompareAddress(int AddressType, const void *Address1, const void *Address2, int CheckBits); -extern const char *IPStack_PrintAddress(int AddressType, const void *Address); - -extern tRoute *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address); - -#endif diff --git a/Modules/IPStack/ipv4.c b/Modules/IPStack/ipv4.c deleted file mode 100644 index 86301e97..00000000 --- a/Modules/IPStack/ipv4.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Acess2 IP Stack - * - IPv4 Protcol Handling - */ -#define DEBUG 1 -#include "ipstack.h" -#include "link.h" -#include "ipv4.h" -#include "firewall.h" - -#define DEFAULT_TTL 32 - -// === IMPORTS === -extern tInterface *gIP_Interfaces; -extern void ICMP_Initialise(); -extern int ICMP_Ping(tInterface *Interface, tIPv4 Addr); -extern tMacAddr ARP_Resolve4(tInterface *Interface, tIPv4 Address); - -// === PROTOTYPES === - int IPv4_Initialise(); - int IPv4_RegisterCallback(int ID, tIPCallback Callback); -void IPv4_int_GetPacket(tAdapter *Interface, tMacAddr From, int Length, void *Buffer); -tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast); -Uint32 IPv4_Netmask(int FixedBits); -Uint16 IPv4_Checksum(const void *Buf, size_t Length); - int IPv4_Ping(tInterface *Iface, tIPv4 Addr); - -// === GLOBALS === -tIPCallback gaIPv4_Callbacks[256]; - -// === CODE === -/** - * \brief Initialise the IPv4 Code - */ -int IPv4_Initialise() -{ - ICMP_Initialise(); - Link_RegisterType(IPV4_ETHERNET_ID, IPv4_int_GetPacket); - return 1; -} - -/** - * \brief Registers a callback - * \param ID 8-bit packet type ID - * \param Callback Callback function - */ -int IPv4_RegisterCallback(int ID, tIPCallback Callback) -{ - if( ID < 0 || ID > 255 ) return 0; - if( gaIPv4_Callbacks[ID] ) return 0; - gaIPv4_Callbacks[ID] = Callback; - return 1; -} - -/** - * \brief Creates and sends an IPv4 Packet - * \param Iface Interface - * \param Address Destination IP - * \param Protocol Protocol ID - * \param ID Some random ID number - * \param Length Data Length - * \param Data Packet Data - * \return Boolean Success - */ -int IPv4_SendPacket(tInterface *Iface, tIPv4 Address, int Protocol, int ID, int Length, const void *Data) -{ - tMacAddr to; - int bufSize = sizeof(tIPv4Header) + Length; - char buf[bufSize]; - tIPv4Header *hdr = (void*)buf; - int ret; - - to = ARP_Resolve4(Iface, Address); - if( MAC_EQU(to, cMAC_ZERO) ) { - // No route to host - Log_Notice("IPv4", "No route to host %i.%i.%i.%i", - Address.B[0], Address.B[1], Address.B[2], Address.B[3]); - return 0; - } - - // OUTPUT Firewall rule go here - ret = IPTables_TestChain("OUTPUT", - 4, (tIPv4*)Iface->Address, &Address, - Protocol, 0, - Length, Data); - if(ret > 0) { - // Just drop it (with an error) - Log_Notice("IPv4", "Firewall dropped packet"); - return 0; - } - - memcpy(&hdr->Options[0], Data, Length); - hdr->Version = 4; - hdr->HeaderLength = sizeof(tIPv4Header)/4; - hdr->DiffServices = 0; // TODO: Check - - hdr->Reserved = 0; - hdr->DontFragment = 0; - hdr->MoreFragments = 0; - hdr->FragOffLow = 0; - hdr->FragOffHi = 0; - - hdr->TotalLength = htons( bufSize ); - hdr->Identifcation = htons( ID ); // TODO: Check - hdr->TTL = DEFAULT_TTL; - hdr->Protocol = Protocol; - hdr->HeaderChecksum = 0; // Will be set later - hdr->Source = *(tIPv4*)Iface->Address; - hdr->Destination = Address; - hdr->HeaderChecksum = htons( IPv4_Checksum(hdr, sizeof(tIPv4Header)) ); - - Log_Log("IPv4", "Sending packet to %i.%i.%i.%i", - Address.B[0], Address.B[1], Address.B[2], Address.B[3]); - Link_SendPacket(Iface->Adapter, IPV4_ETHERNET_ID, to, bufSize, buf); - return 1; -} - -/** - * \fn void IPv4_int_GetPacket(tInterface *Adapter, tMacAddr From, int Length, void *Buffer) - * \brief Process an IPv4 Packet - */ -void IPv4_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer) -{ - tIPv4Header *hdr = Buffer; - tInterface *iface; - Uint8 *data; - int dataLength; - int ret; - - if(Length < sizeof(tIPv4Header)) return; - - #if 0 - //Log_Log("IPv4", "Version = %i", hdr->Version); - //Log_Log("IPv4", "HeaderLength = %i", hdr->HeaderLength); - //Log_Log("IPv4", "DiffServices = %i", hdr->DiffServices); - Log_Debug("IPv4", "TotalLength = %i", ntohs(hdr->TotalLength) ); - //Log_Log("IPv4", "Identifcation = %i", ntohs(hdr->Identifcation) ); - //Log_Log("IPv4", "TTL = %i", hdr->TTL ); - Log_Debug("IPv4", "Protocol = %i", hdr->Protocol ); - //Log_Log("IPv4", "HeaderChecksum = 0x%x", ntohs(hdr->HeaderChecksum) ); - Log_Debug("IPv4", "Source = %i.%i.%i.%i", - hdr->Source.B[0], hdr->Source.B[1], hdr->Source.B[2], hdr->Source.B[3] ); - Log_Debug("IPv4", "Destination = %i.%i.%i.%i", - hdr->Destination.B[0], hdr->Destination.B[1], - hdr->Destination.B[2], hdr->Destination.B[3] ); - #endif - - // Check that the version IS IPv4 - if(hdr->Version != 4) { - Log_Log("IPv4", "hdr->Version(%i) != 4", hdr->Version); - return; - } - - // Check Header checksum - { - Uint16 hdrVal, compVal; - hdrVal = ntohs(hdr->HeaderChecksum); - hdr->HeaderChecksum = 0; - compVal = IPv4_Checksum(hdr, hdr->HeaderLength * 4); - if(hdrVal != compVal) { - Log_Log("IPv4", "Header checksum fails (%04x != %04x)", hdrVal, compVal); - return ; - } - hdr->HeaderChecksum = hdrVal; - } - - // Check Packet length - if( ntohs(hdr->TotalLength) > Length) { - Log_Log("IPv4", "hdr->TotalLength(%i) > Length(%i)", ntohs(hdr->TotalLength), Length); - return; - } - - // TODO: Handle packet fragmentation - - - Log_Debug("IPv4", " From %i.%i.%i.%i to %i.%i.%i.%i", - hdr->Source.B[0], hdr->Source.B[1], hdr->Source.B[2], hdr->Source.B[3], - hdr->Destination.B[0], hdr->Destination.B[1], hdr->Destination.B[2], hdr->Destination.B[3] - ); - - // Get Data and Data Length - dataLength = ntohs(hdr->TotalLength) - sizeof(tIPv4Header); - data = &hdr->Options[0]; - - // Get Interface (allowing broadcasts) - iface = IPv4_GetInterface(Adapter, hdr->Destination, 1); - - // Firewall rules - if( iface ) { - // Incoming Packets - ret = IPTables_TestChain("INPUT", - 4, &hdr->Source, &hdr->Destination, - hdr->Protocol, 0, - dataLength, data - ); - } - else { - // Routed packets - ret = IPTables_TestChain("FORWARD", - 4, &hdr->Source, &hdr->Destination, - hdr->Protocol, 0, - dataLength, data - ); - } - switch(ret) - { - // 0 - Allow - case 0: break; - // 1 - Silent Drop - case 1: - Log_Debug("IPv4", "Silently dropping packet"); - return ; - case -1: - // Bad rule - break ; - // Unknown, silent drop - default: - Log_Warning("IPv4", "Unknown firewall rule"); - return ; - } - - // Routing - if(!iface) - { - tMacAddr to; - tRoute *rt; - - Log_Debug("IPv4", "Route the packet"); - // Drop the packet if the TTL is zero - if( hdr->TTL == 0 ) { - Log_Warning("IPv4", "TODO: Send ICMP-Timeout when TTL exceeded"); - return ; - } - - hdr->TTL --; - - rt = IPStack_FindRoute(4, NULL, &hdr->Destination); // Get the route (gets the interface) - if( !rt || !rt->Interface ) - return ; - to = ARP_Resolve4(rt->Interface, hdr->Destination); // Resolve address - if( MAC_EQU(to, cMAC_ZERO) ) - return ; - - // Send packet - Log_Log("IPv4", "Forwarding packet to %i.%i.%i.%i (via %i.%i.%i.%i)", - hdr->Destination.B[0], hdr->Destination.B[1], - hdr->Destination.B[2], hdr->Destination.B[3], - ((tIPv4*)rt->NextHop)->B[0], ((tIPv4*)rt->NextHop)->B[1], - ((tIPv4*)rt->NextHop)->B[2], ((tIPv4*)rt->NextHop)->B[3]); - Link_SendPacket(rt->Interface->Adapter, IPV4_ETHERNET_ID, to, Length, Buffer); - - - return ; - } - - // Send it on - if( !gaIPv4_Callbacks[hdr->Protocol] ) { - Log_Log("IPv4", "Unknown Protocol %i", hdr->Protocol); - return ; - } - - gaIPv4_Callbacks[hdr->Protocol]( iface, &hdr->Source, dataLength, data ); -} - -/** - * \fn tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address) - * \brief Searches an adapter for a matching address - * \param Adapter Incoming Adapter - * \param Address Destination Address - * \param Broadcast Allow broadcast packets - */ -tInterface *IPv4_GetInterface(tAdapter *Adapter, tIPv4 Address, int Broadcast) -{ - tInterface *iface = NULL; - Uint32 netmask; - Uint32 addr, this; - - ENTER("pAdapter xAddress bBroadcast", Adapter, Address, Broadcast); - - addr = ntohl( Address.L ); - LOG("addr = 0x%x", addr); - - for( iface = gIP_Interfaces; iface; iface = iface->Next) - { - if( iface->Adapter != Adapter ) continue; - if( iface->Type != 4 ) continue; - if( IP4_EQU(Address, *(tIPv4*)iface->Address) ) { - LOG("Exact match"); - LEAVE('p', iface); - return iface; - } - - if( !Broadcast ) continue; - - // Check for broadcast - this = ntohl( ((tIPv4*)iface->Address)->L ); - netmask = IPv4_Netmask(iface->SubnetBits); - LOG("iface addr = 0x%x, netmask = 0x%x (bits = %i)", this, netmask, iface->SubnetBits); - - if( (addr & netmask) == (this & netmask) && (addr & ~netmask) == (0xFFFFFFFF & ~netmask) ) - { - LOG("Broadcast match"); - LEAVE('p', iface); - return iface; - } - } - LEAVE('n'); - return NULL; -} - -/** - * \brief Convert a network prefix to a netmask - * \param FixedBits Netmask size (/n) - * - * For example /24 will become 255.255.255.0 (0xFFFFFF00) - */ -Uint32 IPv4_Netmask(int FixedBits) -{ - Uint32 ret = 0xFFFFFFFF; - if( FixedBits == 0 ) - return 0; - if( FixedBits < 32 ) - { - ret >>= (32-FixedBits); - ret <<= (32-FixedBits); - } - // Returns a native endian netmask - return ret; -} - -/** - * \brief Calculate the IPv4 Checksum - * \param Buf Input buffer - * \param Size Size of input - * - * One's complement sum of all 16-bit words (bitwise inverted) - */ -Uint16 IPv4_Checksum(const void *Buf, size_t Length) -{ - const Uint16 *words = Buf; - Uint32 sum = 0; - int i; - - // Sum all whole words - for(i = 0; i < Length/2; i++ ) - { - sum += ntohs(words[i]); - } - if( Length & 1 ) - sum += ntohs( words[i] & 0xFF ); - - // Apply one's complement - while (sum >> 16) - sum = (sum & 0xFFFF) + (sum >> 16); - - return ~sum; -} - -/** - * \brief Sends an ICMP Echo and waits for a reply - * \param IFace Interface - * \param Addr Destination address - */ -int IPv4_Ping(tInterface *IFace, tIPv4 Addr) -{ - return ICMP_Ping(IFace, Addr); -} diff --git a/Modules/IPStack/ipv4.h b/Modules/IPStack/ipv4.h deleted file mode 100644 index 2c3e1cae..00000000 --- a/Modules/IPStack/ipv4.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Acess2 IP Stack - * - IPv4 Definitions - */ -#ifndef _IPV4_H_ -#define _IPV4_H_ - -#include "ipstack.h" - -typedef struct sIPv4Header tIPv4Header; - -struct sIPv4Header -{ - struct { - // Spec says Version is first, but stupid bit ordering - unsigned HeaderLength: 4; // in 4-byte chunks - unsigned Version: 4; // = 4 - } __attribute__((packed)); - Uint8 DiffServices; // Differentiated Services - Uint16 TotalLength; - Uint16 Identifcation; - - struct { - unsigned Reserved: 1; - unsigned DontFragment: 1; - unsigned MoreFragments: 1; - unsigned FragOffLow: 5; - } __attribute__((packed)); - Uint8 FragOffHi; // Number of 8-byte blocks from the original start - - Uint8 TTL; // Max number of hops, effectively - Uint8 Protocol; - Uint16 HeaderChecksum; // One's Complement Sum of the entire header must equal zero - - tIPv4 Source; - tIPv4 Destination; - - Uint8 Options[]; -} __attribute__((packed)); - -#define IP4PROT_ICMP 1 -#define IP4PROT_TCP 6 -#define IP4PROT_UDP 17 - -#define IPV4_ETHERNET_ID 0x0800 - -// === FUNCTIONS === -extern int IPv4_RegisterCallback(int ID, tIPCallback Callback); -extern Uint16 IPv4_Checksum(const void *Buf, size_t Length); -extern int IPv4_SendPacket(tInterface *Iface, tIPv4 Address, int Protocol, int ID, int Length, const void *Data); - -#endif diff --git a/Modules/IPStack/ipv6.c b/Modules/IPStack/ipv6.c deleted file mode 100644 index 7cb8e0a8..00000000 --- a/Modules/IPStack/ipv6.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Acess2 IP Stack - * - IPv6 Protcol Handling - */ -#include "ipstack.h" -#include "link.h" -#include "ipv6.h" -#include "firewall.h" - -// === IMPORTS === -extern tInterface *gIP_Interfaces; -extern Uint32 IPv4_Netmask(int FixedBits); - -// === PROTOTYPES === - int IPv6_Initialise(); - int IPv6_RegisterCallback(int ID, tIPCallback Callback); -void IPv6_int_GetPacket(tAdapter *Interface, tMacAddr From, int Length, void *Buffer); -tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast); - -// === GLOBALS === -tIPCallback gaIPv6_Callbacks[256]; - -// === CODE === -/** - * \brief Initialise the IPv6 handling code - */ -int IPv6_Initialise() -{ - Link_RegisterType(IPV6_ETHERNET_ID, IPv6_int_GetPacket); - return 1; -} - -/** - * \brief Registers a callback - * \param ID 8-bit packet type ID - * \param Callback Callback function - */ -int IPv6_RegisterCallback(int ID, tIPCallback Callback) -{ - if( ID < 0 || ID > 255 ) return 0; - if( gaIPv6_Callbacks[ID] ) return 0; - gaIPv6_Callbacks[ID] = Callback; - return 1; -} - -/** - * \brief Creates and sends an IPv6 Packet - * \param Iface Interface - * \param Destination Destination IP - * \param Protocol Protocol ID - * \param Length Data Length - * \param Data Packet Data - * \return Boolean Success - */ -int IPv6_SendPacket(tInterface *Iface, tIPv6 Destination, int Protocol, size_t Length, const void *Data) -{ - return 0; -} - -/** - * \fn void IPv6_int_GetPacket(tInterface *Interface, tMacAddr From, int Length, void *Buffer) - * \brief Process an IPv6 Packet - * \param Interface Input interface - * \param From Source MAC address - * \param Length Packet length - * \param Buffer Packet data - */ -void IPv6_int_GetPacket(tAdapter *Adapter, tMacAddr From, int Length, void *Buffer) -{ - tInterface *iface; - tIPv6Header *hdr = Buffer; - int ret, dataLength; - char *dataPtr; - Uint8 nextHeader; - - if(Length < sizeof(tIPv6Header)) return; - - hdr->Head = ntohl(hdr->Head); - - //if( ((hdr->Head >> (20+8)) & 0xF) != 6 ) - if( hdr->Version != 6 ) - return; - - #if 1 - Log_Debug("IPv6", "hdr = {"); - Log_Debug("IPv6", " .Version = %i", hdr->Version ); - Log_Debug("IPv6", " .TrafficClass = %i", hdr->TrafficClass ); - Log_Debug("IPv6", " .FlowLabel = %i", hdr->FlowLabel ); - Log_Debug("IPv6", " .PayloadLength = 0x%04x", ntohs(hdr->PayloadLength) ); - Log_Debug("IPv6", " .NextHeader = 0x%02x", hdr->NextHeader ); - Log_Debug("IPv6", " .HopLimit = 0x%02x", hdr->HopLimit ); - Log_Debug("IPv6", " .Source = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", hdr->Source ); - Log_Debug("IPv6", " .Destination = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", hdr->Destination ); - Log_Debug("IPv6", "}"); - #endif - - // No checksum in IPv6 - - // Check Packet length - if( ntohs(hdr->PayloadLength)+sizeof(tIPv6Header) > Length) { - Log_Log("IPv6", "hdr->PayloadLength(%i) > Length(%i)", ntohs(hdr->PayloadLength), Length); - return; - } - - // Process Options - nextHeader = hdr->NextHeader; - dataPtr = hdr->Data; - dataLength = hdr->PayloadLength; - for( ;; ) - { - struct { - Uint8 NextHeader; - Uint8 Length; // In 8-byte chunks, with 0 being 8 bytes long - Uint8 Data[]; - } *optionHdr; - optionHdr = (void*)dataPtr; - // Hop-by-hop options - if(nextHeader == 0) - { - // TODO: Parse the options (actually, RFC2460 doesn't specify any) - } - // Routing Options - else if(nextHeader == 43) - { - // TODO: Routing Header options - } - else - { - break; // Unknown, pass on - } - nextHeader = optionHdr->NextHeader; - dataPtr += (optionHdr->Length + 1) * 8; // 8-octet length (0 = 8 bytes long) - } - - // Get Interface (allowing broadcasts) - iface = IPv6_GetInterface(Adapter, hdr->Destination, 1); - - // Firewall rules - if( iface ) { - // Incoming Packets - ret = IPTables_TestChain("INPUT", - 6, &hdr->Source, &hdr->Destination, - hdr->NextHeader, 0, - hdr->PayloadLength, hdr->Data - ); - } - else { - // Routed packets - ret = IPTables_TestChain("FORWARD", - 6, &hdr->Source, &hdr->Destination, - hdr->NextHeader, 0, - hdr->PayloadLength, hdr->Data - ); - } - - switch(ret) - { - // 0 - Allow - case 0: break; - // 1 - Silent Drop - case 1: - Log_Debug("IPv6", "Silently dropping packet"); - return ; - // Unknown, silent drop - default: - return ; - } - - // Routing - if(!iface) - { - #if 0 - tMacAddr to; - tRoute *rt; - - Log_Debug("IPv6", "Route the packet"); - // Drop the packet if the TTL is zero - if( hdr->HopLimit == 0 ) { - Log_Warning("IPv6", "TODO: Sent ICMP-Timeout when TTL exceeded"); - return ; - } - - hdr->HopLimit --; - - rt = IPStack_FindRoute(6, NULL, &hdr->Destination); // Get the route (gets the interface) - to = ICMP6_ResolveHWAddr(rt->Interface, hdr->Destination); // Resolve address - - // Send packet - Log_Log("IPv6", "Forwarding packet"); - Link_SendPacket(rt->Interface->Adapter, IPV6_ETHERNET_ID, to, Length, Buffer); - #endif - - return ; - } - - // Send it on - if( !gaIPv6_Callbacks[hdr->NextHeader] ) { - Log_Log("IPv6", "Unknown Protocol %i", hdr->NextHeader); - return ; - } - - gaIPv6_Callbacks[hdr->NextHeader]( iface, &hdr->Source, hdr->PayloadLength, hdr->Data ); -} - -/** - * \fn tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address) - * \brief Searches an adapter for a matching address - * \param Adapter Source adapter - * \param Address Destination Address - * \param Broadcast Allow broadcast? - */ -tInterface *IPv6_GetInterface(tAdapter *Adapter, tIPv6 Address, int Broadcast) -{ - int i, j; - tInterface *iface = NULL; - Uint32 netmask; - - for( iface = gIP_Interfaces; iface; iface = iface->Next) - { - tIPv6 *thisAddr; - - // Check for this adapter - if( iface->Adapter != Adapter ) continue; - - // Skip non-IPv6 Interfaces - if( iface->Type != 6 ) continue; - - thisAddr = (tIPv6*)iface->Address; - // If the address is a perfect match, return this interface - if( IP6_EQU(Address, *thisAddr) ) return iface; - - // Check if we want to match broadcast addresses - if( !Broadcast ) continue; - - // Check for broadcast - // - Check first DWORDs - if( iface->SubnetBits > 32 && Address.L[0] != thisAddr->L[0] ) - continue; - if( iface->SubnetBits > 64 && Address.L[1] != thisAddr->L[1] ) - continue; - if( iface->SubnetBits > 96 && Address.L[2] != thisAddr->L[2] ) - continue; - - // Check final DWORD - j = iface->SubnetBits / 32; - i = iface->SubnetBits % 32; - netmask = IPv4_Netmask( iface->SubnetBits % 32 ); - - // Check the last bit of the netmask - if( (Address.L[j] >> i) != (thisAddr->L[j] >> i) ) continue; - - // Check that the host portion is one - if( (Address.L[j] & ~netmask) != (0xFFFFFFFF & ~netmask) ) continue; - if( j >= 2 && Address.L[3] != 0xFFFFFFFF) continue; - if( j >= 1 && Address.L[2] != 0xFFFFFFFF) continue; - if( j >= 0 && Address.L[1] != 0xFFFFFFFF) continue; - - return iface; - } - return NULL; -} diff --git a/Modules/IPStack/ipv6.h b/Modules/IPStack/ipv6.h deleted file mode 100644 index d2e4f28d..00000000 --- a/Modules/IPStack/ipv6.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Acess2 IP Stack - * - IPv6 Definitions - */ -#ifndef _IPV6_H_ -#define _IPV6_H_ - -#include "ipstack.h" - -typedef struct sIPv6Header tIPv6Header; - -struct sIPv6Header -{ - #if 0 - // High 4: Version - // Next 8: Traffic Class - // Low 20: Flow Label - Uint32 Head; - #else - union { - Uint32 Head; // Allow a ntohl to happen - struct { - unsigned Version: 4; - unsigned TrafficClass: 8; - unsigned FlowLabel: 20; - } PACKED; - } PACKED; - #endif - Uint16 PayloadLength; - Uint8 NextHeader; // Type of payload data - Uint8 HopLimit; - tIPv6 Source; - tIPv6 Destination; - char Data[]; -}; - -#define IPV6_ETHERNET_ID 0x86DD - -extern int IPv6_RegisterCallback(int ID, tIPCallback Callback); -extern int IPv6_SendPacket(tInterface *Iface, tIPv6 Destination, int Protocol, size_t Length, const void *Data); - -#endif diff --git a/Modules/IPStack/link.c b/Modules/IPStack/link.c deleted file mode 100644 index 8bca51fc..00000000 --- a/Modules/IPStack/link.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Acess2 IP Stack - * - Link/Media Layer Interface - */ -#include "ipstack.h" -#include "link.h" - -// === CONSTANTS === -#define MAX_PACKET_SIZE 2048 - -// === PROTOTYPES === -void Link_RegisterType(Uint16 Type, tPacketCallback Callback); -void Link_InitCRC(); -Uint32 Link_CalculateCRC(void *Data, int Length); -void Link_SendPacket(tAdapter *Adapter, Uint16 Type, tMacAddr To, int Length, void *Buffer); -void Link_WatchDevice(tAdapter *Adapter); - -// === GLOBALS === - int giRegisteredTypes = 0; - int giRegisteredTypeSpace = 0; -struct { - Uint16 Type; - tPacketCallback Callback; -} *gaRegisteredTypes; - int gbLink_CRCTableGenerated = 0; -Uint32 gaiLink_CRCTable[256]; - -// === CODE === -/** - * \fn void Link_RegisterType(Uint16 Type, tPacketCallback Callback) - * \brief Registers a callback for a specific packet type - * - * \todo Make thread safe (place a mutex on the list) - */ -void Link_RegisterType(Uint16 Type, tPacketCallback Callback) -{ - int i; - void *tmp; - - for( i = giRegisteredTypes; i -- ; ) - { - if(gaRegisteredTypes[i].Type == Type) { - Log_Warning("Net Link", "Attempt to register 0x%x twice", Type); - return ; - } - // Ooh! Free slot! - if(gaRegisteredTypes[i].Callback == NULL) break; - } - - if(i == -1) - { - giRegisteredTypeSpace += 5; - tmp = realloc(gaRegisteredTypes, giRegisteredTypeSpace*sizeof(*gaRegisteredTypes)); - if(!tmp) { - Log_Warning("Net Link", - "Out of heap space! (Attempted to allocate %i)", - giRegisteredTypeSpace*sizeof(*gaRegisteredTypes) - ); - return ; - } - gaRegisteredTypes = tmp; - i = giRegisteredTypes; - giRegisteredTypes ++; - } - - gaRegisteredTypes[i].Callback = Callback; - gaRegisteredTypes[i].Type = Type; -} - -/** - * \fn void Link_SendPacket(tAdapter *Adapter, Uint16 Type, tMacAddr To, int Length, void *Buffer) - * \brief Formats and sends a packet on the specified interface - */ -void Link_SendPacket(tAdapter *Adapter, Uint16 Type, tMacAddr To, int Length, void *Buffer) -{ - int bufSize = sizeof(tEthernetHeader) + ((Length+3)&~3) + 4; - Uint8 buf[bufSize]; // dynamic stack arrays ftw! - tEthernetHeader *hdr = (void*)buf; - - Log_Log("Net Link", "Sending %i bytes to %02x:%02x:%02x:%02x:%02x:%02x (Type 0x%x)", - Length, To.B[0], To.B[1], To.B[2], To.B[3], To.B[4], To.B[5], Type); - - hdr->Dest = To; - hdr->Src = Adapter->MacAddr; - hdr->Type = htons(Type); - - memcpy(hdr->Data, Buffer, Length); - - *(Uint32*) &hdr->Data[bufSize-4] = 0; - *(Uint32*) &hdr->Data[bufSize-4] = htonl( Link_CalculateCRC(buf, bufSize) ); - - VFS_Write(Adapter->DeviceFD, bufSize, buf); -} - -void Link_WorkerThread(void *Ptr) -{ - tAdapter *Adapter = Ptr; - - Threads_SetName(Adapter->Device); - Log_Log("Net Link", "Thread %i watching '%s'", Threads_GetTID(), Adapter->Device); - - // Child Thread - while(Adapter->DeviceFD != -1) - { - Uint8 buf[MAX_PACKET_SIZE]; - tEthernetHeader *hdr = (void*)buf; - int ret, i; - Uint32 checksum; - - // Wait for a packet (Read on a network device is blocking) - //Log_Debug("NET", "Waiting on adapter FD#0x%x", Adapter->DeviceFD); - ret = VFS_Read(Adapter->DeviceFD, MAX_PACKET_SIZE, buf); - if(ret == -1) break; - - if(ret < sizeof(tEthernetHeader)) { - Log_Log("Net Link", "Recieved an undersized packet (%i < %i)", - ret, sizeof(tEthernetHeader)); - continue; - } - - Log_Log("Net Link", - "Packet from %02x:%02x:%02x:%02x:%02x:%02x" - " to %02x:%02x:%02x:%02x:%02x:%02x (Type=%04x)", - hdr->Src.B[0], hdr->Src.B[1], hdr->Src.B[2], - hdr->Src.B[3], hdr->Src.B[4], hdr->Src.B[5], - hdr->Dest.B[0], hdr->Dest.B[1], hdr->Dest.B[2], - hdr->Dest.B[3], hdr->Dest.B[4], hdr->Dest.B[5], - ntohs(hdr->Type) - ); - checksum = *(Uint32*)&hdr->Data[ret-sizeof(tEthernetHeader)-4]; - //Log_Log("NET", "Checksum 0x%08x", checksum); - // TODO: Check checksum - - // Check if there is a registered callback for this packet type - for( i = giRegisteredTypes; i--; ) - { - if(gaRegisteredTypes[i].Type == ntohs(hdr->Type)) break; - } - // No? Ignore it - if( i == -1 ) { - Log_Log("Net Link", "Unregistered type 0x%x", ntohs(hdr->Type)); - continue; - } - - // Call the callback - gaRegisteredTypes[i].Callback( - Adapter, - hdr->Src, - ret - sizeof(tEthernetHeader), - hdr->Data - ); - } - - Log_Log("Net Link", "Watcher terminated (file closed)"); - - Threads_Exit(0, 0); -} - -/** - * \fn void Link_WatchDevice(tAdapter *Adapter) - * \brief Spawns a worker thread to watch the specified adapter - */ -void Link_WatchDevice(tAdapter *Adapter) -{ - int tid; - - if( !gbLink_CRCTableGenerated ) - Link_InitCRC(); - - tid = Proc_SpawnWorker(Link_WorkerThread, Adapter); // Create a new worker thread - - if(tid < 0) { - Log_Warning("Net Link", "Unable to create watcher thread for '%s'", Adapter->Device); - return ; - } - - Log_Log("Net Link", "Watching '%s' using tid %i", Adapter->Device, tid); -} - -// From http://www.cl.cam.ac.uk/research/srg/bluebook/21/crc/node6.html -#define QUOTIENT 0x04c11db7 -void Link_InitCRC(void) -{ - int i, j; - Uint32 crc; - - for (i = 0; i < 256; i++) - { - crc = i << 24; - for (j = 0; j < 8; j++) - { - if (crc & 0x80000000) - crc = (crc << 1) ^ QUOTIENT; - else - crc = crc << 1; - } - gaiLink_CRCTable[i] = crc; - } - - gbLink_CRCTableGenerated = 1; -} - -Uint32 Link_CalculateCRC(void *Data, int Length) -{ - // x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1 - Uint32 result; - int i; - Uint32 *data = Data; - - if(Length < 4) return 0; - - result = *data++ << 24; - result |= *data++ << 16; - result |= *data++ << 8; - result |= *data++; - result = ~ result; - Length -= 4; - - for( i = 0; i < Length; i++ ) - { - result = (result << 8 | *data++) ^ gaiLink_CRCTable[result >> 24]; - } - - return ~result; -} diff --git a/Modules/IPStack/link.h b/Modules/IPStack/link.h deleted file mode 100644 index d96008d4..00000000 --- a/Modules/IPStack/link.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Acess2 IP Stack - * - Link/Media Layer Header - */ -#ifndef _LINK_H_ -#define _LINK_H_ - -// === EXTERNAL === -typedef void (*tPacketCallback)(tAdapter *Interface, tMacAddr From, int Length, void *Buffer); - -extern void Link_RegisterType(Uint16 Type, tPacketCallback Callback); -extern void Link_SendPacket(tAdapter *Interface, Uint16 Type, tMacAddr To, int Length, void *Buffer); -extern void Link_WatchDevice(tAdapter *Adapter); - -// === INTERNAL === -typedef struct sEthernetHeader tEthernetHeader; -typedef struct sEthernetFooter tEthernetFooter; -struct sEthernetHeader { - tMacAddr Dest; - tMacAddr Src; - Uint16 Type; - Uint8 Data[]; -}; - -struct sEthernetFooter { - //Uint32 CRC; -}; - -#endif diff --git a/Modules/IPStack/main.c b/Modules/IPStack/main.c deleted file mode 100644 index d5d1ebc2..00000000 --- a/Modules/IPStack/main.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Acess2 IP Stack - * - Stack Initialisation - */ -#define DEBUG 0 -#define VERSION VER2(0,10) -#include "ipstack.h" -#include "link.h" -#include -#include - -// === IMPORTS === -extern int ARP_Initialise(); -extern void UDP_Initialise(); -extern void TCP_Initialise(); -extern int IPv4_Initialise(); -extern int IPv6_Initialise(); - -extern tAdapter *IPStack_GetAdapter(const char *Path); -extern char *IPStack_Root_ReadDir(tVFS_Node *Node, int Pos); -extern tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Name); -extern int IPStack_Root_IOCtl(tVFS_Node *Node, int ID, void *Data); -extern tInterface gIP_LoopInterface; -extern tInterface *IPStack_AddInterface(const char *Device, const char *Name); -extern tRoute *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric); - -// === PROTOTYPES === - int IPStack_Install(char **Arguments); - int IPStack_CompareAddress(int AddressType, const void *Address1, const void *Address2, int CheckBits); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, IPStack, IPStack_Install, NULL, NULL); -tVFS_NodeType gIP_RootNodeType = { - .ReadDir = IPStack_Root_ReadDir, - .FindDir = IPStack_Root_FindDir, - .IOCtl = IPStack_Root_IOCtl -}; -tDevFS_Driver gIP_DriverInfo = { - NULL, "ip", - { - .Size = -1, // Number of interfaces - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Flags = VFS_FFLAG_DIRECTORY, - .Type = &gIP_RootNodeType - } -}; - -// === CODE === -/** - * \fn int IPStack_Install(char **Arguments) - * \brief Intialise the relevant parts of the stack and register with DevFS - */ -int IPStack_Install(char **Arguments) -{ - int i = 0; - - // Layer 3 - Network Layer Protocols - ARP_Initialise(); - IPv4_Initialise(); - IPv6_Initialise(); - // Layer 4 - Transport Layer Protocols - TCP_Initialise(); - UDP_Initialise(); - - if(Arguments) - { - // Parse module arguments - for( i = 0; Arguments[i]; i++ ) - { - // TODO: - // Define interfaces by ::: - // Where: - // - is the device path (E.g. /Devices/ne2k/0) - // - is a number (e.g. 4) or symbol (e.g. AF_INET4) - // - is a condensed hexadecimal stream (in big endian) - // (E.g. 0A000201 for 10.0.2.1 IPv4) - // - is the number of subnet bits (E.g. 24 for an IPv4 Class C) - // Example: /Devices/ne2k/0:4:0A00020A:24 - // would define an interface with the address 10.0.2.10/24 - if( Arguments[i][0] == '/' ) { - // Define Interface - char *dev, *type, *addr, *bits; - - // Read definition - dev = Arguments[i]; - type = strchr(dev, ':'); - if( !type ) { - Log_Warning("IPStack", ":::"); - continue; - } - *type = '\0'; type ++; - - addr = strchr(type, ':'); - if( !addr ) { - Log_Warning("IPStack", ":::"); - continue; - } - *addr = '\0'; addr ++; - - bits = strchr(addr, ':'); - if( !bits ) { - Log_Warning("IPStack", ":::"); - continue; - } - *bits = '\0'; bits ++; - - // Define interface - { - int iType = atoi(type); - int size = IPStack_GetAddressSize(iType); - Uint8 addrData[size]; - int iBits = atoi(bits); - - UnHex(addrData, size, addr); - - tInterface *iface = IPStack_AddInterface(dev, ""); - if( !iface ) { - Log_Warning("IPStack", "Unable to add interface on '%s'", dev); - continue ; - } - iface->Type = iType; - memcpy(iface->Address, addrData, size); - iface->SubnetBits = iBits; - - // Route for addrData/iBits, no next hop, default metric - IPStack_AddRoute(iface->Name, iface->Address, iBits, NULL, 0); - - Log_Notice("IPStack", "Boot interface %s/%i on %s", - IPStack_PrintAddress(iType, addrData), iBits, - dev); - } - - continue; - } - - // I could also define routes using ::[:] - // Example: 1:00000000:0:0A000201 - if( '0' <= Arguments[i][0] && Arguments[i][0] <= '9' ) - { - // Define Interface - char *ifaceName, *network, *bits, *gateway; - - // Read definition - ifaceName = Arguments[i]; - - network = strchr(ifaceName, ':'); - if( !network ) { - Log_Warning("IPStack", ":::"); - continue; - } - *network = '\0'; network ++; - - bits = strchr(network, ':'); - if( !bits ) { - Log_Warning("IPStack", ":::"); - continue; - } - *bits = '\0'; bits ++; - - gateway = strchr(bits, ':'); - if( gateway ) { - *gateway = '\0'; gateway ++; - } - - // Define route - { - tVFS_Node *node = IPStack_Root_FindDir(NULL, ifaceName); - if( !node ) { - Log_Warning("IPStack", "Unknown interface '%s' in arg %i", ifaceName, i); - continue ; - } - tInterface *iface = node->ImplPtr; - - int size = IPStack_GetAddressSize(iface->Type); - Uint8 netData[size]; - Uint8 gwData[size]; - int iBits = atoi(bits); - - UnHex(netData, size, network); - if( gateway ) - UnHex(gwData, size, gateway); - else - memset(gwData, 0, size); - - IPStack_AddRoute(ifaceName, netData, iBits, gwData, 30); - } - - continue; - } - } - } - - // Initialise loopback interface - gIP_LoopInterface.Adapter = IPStack_GetAdapter("LOOPBACK"); - - DevFS_AddDevice( &gIP_DriverInfo ); - - return MODULE_ERR_OK; -} - -/** - * \brief Gets the size (in bytes) of a specified form of address - */ -int IPStack_GetAddressSize(int AddressType) -{ - switch(AddressType) - { - case -1: // -1 = maximum - return sizeof(tIPv6); - - case AF_NULL: - return 0; - - case AF_INET4: - return sizeof(tIPv4); - case AF_INET6: - return sizeof(tIPv6); - - default: - return 0; - } -} - -/** - * \brief Compare two IP Addresses masked by CheckBits - */ -int IPStack_CompareAddress(int AddressType, const void *Address1, const void *Address2, int CheckBits) -{ - int size = IPStack_GetAddressSize(AddressType); - Uint8 mask; - const Uint8 *addr1 = Address1, *addr2 = Address2; - - // Sanity check size - if( CheckBits < 0 ) CheckBits = size*8; - if( CheckBits > size*8 ) CheckBits = size*8; - - if( CheckBits == 0 ) return 1; // /0 matches anything - - // Check first bits/8 bytes - if( memcmp(Address1, Address2, CheckBits/8) != 0 ) return 0; - - // Check if the mask is a multiple of 8 - if( CheckBits % 8 == 0 ) return 1; - - // Check last bits - mask = 0xFF << (8 - (CheckBits % 8)); - if( (addr1[CheckBits/8] & mask) == (addr2[CheckBits/8] & mask) ) - return 1; - - return 0; -} - -const char *IPStack_PrintAddress(int AddressType, const void *Address) -{ - switch( AddressType ) - { - case 4: { - static char ret[4*3+3+1]; - const Uint8 *addr = Address; - sprintf(ret, "%i.%i.%i.%i", addr[0], addr[1], addr[2], addr[3]); - return ret; - } - - case 6: { // TODO: address compression - static char ret[8*4+7+1]; - const Uint16 *addr = Address; - sprintf(ret, "%x:%x:%x:%x:%x:%x:%x:%x", - ntohs(addr[0]), ntohs(addr[1]), ntohs(addr[2]), ntohs(addr[3]), - ntohs(addr[4]), ntohs(addr[5]), ntohs(addr[6]), ntohs(addr[7]) - ); - return ret; - } - - default: - return ""; - } -} diff --git a/Modules/IPStack/routing.c b/Modules/IPStack/routing.c deleted file mode 100644 index 7173d1f3..00000000 --- a/Modules/IPStack/routing.c +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Acess2 IP Stack - * - Routing Tables - */ -#define DEBUG 0 -#define VERSION VER2(0,10) -#include -#include -#include "ipstack.h" -#include "link.h" - -#define DEFAUTL_METRIC 30 - -// === IMPORTS === -extern tInterface *gIP_Interfaces; -extern tVFS_Node *IPStack_Root_FindDir(tVFS_Node *Node, const char *Filename); - -// === PROTOTYPES === -// - Routes directory -char *IPStack_RouteDir_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *IPStack_RouteDir_FindDir(tVFS_Node *Node, const char *Name); - int IPStack_RouteDir_MkNod(tVFS_Node *Node, const char *Name, Uint Flags); - int IPStack_RouteDir_Relink(tVFS_Node *Node, const char *OldName, const char *NewName); -tRoute *_Route_FindExactRoute(int Type, void *Network, int Subnet, int Metric); - int _Route_ParseRouteName(const char *Name, void *Addr, int *SubnetBits, int *Metric); - int IPStack_RouteDir_IOCtl(tVFS_Node *Node, int ID, void *Data); -// - Route Management -tRoute *IPStack_Route_Create(int AddrType, void *Network, int SubnetBits, int Metric); -tRoute *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric); -tRoute *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address); -// - Individual Routes - int IPStack_Route_IOCtl(tVFS_Node *Node, int ID, void *Data); - -// === GLOBALS === - int giIP_NextRouteId = 1; -tRoute *gIP_Routes; -tRoute *gIP_RoutesEnd; -tVFS_NodeType gIP_RouteNodeType = { - .IOCtl = IPStack_Route_IOCtl -}; -tVFS_NodeType gIP_RouteDirNodeType = { - .ReadDir = IPStack_RouteDir_ReadDir, - .FindDir = IPStack_RouteDir_FindDir, - .MkNod = IPStack_RouteDir_MkNod, - .Relink = IPStack_RouteDir_Relink, - .IOCtl = IPStack_RouteDir_IOCtl -}; -tVFS_Node gIP_RouteNode = { - .Flags = VFS_FFLAG_DIRECTORY, - .Size = -1, - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Type = &gIP_RouteDirNodeType -}; - -// === CODE === -/** - * \brief ReadDir for the /Devices/ip/routes/ directory - */ -char *IPStack_RouteDir_ReadDir(tVFS_Node *Node, int Pos) -{ - tRoute *rt; - - for(rt = gIP_Routes; rt && Pos --; rt = rt->Next); - if( !rt ) return NULL; - - { - int addrlen = IPStack_GetAddressSize(rt->AddressType); - int len = sprintf(NULL, "%i::%i:%i", rt->AddressType, rt->SubnetBits, rt->Metric) + addrlen*2; - char buf[len+1]; - int ofs; - ofs = sprintf(buf, "%i:", rt->AddressType); - ofs += Hex(buf+ofs, addrlen, rt->Network); - sprintf(buf+ofs, ":%i:%i", rt->SubnetBits, rt->Metric); - return strdup(buf); - } -} - -/** - * \brief FindDir for the /Devices/ip/routes/ directory - */ -tVFS_Node *IPStack_RouteDir_FindDir(tVFS_Node *Node, const char *Name) -{ - // Interpret the name as :, returning the interface for - // needed to access that address. - // E.g. '@4:0A02000A' - 10.2.0.10 - // Hm... It could do with a way to have a better address type representation - if( Name[0] == '@' ) - { - tRoute *rt; - int ofs = 1; - int type; - - ofs += ParseInt(Name+ofs, &type); - int addrSize = IPStack_GetAddressSize(type); - Uint8 addrData[addrSize]; - - // Separator - if( Name[ofs] != ':' ) return NULL; - ofs ++; - - // Error if the size is invalid - if( strlen(Name+ofs) != addrSize*2 ) - return NULL; - - // Parse the address - // - Error if the address data is not fully hex - if( UnHex(addrData, addrSize, Name + ofs) != addrSize ) - return NULL; - - // Find the route - rt = IPStack_FindRoute(type, NULL, addrData); - if(!rt) return NULL; - - if( rt->Interface ) - { - // Return the interface node - // - Sure it's hijacking it from inteface.c's area, but it's - // simpler this way - return &rt->Interface->Node; - } - else - { - return NULL; - } - } - else if( Name[0] == '#' ) - { - int num, ofs = 1; - - ofs = ParseInt(Name+ofs, &num); - if( ofs == 1 ) return NULL; - if( Name[ofs] != '\0' ) return NULL; - if( num < 0) return NULL; - - for( tRoute *rt = gIP_Routes; rt; rt = rt->Next ) - { - if( rt->Node.Inode > num ) return NULL; - if( rt->Node.Inode == num ) return &rt->Node; - } - return NULL; - } - else - { - int type = _Route_ParseRouteName(Name, NULL, NULL, NULL); - if( type <= 0 ) return NULL; - - int addrSize = IPStack_GetAddressSize(type); - Uint8 addrData[addrSize]; - int subnet_bits, metric; - - _Route_ParseRouteName(Name, addrData, &subnet_bits, &metric); - - tRoute *rt = _Route_FindExactRoute(type, addrData, subnet_bits, metric); - if(rt) return &rt->Node; - return NULL; - } -} - -/** - * \brief Create a new route node - */ -int IPStack_RouteDir_MkNod(tVFS_Node *Node, const char *Name, Uint Flags) -{ - if( Flags ) return -EINVAL; - if( Threads_GetUID() != 0 ) return -EACCES; - - int type = _Route_ParseRouteName(Name, NULL, NULL, NULL); - if( type <= 0 ) return -EINVAL; - - int size = IPStack_GetAddressSize(type); - Uint8 addrdata[size]; - int subnet, metric; - - _Route_ParseRouteName(Name, addrdata, &subnet, &metric); - - // Check for duplicates - if( _Route_FindExactRoute(type, addrdata, subnet, metric) ) - return -EEXIST; - - IPStack_Route_Create(type, addrdata, subnet, metric); - - return 0; -} - -/** - * \brief Rename / Delete a route - */ -int IPStack_RouteDir_Relink(tVFS_Node *Node, const char *OldName, const char *NewName) -{ - tRoute *rt; - - if( Threads_GetUID() != 0 ) return -EACCES; - - // Get the original route entry - { - int type = _Route_ParseRouteName(OldName, NULL, NULL, NULL); - if(type <= 0) return -EINVAL; - Uint8 addr[IPStack_GetAddressSize(type)]; - int subnet, metric; - _Route_ParseRouteName(OldName, addr, &subnet, &metric); - - rt = _Route_FindExactRoute(type, addr, subnet, metric); - } - - if( NewName == NULL ) - { - // Delete the route - tRoute *prev = NULL; - for(tRoute *r = gIP_Routes; r && r != rt; prev = r, r = r->Next); - - if(prev) - prev->Next = rt->Next; - else - gIP_Routes = rt->Next; - free(rt); - } - else - { - // Change the route - int type = _Route_ParseRouteName(NewName, NULL, NULL, NULL); - if(type <= 0) return -EINVAL; - Uint8 addr[IPStack_GetAddressSize(type)]; - int subnet, metric; - _Route_ParseRouteName(NewName, addr, &subnet, &metric); - - return -ENOTIMPL; - } - return 0; -} - -tRoute *_Route_FindExactRoute(int Type, void *Network, int Subnet, int Metric) -{ - for(tRoute *rt = gIP_Routes; rt; rt = rt->Next) - { - if( rt->AddressType != Type ) continue; - if( rt->Metric != Metric ) continue; - if( rt->SubnetBits != Subnet ) continue; - if( IPStack_CompareAddress(Type, rt->Network, Network, -1) == 0 ) - continue ; - return rt; - } - return NULL; -} - -int _Route_ParseRouteName(const char *Name, void *Addr, int *SubnetBits, int *Metric) -{ - int type, addrlen; - int ofs = 0, ilen; - - ENTER("sName pAddr pSubnetBits pMetric", Name, Addr, SubnetBits, Metric); - - ilen = ParseInt(Name, &type); - if(ilen == 0) { - LOG("Type failed to parse"); - LEAVE_RET('i', -1); - } - ofs += ilen; - - if(Name[ofs] != ':') LEAVE_RET('i', -1); - ofs ++; - - addrlen = IPStack_GetAddressSize(type); - if( Addr ) - { - if( UnHex(Addr, addrlen, Name + ofs) != addrlen ) - return -1; - } - ofs += addrlen*2; - - if(Name[ofs] != ':') LEAVE_RET('i', -1); - ofs ++; - - ilen = ParseInt(Name+ofs, SubnetBits); - if(ilen == 0) { - LOG("Subnet failed to parse"); - LEAVE_RET('i', -1); - } - ofs += ilen; - - if(Name[ofs] != ':') LEAVE_RET('i', -1); - ofs ++; - - ilen = ParseInt(Name+ofs, Metric); - if(ilen == 0) { - LOG("Metric failed to parse"); - LEAVE_RET('i', -1); - } - ofs += ilen; - - if(Name[ofs] != '\0') LEAVE_RET('i', -1); - - LEAVE('i', type); - return type; -} - -/** - * \brief Names for the route list IOCtl Calls - */ -static const char *casIOCtls_RouteDir[] = { - DRV_IOCTLNAMES, - "locate_route", // Find the best route for an address - struct {int Type, char Address[]} * - NULL - }; - -/** - * \brief IOCtl for /Devices/ip/routes/ - */ -int IPStack_RouteDir_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - tRoute *rt; - ENTER("pNode iID pData", Node, ID, Data); - switch(ID) - { - // --- Standard IOCtls (0-3) --- - BASE_IOCTLS(DRV_TYPE_MISC, STR(IDENT), VERSION, casIOCtls_RouteDir) - - case 4: // Locate Route - { - struct { - int Type; - Uint8 Addr[]; - } *data = Data; - - if( !CheckMem(Data, sizeof(int)) ) - LEAVE_RET('i', -1); - if( !CheckMem(Data, sizeof(int) + IPStack_GetAddressSize(data->Type)) ) - LEAVE_RET('i', -1); - - Log_Debug("IPStack", "Route_RouteDir_IOCtl - FindRoute %i, %s", - data->Type, IPStack_PrintAddress(data->Type, data->Addr) ); - rt = IPStack_FindRoute(data->Type, NULL, data->Addr); - - if( !rt ) - LEAVE_RET('i', 0); - - LEAVE('i', rt->Node.Inode); - return rt->Node.Inode; - } - break; - } - LEAVE('i', 0); - return 0; -} - -/** - * \brief Create a new route entry - * \param InterfaceName Name of the interface using this route - */ -tRoute *IPStack_Route_Create(int AddrType, void *Network, int SubnetBits, int Metric) -{ - tRoute *rt; - int size; - - // Get the size of the specified address type - size = IPStack_GetAddressSize(AddrType); - if( size == 0 ) { - return NULL; - } - - // Allocate space - rt = calloc(1, sizeof(tRoute) + size*2 ); - - // Set up node - rt->Node.ImplPtr = rt; - rt->Node.Inode = giIP_NextRouteId ++; - rt->Node.Size = 0; - rt->Node.NumACLs = 1, - rt->Node.ACLs = &gVFS_ACL_EveryoneRO; - rt->Node.Type = &gIP_RouteNodeType; - - // Set up state - rt->AddressType = AddrType; - rt->Network = (void *)( (tVAddr)rt + sizeof(tRoute) ); - rt->SubnetBits = SubnetBits; - rt->NextHop = (void *)( (tVAddr)rt + sizeof(tRoute) + size ); - rt->Interface = NULL; - rt->Metric = Metric; - memcpy(rt->Network, Network, size); - memset(rt->NextHop, 0, size); - - // Clear non-fixed bits - { - Uint8 *data = rt->Network; - int i; - i = SubnetBits / 8; - if( SubnetBits % 8 ) { - data[i] &= ~((1 << (8 - SubnetBits % 8)) - 1); - i ++; - } - memset(data + i, 0, size - i); - } - - - // Add to list - if( gIP_RoutesEnd ) { - gIP_RoutesEnd->Next = rt; - gIP_RoutesEnd = rt; - } - else { - gIP_Routes = gIP_RoutesEnd = rt; - } - -// Log_Log("IPStack", "Route entry for '%s' created", InterfaceName); - - return rt; -} - -/** - * \brief Add and fill a route - */ -tRoute *IPStack_AddRoute(const char *Interface, void *Network, int SubnetBits, void *NextHop, int Metric) -{ - tInterface *iface; - tRoute *rt; - int addrSize; - - { - tVFS_Node *tmp; - tmp = IPStack_Root_FindDir(NULL, Interface); - if(!tmp) return NULL; - iface = tmp->ImplPtr; - if(tmp->Type->Close) tmp->Type->Close(tmp); - } - - rt = IPStack_Route_Create(iface->Type, Network, SubnetBits, Metric); - if( !rt ) return NULL; - - addrSize = IPStack_GetAddressSize(iface->Type); - rt->Interface = iface; - - if( NextHop ) - memcpy(rt->NextHop, NextHop, addrSize); - - return rt; -} - -/** - */ -tRoute *IPStack_FindRoute(int AddressType, tInterface *Interface, void *Address) -{ - tRoute *rt; - tRoute *best = NULL; - tInterface *iface; - int addrSize; - - ENTER("iAddressType pInterface sAddress", - AddressType, Interface, IPStack_PrintAddress(AddressType, Address)); - - if( Interface && AddressType != Interface->Type ) { - LOG("Interface->Type (%i) != AddressType", Interface->Type); - LEAVE('n'); - return NULL; - } - - // Get address size - addrSize = IPStack_GetAddressSize(AddressType); - - // Check against explicit routes - for( rt = gIP_Routes; rt; rt = rt->Next ) - { - // Check interface - if( Interface && rt->Interface != Interface ) continue; - // Check address type - if( rt->AddressType != AddressType ) continue; - - LOG("Checking network %s/%i", IPStack_PrintAddress(AddressType, rt->Network), rt->SubnetBits); - - // Check if the address matches - if( !IPStack_CompareAddress(AddressType, rt->Network, Address, rt->SubnetBits) ) - continue; - - if( best ) { - // More direct routes are preferred - if( best->SubnetBits > rt->SubnetBits ) { - LOG("Skipped - less direct (%i < %i)", rt->SubnetBits, best->SubnetBits); - continue; - } - // If equally direct, choose the best metric - if( best->SubnetBits == rt->SubnetBits && best->Metric < rt->Metric ) { - LOG("Skipped - higher metric (%i > %i)", rt->Metric, best->Metric); - continue; - } - } - - best = rt; - } - - // Check against implicit routes - if( !best && !Interface ) - { - for( iface = gIP_Interfaces; iface; iface = iface->Next ) - { - if( Interface && iface != Interface ) continue; - if( iface->Type != AddressType ) continue; - - - // Check if the address matches - if( !IPStack_CompareAddress(AddressType, iface->Address, Address, iface->SubnetBits) ) - continue; - - if( best ) { - // More direct routes are preferred - if( best->SubnetBits > rt->SubnetBits ) { - LOG("Skipped - less direct (%i < %i)", rt->SubnetBits, best->SubnetBits); - continue; - } - // If equally direct, choose the best metric - if( best->SubnetBits == rt->SubnetBits && best->Metric < rt->Metric ) { - LOG("Skipped - higher metric (%i > %i)", rt->Metric, best->Metric); - continue; - } - } - - rt = &iface->Route; - memcpy(rt->Network, iface->Address, addrSize); - memset(rt->NextHop, 0, addrSize); - rt->Metric = DEFAUTL_METRIC; - rt->SubnetBits = iface->SubnetBits; - - best = rt; - } - } - if( !best && Interface ) - { - rt = &Interface->Route; - // Make sure route is up to date - memcpy(rt->Network, Interface->Address, addrSize); - memset(rt->NextHop, 0, addrSize); - rt->Metric = DEFAUTL_METRIC; - rt->SubnetBits = Interface->SubnetBits; - - if( IPStack_CompareAddress(AddressType, rt->Network, Address, rt->SubnetBits) ) - { - best = rt; - } - } - - LEAVE('p', best); - return best; -} - -/** - * \brief Names for route IOCtl Calls - */ -static const char *casIOCtls_Route[] = { - DRV_IOCTLNAMES, - "get_nexthop", // Get next hop - (void *Data), returns boolean success - "set_nexthop", // Set next hop - (void *Data), returns boolean success - "get_interface", // Get interface name - (char *Name), returns name length, NULL OK - "set_interface", // Set interface - (const char *Name) - NULL - }; - -/** - * \brief IOCtl for /Devices/ip/routes/# - */ -int IPStack_Route_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - tRoute *rt = Node->ImplPtr; - int addrSize = IPStack_GetAddressSize(rt->AddressType); - - switch(ID) - { - // --- Standard IOCtls (0-3) --- - BASE_IOCTLS(DRV_TYPE_MISC, STR(IDENT), VERSION, casIOCtls_Route) - - // Get Next Hop - case 4: - if( !CheckMem(Data, addrSize) ) return -1; - memcpy(Data, rt->NextHop, addrSize); - return 1; - // Set Next Hop - case 5: - if( Threads_GetUID() != 0 ) return -1; - if( !CheckMem(Data, addrSize) ) return -1; - memcpy(rt->NextHop, Data, addrSize); - return 1; - - // Get interface name - case 6: - if( !rt->Interface ) { - if(Data && !CheckMem(Data, 1) ) - return -1; - if(Data) - *(char*)Data = 0; - return 0; - } - if( Data ) { - if( !CheckMem(Data, strlen(rt->Interface->Name) + 1) ) - return -1; - strcpy(Data, rt->Interface->Name); - } - return strlen(rt->Interface->Name); - // Set interface name - case 7: - if( Threads_GetUID() != 0 ) - return -1; - if( !CheckString(Data) ) - return -1; - else - { - tInterface *iface; - tVFS_Node *tmp; - tmp = IPStack_Root_FindDir(NULL, Data); - if(!tmp) - return -1; - iface = tmp->ImplPtr; - if(tmp->Type->Close) tmp->Type->Close(tmp); - - if( iface->Type != rt->AddressType ) - return -1; - - // TODO: Other checks? - - rt->Interface = iface; - } - return 0; - - default: - return -1; - } -} diff --git a/Modules/IPStack/sctp.c b/Modules/IPStack/sctp.c deleted file mode 100644 index d130933f..00000000 --- a/Modules/IPStack/sctp.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Acess2 IP Stack - * - SCTP (Stream Control Transmission Protocol) Handling - */ -#include "ipstack.h" -#include -#include "sctp.h" - -#define SCTP_ALLOC_BASE 0xC000 - -// === PROTOTYPES === -void SCTP_Initialise(); -void SCTP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer); -void SCTP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer); -void SCTP_SendPacket(tSCTPChannel *Channel, void *Data, size_t Length); -// --- Listening Server -tVFS_Node *SCTP_Server_Init(tInterface *Interface); -char *SCTP_Server_ReadDir(tVFS_Node *Node, int ID); -tVFS_Node *SCTP_Server_FindDir(tVFS_Node *Node, const char *Name); - int SCTP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data); -void SCTP_Server_Close(tVFS_Node *Node); -// --- Client Channels -tVFS_Node *SCTP_Channel_Init(tInterface *Interface); -Uint64 SCTP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -Uint64 SCTP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); - int SCTP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data); -void SCTP_Channel_Close(tVFS_Node *Node); -// --- Helpers -Uint16 SCTP_int_AllocatePort(); - int SCTP_int_MarkPortAsUsed(Uint16 Port); -void SCTP_int_FreePort(Uint16 Port); - -// === GLOBALS === -tMutex glSCTP_Servers; -tSCTPServer *gpSCTP_Servers; - -tMutex glSCTP_Channels; -tSCTPChannel *gpSCTP_Channels; - -tMutex glSCTP_Ports; -Uint32 gSCTP_Ports[0x10000/32]; - -tSocketFile gSCTP_ServerFile = {NULL, "sctps", SCTP_Server_Init}; -tSocketFile gSCTP_ClientFile = {NULL, "sctpc", SCTP_Channel_Init}; - -// === CODE === -/** - * \fn void TCP_Initialise() - * \brief Initialise the TCP Layer - */ -void SCTP_Initialise() -{ - IPStack_AddFile(&gSCTP_ServerFile); - IPStack_AddFile(&gSCTP_ClientFile); - //IPv4_RegisterCallback(IP4PROT_SCTP, SCTP_GetPacket, SCTP_Unreachable); - IPv4_RegisterCallback(IP4PROT_SCTP, SCTP_GetPacket); -} - -/** - * \brief Scan a list of tSCTPChannels and find process the first match - * \return 0 if no match was found, -1 on error and 1 if a match was found - */ -int SCTP_int_ScanList(tSCTPChannel *List, tInterface *Interface, void *Address, int Length, void *Buffer) -{ - tSCTPHeader *hdr = Buffer; - tSCTPChannel *chan; - tSCTPPacket *pack; - int len; - - for(chan = List; - chan; - chan = chan->Next) - { - if(chan->Interface != Interface) continue; - if(chan->LocalPort != ntohs(hdr->DestPort)) continue; - if(chan->RemotePort != ntohs(hdr->SourcePort)) continue; - - if(Interface->Type == 4) { - if(!IP4_EQU(chan->RemoteAddr.v4, *(tIPv4*)Address)) continue; - } - else if(Interface->Type == 6) { - if(!IP6_EQU(chan->RemoteAddr.v6, *(tIPv6*)Address)) continue; - } - else { - Log_Warning("SCTP", "Address type %i unknown", Interface->Type); - Mutex_Release(&glSCTP_Channels); - return -1; - } - - Log_Log("SCTP", "Recieved packet for %p", chan); - // Create the cached packet - len = ntohs(hdr->Length); - pack = malloc(sizeof(tSCTPPacket) + len); - pack->Next = NULL; - pack->Length = len; - memcpy(pack->Data, hdr->Data, len); - - // Add the packet to the channel's queue - SHORTLOCK(&chan->lQueue); - if(chan->Queue) - chan->QueueEnd->Next = pack; - else - chan->QueueEnd = chan->Queue = pack; - SHORTREL(&chan->lQueue); - Mutex_Release(&glSCTP_Channels); - return 1; - } - return 0; -} - -/** - * \fn void SCTP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) - * \brief Handles a packet from the IP Layer - */ -void SCTP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) -{ - tSCTPHeader *hdr = Buffer; - tSCTPServer *srv; - int ret; - - Log_Log("SCTP", "hdr->SourcePort = %i", ntohs(hdr->SourcePort)); - Log_Log("SCTP", "hdr->DestPort = %i", ntohs(hdr->DestPort)); - Log_Log("SCTP", "hdr->VerifcationTag = %i", ntohs(hdr->VerifcationTag)); - Log_Log("SCTP", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum)); - - // Check registered connections - Mutex_Acquire(&glSCTP_Channels); - ret = SCTP_int_ScanList(gpSCTP_Channels, Interface, Address, Length, Buffer); - Mutex_Release(&glSCTP_Channels); - if(ret != 0) return ; - - - // TODO: Server/Listener - Mutex_Acquire(&glSCTP_Servers); - for(srv = gpSCTP_Servers; - srv; - srv = srv->Next) - { - if(srv->Interface != Interface) continue; - if(srv->ListenPort != ntohs(hdr->DestPort)) continue; - ret = SCTP_int_ScanList(srv->Channels, Interface, Address, Length, Buffer); - if(ret != 0) break; - - // Add connection - Log_Warning("SCTP", "TODO - Add channel on connection"); - //TODO - } - Mutex_Release(&glSCTP_Servers); - -} - -/** - * \brief Handle an ICMP Unrechable Error - */ -void SCTP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer) -{ - -} - -/** - * \brief Send a packet - * \param Channel Channel to send the packet from - * \param Data Packet data - * \param Length Length in bytes of packet data - */ -void SCTP_SendPacket(tSCTPChannel *Channel, void *Data, size_t Length) -{ - tSCTPHeader *hdr; - - switch(Channel->Interface->Type) - { - case 4: - // Create the packet - hdr = malloc(sizeof(tSCTPHeader)+Length); - hdr->SourcePort = htons( Channel->LocalPort ); - hdr->DestPort = htons( Channel->RemotePort ); - hdr->Length = htons( sizeof(tSCTPHeader) + Length ); - hdr->Checksum = 0; // Checksum can be zero on IPv4 - memcpy(hdr->Data, Data, Length); - // Pass on the the IPv4 Layer - IPv4_SendPacket(Channel->Interface, Channel->RemoteAddr.v4, IP4PROT_SCTP, 0, sizeof(tSCTPHeader)+Length, hdr); - // Free allocated packet - free(hdr); - break; - } -} - -// --- Listening Server -tVFS_Node *SCTP_Server_Init(tInterface *Interface) -{ - tSCTPServer *new; - new = calloc( sizeof(tSCTPServer), 1 ); - if(!new) return NULL; - - new->Node.ImplPtr = new; - new->Node.Flags = VFS_FFLAG_DIRECTORY; - new->Node.NumACLs = 1; - new->Node.ACLs = &gVFS_ACL_EveryoneRX; - new->Node.ReadDir = SCTP_Server_ReadDir; - new->Node.FindDir = SCTP_Server_FindDir; - new->Node.IOCtl = SCTP_Server_IOCtl; - new->Node.Close = SCTP_Server_Close; - - Mutex_Acquire(&glSCTP_Servers); - new->Next = gpSCTP_Servers; - gpSCTP_Servers = new; - Mutex_Release(&glSCTP_Servers); - - return &new->Node; -} - -/** - * \brief Wait for a connection and return its ID in a string - */ -char *SCTP_Server_ReadDir(tVFS_Node *Node, int ID) -{ - tSCTPServer *srv = Node->ImplPtr; - tSCTPChannel *chan; - char *ret; - - if( srv->ListenPort == 0 ) return NULL; - - // Lock (so another thread can't collide with us here) and wait for a connection - Mutex_Acquire( &srv->Lock ); - while( srv->NewChannels == NULL ) Threads_Yield(); - // Pop the connection off the new list - chan = srv->NewChannels; - srv->NewChannels = chan->Next; - // Release the lock - Mutex_Release( &srv->Lock ); - - // Create the ID string and return it - ret = malloc(11+1); - sprintf(ret, "%i", chan->Node.ImplInt); - - return ret; -} - -/** - * \brief Take a string and find the channel - */ -tVFS_Node *SCTP_Server_FindDir(tVFS_Node *Node, const char *Name) -{ - tSCTPServer *srv = Node->ImplPtr; - tSCTPChannel *chan; - int id = atoi(Name); - - for(chan = srv->Channels; - chan; - chan = chan->Next) - { - if( chan->Node.ImplInt < id ) continue; - if( chan->Node.ImplInt > id ) break; // Go sorted lists! - - return &chan->Node; - } - - return NULL; -} - -/** - * \brief Names for server IOCtl Calls - */ -static const char *casIOCtls_Server[] = { - DRV_IOCTLNAMES, - "getset_listenport", - NULL - }; -/** - * \brief Channel IOCtls - */ -int SCTP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - tSCTPServer *srv = Node->ImplPtr; - - ENTER("pNode iID pData", Node, ID, Data); - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_MISC, "SCTP Server", 0x100, casIOCtls_Server); - - case 4: // getset_localport (returns bool success) - if(!Data) LEAVE_RET('i', srv->ListenPort); - if(!CheckMem( Data, sizeof(Uint16) ) ) { - LOG("Invalid pointer %p", Data); - LEAVE_RET('i', -1); - } - // Set port - srv->ListenPort = *(Uint16*)Data; - // Permissions check (Ports lower than 1024 are root-only) - if(srv->ListenPort != 0 && srv->ListenPort < 1024) { - if( Threads_GetUID() != 0 ) { - LOG("Attempt by non-superuser to listen on port %i", srv->ListenPort); - srv->ListenPort = 0; - LEAVE_RET('i', -1); - } - } - // Allocate a random port if requested - if( srv->ListenPort == 0 ) - srv->ListenPort = SCTP_int_AllocatePort(); - else - { - // Else, mark the requested port as used - if( SCTP_int_MarkPortAsUsed(srv->ListenPort) == 0 ) { - LOG("Port %i us currently in use", srv->ListenPort); - srv->ListenPort = 0; - LEAVE_RET('i', -1); - } - LEAVE_RET('i', 1); - } - LEAVE_RET('i', 1); - - default: - LEAVE_RET('i', -1); - } - LEAVE_RET('i', 0); -} - -void SCTP_Server_Close(tVFS_Node *Node) -{ - tSCTPServer *srv = Node->ImplPtr; - tSCTPServer *prev; - tSCTPChannel *chan; - tSCTPPacket *tmp; - - - // Remove from the main list first - Mutex_Acquire(&glSCTP_Servers); - if(gpSCTP_Servers == srv) - gpSCTP_Servers = gpSCTP_Servers->Next; - else - { - for(prev = gpSCTP_Servers; - prev->Next && prev->Next != srv; - prev = prev->Next); - if(!prev->Next) - Log_Warning("SCTP", "Bookeeping Fail, server %p is not in main list", srv); - else - prev->Next = prev->Next->Next; - } - Mutex_Release(&glSCTP_Servers); - - - Mutex_Acquire(&srv->Lock); - for(chan = srv->Channels; - chan; - chan = chan->Next) - { - // Clear Queue - SHORTLOCK(&chan->lQueue); - while(chan->Queue) - { - tmp = chan->Queue; - chan->Queue = tmp->Next; - free(tmp); - } - SHORTREL(&chan->lQueue); - - // Free channel structure - free(chan); - } - Mutex_Release(&srv->Lock); - - free(srv); -} - -// --- Client Channels -tVFS_Node *SCTP_Channel_Init(tInterface *Interface) -{ - tSCTPChannel *new; - new = calloc( sizeof(tSCTPChannel), 1 ); - new->Interface = Interface; - new->Node.ImplPtr = new; - new->Node.NumACLs = 1; - new->Node.ACLs = &gVFS_ACL_EveryoneRW; - new->Node.Read = SCTP_Channel_Read; - new->Node.Write = SCTP_Channel_Write; - new->Node.IOCtl = SCTP_Channel_IOCtl; - new->Node.Close = SCTP_Channel_Close; - - Mutex_Acquire(&glSCTP_Channels); - new->Next = gpSCTP_Channels; - gpSCTP_Channels = new; - Mutex_Release(&glSCTP_Channels); - - return &new->Node; -} - -/** - * \brief Read from the channel file (wait for a packet) - */ -Uint64 SCTP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - tSCTPChannel *chan = Node->ImplPtr; - tSCTPPacket *pack; - - if(chan->LocalPort == 0) return 0; - if(chan->RemotePort == 0) return 0; - - while(chan->Queue == NULL) Threads_Yield(); - - for(;;) - { - SHORTLOCK(&chan->lQueue); - if(chan->Queue == NULL) { - SHORTREL(&chan->lQueue); - continue; - } - pack = chan->Queue; - chan->Queue = pack->Next; - if(!chan->Queue) chan->QueueEnd = NULL; - SHORTREL(&chan->lQueue); - break; - } - - // Clip length to packet length - if(Length > pack->Length) Length = pack->Length; - // Copy packet data from cache - memcpy(Buffer, pack->Data, Length); - // Free cached packet - free(pack); - - return Length; -} - -/** - * \brief Write to the channel file (send a packet) - */ -Uint64 SCTP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - tSCTPChannel *chan = Node->ImplPtr; - if(chan->RemotePort == 0) return 0; - - SCTP_SendPacket(chan, Buffer, (size_t)Length); - - return 0; -} - -/** - * \brief Names for channel IOCtl Calls - */ -static const char *casIOCtls_Channel[] = { - DRV_IOCTLNAMES, - "getset_localport", - "getset_remoteport", - "set_remoteaddr", - NULL - }; -/** - * \brief Channel IOCtls - */ -int SCTP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - tSCTPChannel *chan = Node->ImplPtr; - ENTER("pNode iID pData", Node, ID, Data); - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_MISC, "SCTP Channel", 0x100, casIOCtls_Channel); - - case 4: // getset_localport (returns bool success) - if(!Data) LEAVE_RET('i', chan->LocalPort); - if(!CheckMem( Data, sizeof(Uint16) ) ) { - LOG("Invalid pointer %p", Data); - LEAVE_RET('i', -1); - } - // Set port - chan->LocalPort = *(Uint16*)Data; - // Permissions check (Ports lower than 1024 are root-only) - if(chan->LocalPort != 0 && chan->LocalPort < 1024) { - if( Threads_GetUID() != 0 ) { - LOG("Attempt by non-superuser to listen on port %i", chan->LocalPort); - chan->LocalPort = 0; - LEAVE_RET('i', -1); - } - } - // Allocate a random port if requested - if( chan->LocalPort == 0 ) - chan->LocalPort = SCTP_int_AllocatePort(); - else - { - // Else, mark the requested port as used - if( SCTP_int_MarkPortAsUsed(chan->LocalPort) == 0 ) { - LOG("Port %i us currently in use", chan->LocalPort); - chan->LocalPort = 0; - LEAVE_RET('i', 0); - } - LEAVE_RET('i', 1); - } - LEAVE_RET('i', 1); - - case 5: // getset_remoteport (returns bool success) - if(!Data) LEAVE_RET('i', chan->RemotePort); - if(!CheckMem( Data, sizeof(Uint16) ) ) { - LOG("Invalid pointer %p", Data); - LEAVE_RET('i', -1); - } - chan->RemotePort = *(Uint16*)Data; - return 1; - - case 6: // set_remoteaddr (returns bool success) - switch(chan->Interface->Type) - { - case 4: - if(!CheckMem(Data, sizeof(tIPv4))) { - LOG("Invalid pointer %p", Data); - LEAVE_RET('i', -1); - } - chan->RemoteAddr.v4 = *(tIPv4*)Data; - break; - } - break; - } - LEAVE_RET('i', 0); -} - -/** - * \brief Close and destroy an open channel - */ -void SCTP_Channel_Close(tVFS_Node *Node) -{ - tSCTPChannel *chan = Node->ImplPtr; - tSCTPChannel *prev; - - // Remove from the main list first - Mutex_Acquire(&glSCTP_Channels); - if(gpSCTP_Channels == chan) - gpSCTP_Channels = gpSCTP_Channels->Next; - else - { - for(prev = gpSCTP_Channels; - prev->Next && prev->Next != chan; - prev = prev->Next); - if(!prev->Next) - Log_Warning("SCTP", "Bookeeping Fail, channel %p is not in main list", chan); - else - prev->Next = prev->Next->Next; - } - Mutex_Release(&glSCTP_Channels); - - // Clear Queue - SHORTLOCK(&chan->lQueue); - while(chan->Queue) - { - tSCTPPacket *tmp; - tmp = chan->Queue; - chan->Queue = tmp->Next; - free(tmp); - } - SHORTREL(&chan->lQueue); - - // Free channel structure - free(chan); -} - -/** - * \return Port Number on success, or zero on failure - */ -Uint16 SCTP_int_AllocatePort() -{ - int i; - Mutex_Acquire(&glSCTP_Ports); - // Fast Search - for( i = SCTP_ALLOC_BASE; i < 0x10000; i += 32 ) - if( gSCTP_Ports[i/32] != 0xFFFFFFFF ) - break; - if(i == 0x10000) return 0; - for( ;; i++ ) - { - if( !(gSCTP_Ports[i/32] & (1 << (i%32))) ) - return i; - } - Mutex_Release(&glSCTP_Ports); -} - -/** - * \brief Allocate a specific port - * \return Boolean Success - */ -int SCTP_int_MarkPortAsUsed(Uint16 Port) -{ - Mutex_Acquire(&glSCTP_Ports); - if( gSCTP_Ports[Port/32] & (1 << (Port%32)) ) { - return 0; - Mutex_Release(&glSCTP_Ports); - } - gSCTP_Ports[Port/32] |= 1 << (Port%32); - Mutex_Release(&glSCTP_Ports); - return 1; -} - -/** - * \brief Free an allocated port - */ -void SCTP_int_FreePort(Uint16 Port) -{ - Mutex_Acquire(&glSCTP_Ports); - gSCTP_Ports[Port/32] &= ~(1 << (Port%32)); - Mutex_Release(&glSCTP_Ports); -} diff --git a/Modules/IPStack/tcp.c b/Modules/IPStack/tcp.c deleted file mode 100644 index e9c3de98..00000000 --- a/Modules/IPStack/tcp.c +++ /dev/null @@ -1,1373 +0,0 @@ -/* - * Acess2 IP Stack - * - TCP Handling - */ -#define DEBUG 1 -#include "ipstack.h" -#include "ipv4.h" -#include "ipv6.h" -#include "tcp.h" - -#define USE_SELECT 1 -#define HEXDUMP_INCOMING 0 -#define HEXDUMP_OUTGOING 0 -#define CACHE_FUTURE_PACKETS_IN_BYTES 1 // Use a ring buffer to cache out of order packets - -#define TCP_MIN_DYNPORT 0xC000 -#define TCP_MAX_HALFOPEN 1024 // Should be enough - -#define TCP_MAX_PACKET_SIZE 1024 -#define TCP_WINDOW_SIZE 0x2000 -#define TCP_RECIEVE_BUFFER_SIZE 0x4000 - -// === PROTOTYPES === -void TCP_Initialise(void); -void TCP_StartConnection(tTCPConnection *Conn); -void TCP_SendPacket(tTCPConnection *Conn, size_t Length, tTCPHeader *Data); -void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer); -void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length); -int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t Length); -void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection); -Uint16 TCP_GetUnusedPort(); - int TCP_AllocatePort(Uint16 Port); - int TCP_DeallocatePort(Uint16 Port); -// --- Server -tVFS_Node *TCP_Server_Init(tInterface *Interface); -char *TCP_Server_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, const char *Name); - int TCP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data); -void TCP_Server_Close(tVFS_Node *Node); -// --- Client -tVFS_Node *TCP_Client_Init(tInterface *Interface); -Uint64 TCP_Client_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -Uint64 TCP_Client_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); - int TCP_Client_IOCtl(tVFS_Node *Node, int ID, void *Data); -void TCP_Client_Close(tVFS_Node *Node); -// --- Helpers - int WrapBetween(Uint32 Lower, Uint32 Value, Uint32 Higher, Uint32 MaxValue); - -// === TEMPLATES === -tSocketFile gTCP_ServerFile = {NULL, "tcps", TCP_Server_Init}; -tSocketFile gTCP_ClientFile = {NULL, "tcpc", TCP_Client_Init}; -tVFS_NodeType gTCP_ServerNodeType = { - .TypeName = "TCP Server", - .ReadDir = TCP_Server_ReadDir, - .FindDir = TCP_Server_FindDir, - .IOCtl = TCP_Server_IOCtl, - .Close = TCP_Server_Close - }; -tVFS_NodeType gTCP_ClientNodeType = { - .TypeName = "TCP Client/Connection", - .Read = TCP_Client_Read, - .Write = TCP_Client_Write, - .IOCtl = TCP_Client_IOCtl, - .Close = TCP_Client_Close - }; - -// === GLOBALS === - int giTCP_NumHalfopen = 0; -tShortSpinlock glTCP_Listeners; -tTCPListener *gTCP_Listeners; -tShortSpinlock glTCP_OutbountCons; -tTCPConnection *gTCP_OutbountCons; -Uint32 gaTCP_PortBitmap[0x800]; - int giTCP_NextOutPort = TCP_MIN_DYNPORT; - -// === CODE === -/** - * \brief Initialise the TCP Layer - * - * Registers the client and server files and the GetPacket callback - */ -void TCP_Initialise(void) -{ - giTCP_NextOutPort += rand()%32; - IPStack_AddFile(&gTCP_ServerFile); - IPStack_AddFile(&gTCP_ClientFile); - IPv4_RegisterCallback(IP4PROT_TCP, TCP_GetPacket); - IPv6_RegisterCallback(IP4PROT_TCP, TCP_GetPacket); -} - -/** - * \brief Sends a packet from the specified connection, calculating the checksums - * \param Conn Connection - * \param Length Length of data - * \param Data Packet data (cast as a TCP Header) - */ -void TCP_SendPacket( tTCPConnection *Conn, size_t Length, tTCPHeader *Data ) -{ - Uint16 checksum[2]; - - Data->Checksum = 0; - checksum[1] = htons( ~IPv4_Checksum( (void*)Data, Length ) ); // Partial checksum - if(Length & 1) - ((Uint8*)Data)[Length] = 0; - - // TODO: Fragment packet - - switch( Conn->Interface->Type ) - { - case 4: - // Append IPv4 Pseudo Header - { - Uint32 buf[3]; - buf[0] = ((tIPv4*)Conn->Interface->Address)->L; - buf[1] = Conn->RemoteIP.v4.L; - buf[2] = (htons(Length)<<16) | (6<<8) | 0; - checksum[0] = htons( ~IPv4_Checksum(buf, sizeof(buf)) ); // Partial checksum - } - Data->Checksum = htons( IPv4_Checksum(checksum, 2*2) ); // Combine the two - IPv4_SendPacket(Conn->Interface, Conn->RemoteIP.v4, IP4PROT_TCP, 0, Length, Data); - break; - - case 6: - // Append IPv6 Pseudo Header - { - Uint32 buf[4+4+1+1]; - memcpy(buf, Conn->Interface->Address, 16); - memcpy(&buf[4], &Conn->RemoteIP, 16); - buf[8] = htonl(Length); - buf[9] = htonl(6); - checksum[0] = htons( ~IPv4_Checksum(buf, sizeof(buf)) ); // Partial checksum - } - Data->Checksum = htons( IPv4_Checksum(checksum, 2*2) ); // Combine the two - IPv6_SendPacket(Conn->Interface, Conn->RemoteIP.v6, IP4PROT_TCP, Length, Data); - break; - } -} - -/** - * \brief Handles a packet from the IP Layer - * \param Interface Interface the packet arrived from - * \param Address Pointer to the addres structure - * \param Length Size of packet in bytes - * \param Buffer Packet data - */ -void TCP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) -{ - tTCPHeader *hdr = Buffer; - tTCPListener *srv; - tTCPConnection *conn; - - Log_Log("TCP", "TCP_GetPacket: :%i from [%s]:%i, Flags= %s%s%s%s%s%s%s%s", - ntohs(hdr->DestPort), - IPStack_PrintAddress(Interface->Type, Address), - ntohs(hdr->SourcePort), - (hdr->Flags & TCP_FLAG_CWR) ? "CWR " : "", - (hdr->Flags & TCP_FLAG_ECE) ? "ECE " : "", - (hdr->Flags & TCP_FLAG_URG) ? "URG " : "", - (hdr->Flags & TCP_FLAG_ACK) ? "ACK " : "", - (hdr->Flags & TCP_FLAG_PSH) ? "PSH " : "", - (hdr->Flags & TCP_FLAG_RST) ? "RST " : "", - (hdr->Flags & TCP_FLAG_SYN) ? "SYN " : "", - (hdr->Flags & TCP_FLAG_FIN) ? "FIN " : "" - ); - - if( Length > (hdr->DataOffset >> 4)*4 ) - { - Log_Log("TCP", "TCP_GetPacket: SequenceNumber = 0x%x", ntohl(hdr->SequenceNumber)); -#if HEXDUMP_INCOMING - Debug_HexDump( - "TCP_GetPacket: Packet Data = ", - (Uint8*)hdr + (hdr->DataOffset >> 4)*4, - Length - (hdr->DataOffset >> 4)*4 - ); -#endif - } - - // Check Servers - { - for( srv = gTCP_Listeners; srv; srv = srv->Next ) - { - // Check if the server is active - if(srv->Port == 0) continue; - // Check the interface - if(srv->Interface && srv->Interface != Interface) continue; - // Check the destination port - if(srv->Port != htons(hdr->DestPort)) continue; - - Log_Log("TCP", "TCP_GetPacket: Matches server %p", srv); - // Is this in an established connection? - for( conn = srv->Connections; conn; conn = conn->Next ) - { - // Check that it is coming in on the same interface - if(conn->Interface != Interface) continue; - - // Check Source Port - Log_Log("TCP", "TCP_GetPacket: conn->RemotePort(%i) == hdr->SourcePort(%i)", - conn->RemotePort, ntohs(hdr->SourcePort)); - if(conn->RemotePort != ntohs(hdr->SourcePort)) continue; - - // Check Source IP - Log_Debug("TCP", "TCP_GetPacket: conn->RemoteIP(%s)", - IPStack_PrintAddress(conn->Interface->Type, &conn->RemoteIP)); - Log_Debug("TCP", " == Address(%s)", - IPStack_PrintAddress(conn->Interface->Type, Address)); - if( IPStack_CompareAddress(conn->Interface->Type, &conn->RemoteIP, Address, -1) == 0 ) - continue ; - - Log_Log("TCP", "TCP_GetPacket: Matches connection %p", conn); - // We have a response! - TCP_INT_HandleConnectionPacket(conn, hdr, Length); - - return; - } - - Log_Log("TCP", "TCP_GetPacket: Opening Connection"); - // Open a new connection (well, check that it's a SYN) - if(hdr->Flags != TCP_FLAG_SYN) { - Log_Log("TCP", "TCP_GetPacket: Packet is not a SYN"); - return ; - } - - // TODO: Check for halfopen max - - conn = calloc(1, sizeof(tTCPConnection)); - conn->State = TCP_ST_SYN_RCVD; - conn->LocalPort = srv->Port; - conn->RemotePort = ntohs(hdr->SourcePort); - conn->Interface = Interface; - - switch(Interface->Type) - { - case 4: conn->RemoteIP.v4 = *(tIPv4*)Address; break; - case 6: conn->RemoteIP.v6 = *(tIPv6*)Address; break; - } - - conn->RecievedBuffer = RingBuffer_Create( TCP_RECIEVE_BUFFER_SIZE ); - - conn->NextSequenceRcv = ntohl( hdr->SequenceNumber ) + 1; - conn->NextSequenceSend = rand(); - - // Create node - conn->Node.NumACLs = 1; - conn->Node.ACLs = &gVFS_ACL_EveryoneRW; - conn->Node.ImplPtr = conn; - conn->Node.ImplInt = srv->NextID ++; - conn->Node.Type = &gTCP_ClientNodeType; // TODO: Special type for the server end? - - // Hmm... Theoretically, this lock will never have to wait, - // as the interface is locked to the watching thread, and this - // runs in the watching thread. But, it's a good idea to have - // it, just in case - // Oh, wait, there is a case where a wildcard can be used - // (srv->Interface == NULL) so having the lock is a good idea - SHORTLOCK(&srv->lConnections); - if( !srv->Connections ) - srv->Connections = conn; - else - srv->ConnectionsTail->Next = conn; - srv->ConnectionsTail = conn; - if(!srv->NewConnections) - srv->NewConnections = conn; - VFS_MarkAvaliable( &srv->Node, 1 ); - SHORTREL(&srv->lConnections); - - // Send the SYN ACK - hdr->Flags |= TCP_FLAG_ACK; - hdr->AcknowlegementNumber = htonl(conn->NextSequenceRcv); - hdr->SequenceNumber = htonl(conn->NextSequenceSend); - hdr->DestPort = hdr->SourcePort; - hdr->SourcePort = htons(srv->Port); - hdr->DataOffset = (sizeof(tTCPHeader)/4) << 4; - TCP_SendPacket( conn, sizeof(tTCPHeader), hdr ); - conn->NextSequenceSend ++; - return ; - } - } - - - // Check Open Connections - { - for( conn = gTCP_OutbountCons; conn; conn = conn->Next ) - { - // Check that it is coming in on the same interface - if(conn->Interface != Interface) continue; - - // Check Source Port - if(conn->RemotePort != ntohs(hdr->SourcePort)) continue; - - // Check Source IP - if(conn->Interface->Type == 6 && !IP6_EQU(conn->RemoteIP.v6, *(tIPv6*)Address)) - continue; - if(conn->Interface->Type == 4 && !IP4_EQU(conn->RemoteIP.v4, *(tIPv4*)Address)) - continue; - - TCP_INT_HandleConnectionPacket(conn, hdr, Length); - return ; - } - } - - Log_Log("TCP", "TCP_GetPacket: No Match"); -} - -/** - * \brief Handles a packet sent to a specific connection - * \param Connection TCP Connection pointer - * \param Header TCP Packet pointer - * \param Length Length of the packet - */ -void TCP_INT_HandleConnectionPacket(tTCPConnection *Connection, tTCPHeader *Header, int Length) -{ - int dataLen; - Uint32 sequence_num; - - // Silently drop once finished - // TODO: Check if this needs to be here - if( Connection->State == TCP_ST_FINISHED ) { - Log_Log("TCP", "Packet ignored - connection finnished"); - return ; - } - - // Syncronise sequence values - if(Header->Flags & TCP_FLAG_SYN) { - // TODO: What if the packet also has data? - Connection->NextSequenceRcv = ntohl(Header->SequenceNumber); - } - - // Ackowledge a sent packet - if(Header->Flags & TCP_FLAG_ACK) { - // TODO: Process an ACKed Packet - Log_Log("TCP", "Conn %p, Sent packet 0x%x ACKed", Connection, Header->AcknowlegementNumber); - } - - // Get length of data - dataLen = Length - (Header->DataOffset>>4)*4; - Log_Log("TCP", "HandleConnectionPacket - dataLen = %i", dataLen); - - // - // State Machine - // - switch( Connection->State ) - { - // Pre-init connection? - case TCP_ST_CLOSED: - Log_Log("TCP", "Packets to a closed connection?!"); - break; - - // --- Init States --- - // SYN sent, expecting SYN-ACK Connection Opening - case TCP_ST_SYN_SENT: - if( Header->Flags & TCP_FLAG_SYN ) - { - Connection->NextSequenceRcv ++; - Header->DestPort = Header->SourcePort; - Header->SourcePort = htons(Connection->LocalPort); - Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv); - Header->SequenceNumber = htonl(Connection->NextSequenceSend); - Header->WindowSize = htons(TCP_WINDOW_SIZE); - Header->Flags = TCP_FLAG_ACK; - Header->DataOffset = (sizeof(tTCPHeader)/4) << 4; - TCP_SendPacket( Connection, sizeof(tTCPHeader), Header ); - - if( Header->Flags & TCP_FLAG_ACK ) - { - Log_Log("TCP", "ACKing SYN-ACK"); - Connection->State = TCP_ST_OPEN; - } - else - { - Log_Log("TCP", "ACKing SYN"); - Connection->State = TCP_ST_SYN_RCVD; - } - } - break; - - // SYN-ACK sent, expecting ACK - case TCP_ST_SYN_RCVD: - if( Header->Flags & TCP_FLAG_ACK ) - { - // TODO: Handle max half-open limit - Connection->State = TCP_ST_OPEN; - Log_Log("TCP", "Connection fully opened"); - } - break; - - // --- Established State --- - case TCP_ST_OPEN: - // - Handle State changes - // - if( Header->Flags & TCP_FLAG_FIN ) { - Log_Log("TCP", "Conn %p closed, recieved FIN", Connection); - VFS_MarkError(&Connection->Node, 1); - Connection->State = TCP_ST_CLOSE_WAIT; -// Header->Flags &= ~TCP_FLAG_FIN; - // CLOSE WAIT requires the client to close (or does it?) - #if 0 - - #endif - } - - // Check for an empty packet - if(dataLen == 0) { - if( Header->Flags == TCP_FLAG_ACK ) - { - Log_Log("TCP", "ACK only packet"); - return ; - } - Connection->NextSequenceRcv ++; // TODO: Is this right? (empty packet counts as one byte) - Log_Log("TCP", "Empty Packet, inc and ACK the current sequence number"); - Header->DestPort = Header->SourcePort; - Header->SourcePort = htons(Connection->LocalPort); - Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv); - Header->SequenceNumber = htonl(Connection->NextSequenceSend); - Header->Flags |= TCP_FLAG_ACK; - TCP_SendPacket( Connection, sizeof(tTCPHeader), Header ); - return ; - } - - // NOTES: - // Flags - // PSH - Has Data? - // /NOTES - - sequence_num = ntohl(Header->SequenceNumber); - - Log_Log("TCP", "0x%08x <= 0x%08x < 0x%08x", - Connection->NextSequenceRcv, - ntohl(Header->SequenceNumber), - Connection->NextSequenceRcv + TCP_WINDOW_SIZE - ); - - // Is this packet the next expected packet? - if( sequence_num == Connection->NextSequenceRcv ) - { - int rv; - // Ooh, Goodie! Add it to the recieved list - rv = TCP_INT_AppendRecieved(Connection, - (Uint8*)Header + (Header->DataOffset>>4)*4, - dataLen - ); - if(rv != 0) { - break; - } - Log_Log("TCP", "0x%08x += %i", Connection->NextSequenceRcv, dataLen); - Connection->NextSequenceRcv += dataLen; - - // TODO: This should be moved out of the watcher thread, - // so that a single lost packet on one connection doesn't cause - // all connections on the interface to lag. - // - Meh, no real issue, as the cache shouldn't be that large - TCP_INT_UpdateRecievedFromFuture(Connection); - - // ACK Packet - Header->DestPort = Header->SourcePort; - Header->SourcePort = htons(Connection->LocalPort); - Header->AcknowlegementNumber = htonl(Connection->NextSequenceRcv); - Header->SequenceNumber = htonl(Connection->NextSequenceSend); - Header->WindowSize = htons(TCP_WINDOW_SIZE); - Header->Flags &= TCP_FLAG_SYN; // Eliminate all flags save for SYN - Header->Flags |= TCP_FLAG_ACK; // Add ACK - Log_Log("TCP", "Sending ACK for 0x%08x", Connection->NextSequenceRcv); - TCP_SendPacket( Connection, sizeof(tTCPHeader), Header ); - //Connection->NextSequenceSend ++; - } - // Check if the packet is in window - else if( WrapBetween(Connection->NextSequenceRcv, sequence_num, - Connection->NextSequenceRcv+TCP_WINDOW_SIZE, 0xFFFFFFFF) ) - { - Uint8 *dataptr = (Uint8*)Header + (Header->DataOffset>>4)*4; - #if CACHE_FUTURE_PACKETS_IN_BYTES - Uint32 index; - int i; - - index = sequence_num % TCP_WINDOW_SIZE; - for( i = 0; i < dataLen; i ++ ) - { - Connection->FuturePacketValidBytes[index/8] |= 1 << (index%8); - Connection->FuturePacketData[index] = dataptr[i]; - // Do a wrap increment - index ++; - if(index == TCP_WINDOW_SIZE) index = 0; - } - #else - tTCPStoredPacket *pkt, *tmp, *prev = NULL; - - // Allocate and fill cached packet - pkt = malloc( sizeof(tTCPStoredPacket) + dataLen ); - pkt->Next = NULL; - pkt->Sequence = ntohl(Header->SequenceNumber); - pkt->Length = dataLen; - memcpy(pkt->Data, dataptr, dataLen); - - Log_Log("TCP", "We missed a packet, caching", - pkt->Sequence, Connection->NextSequenceRcv); - - // No? Well, let's cache it and look at it later - SHORTLOCK( &Connection->lFuturePackets ); - for(tmp = Connection->FuturePackets; - tmp; - prev = tmp, tmp = tmp->Next) - { - if(tmp->Sequence >= pkt->Sequence) break; - } - - // Add if before first, or sequences don't match - if( !tmp || tmp->Sequence != pkt->Sequence ) - { - if(prev) - prev->Next = pkt; - else - Connection->FuturePackets = pkt; - pkt->Next = tmp; - } - // Replace if larger - else if(pkt->Length > tmp->Length) - { - if(prev) - prev->Next = pkt; - pkt->Next = tmp->Next; - free(tmp); - } - else - { - free(pkt); // TODO: Find some way to remove this - } - SHORTREL( &Connection->lFuturePackets ); - #endif - } - // Badly out of sequence packet - else - { - Log_Log("TCP", "Fully out of sequence packet (0x%08x not between 0x%08x and 0x%08x), dropped", - sequence_num, Connection->NextSequenceRcv, Connection->NextSequenceRcv+TCP_WINDOW_SIZE); - // TODO: Spec says we should send an empty ACK with the current state - } - break; - - // --- Remote close states - case TCP_ST_CLOSE_WAIT: - - // Ignore everything, CLOSE_WAIT is terminated by the client - Log_Debug("TCP", "CLOSE WAIT - Ignoring packets"); - - break; - - // LAST-ACK - Waiting for the ACK of FIN (from CLOSE WAIT) - case TCP_ST_LAST_ACK: - if( Header->Flags & TCP_FLAG_ACK ) - { - Connection->State = TCP_ST_FINISHED; // Connection completed - Log_Log("TCP", "LAST-ACK to CLOSED - Connection remote closed"); - // TODO: Destrory the TCB - } - break; - - // --- Local close States - case TCP_ST_FIN_WAIT1: - if( Header->Flags & TCP_FLAG_FIN ) - { - Connection->State = TCP_ST_CLOSING; - Log_Debug("TCP", "Conn %p closed, sent FIN and recieved FIN", Connection); - VFS_MarkError(&Connection->Node, 1); - - // ACK Packet - Header->DestPort = Header->SourcePort; - Header->SourcePort = htons(Connection->LocalPort); - Header->AcknowlegementNumber = Header->SequenceNumber; - Header->SequenceNumber = htonl(Connection->NextSequenceSend); - Header->WindowSize = htons(TCP_WINDOW_SIZE); - Header->Flags = TCP_FLAG_ACK; - TCP_SendPacket( Connection, sizeof(tTCPHeader), Header ); - break ; - } - - // TODO: Make sure that the packet is actually ACKing the FIN - if( Header->Flags & TCP_FLAG_ACK ) - { - Connection->State = TCP_ST_FIN_WAIT2; - Log_Debug("TCP", "Conn %p closed, sent FIN ACKed", Connection); - VFS_MarkError(&Connection->Node, 1); - return ; - } - break; - - case TCP_ST_FIN_WAIT2: - if( Header->Flags & TCP_FLAG_FIN ) - { - Connection->State = TCP_ST_TIME_WAIT; - Log_Debug("TCP", "FIN sent and recieved, ACKing and going into TIME WAIT %p FINWAIT-2 -> TIME WAIT", Connection); - // Send ACK - Header->DestPort = Header->SourcePort; - Header->SourcePort = htons(Connection->LocalPort); - Header->AcknowlegementNumber = Header->SequenceNumber; - Header->SequenceNumber = htonl(Connection->NextSequenceSend); - Header->WindowSize = htons(TCP_WINDOW_SIZE); - Header->Flags = TCP_FLAG_ACK; - TCP_SendPacket( Connection, sizeof(tTCPHeader), Header ); - } - break; - - case TCP_ST_CLOSING: - // TODO: Make sure that the packet is actually ACKing the FIN - if( Header->Flags & TCP_FLAG_ACK ) - { - Connection->State = TCP_ST_TIME_WAIT; - Log_Debug("TCP", "Conn %p CLOSING -> TIME WAIT", Connection); - VFS_MarkError(&Connection->Node, 1); - return ; - } - break; - - // --- Closed (or near closed) states) --- - case TCP_ST_TIME_WAIT: - Log_Log("TCP", "Packets on Time-Wait, ignored"); - break; - - case TCP_ST_FINISHED: - Log_Log("TCP", "Packets when CLOSED, ignoring"); - break; - - //default: - // Log_Warning("TCP", "Unhandled TCP state %i", Connection->State); - // break; - } - -} - -/** - * \brief Appends a packet to the recieved list - * \param Connection Connection structure - * \param Data Packet contents - * \param Length Length of \a Data - */ -int TCP_INT_AppendRecieved(tTCPConnection *Connection, const void *Data, size_t Length) -{ - Mutex_Acquire( &Connection->lRecievedPackets ); - - if(Connection->RecievedBuffer->Length + Length > Connection->RecievedBuffer->Space ) - { - VFS_MarkAvaliable(&Connection->Node, 1); - Log_Error("TCP", "Buffer filled, packet dropped (:%i) - %i + %i > %i", - Connection->LocalPort, Connection->RecievedBuffer->Length, Length, - Connection->RecievedBuffer->Space - ); - Mutex_Release( &Connection->lRecievedPackets ); - return 1; - } - - RingBuffer_Write( Connection->RecievedBuffer, Data, Length ); - - VFS_MarkAvaliable(&Connection->Node, 1); - - Mutex_Release( &Connection->lRecievedPackets ); - return 0; -} - -/** - * \brief Updates the connections recieved list from the future list - * \param Connection Connection structure - * - * Updates the recieved packets list with packets from the future (out - * of order) packets list that are now able to be added in direct - * sequence. - */ -void TCP_INT_UpdateRecievedFromFuture(tTCPConnection *Connection) -{ - #if CACHE_FUTURE_PACKETS_IN_BYTES - int i, length = 0; - Uint32 index; - - // Calculate length of contiguous bytes - length = Connection->HighestSequenceRcvd - Connection->NextSequenceRcv; - index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE; - for( i = 0; i < length; i ++ ) - { - if( Connection->FuturePacketValidBytes[i / 8] == 0xFF ) { - i += 7; index += 7; - continue; - } - else if( !(Connection->FuturePacketValidBytes[i / 8] & (1 << (i%8))) ) - break; - - index ++; - if(index > TCP_WINDOW_SIZE) - index -= TCP_WINDOW_SIZE; - } - length = i; - - index = Connection->NextSequenceRcv % TCP_WINDOW_SIZE; - - // Write data to to the ring buffer - if( TCP_WINDOW_SIZE - index > length ) - { - // Simple case - RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, length ); - } - else - { - int endLen = TCP_WINDOW_SIZE - index; - // 2-part case - RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData + index, endLen ); - RingBuffer_Write( Connection->RecievedBuffer, Connection->FuturePacketData, endLen - length ); - } - - // Mark (now saved) bytes as invalid - // - Align index - while(index % 8 && length) - { - Connection->FuturePacketData[index] = 0; - Connection->FuturePacketData[index/8] &= ~(1 << (index%8)); - index ++; - if(index > TCP_WINDOW_SIZE) - index -= TCP_WINDOW_SIZE; - length --; - } - while( length > 7 ) - { - Connection->FuturePacketData[index] = 0; - Connection->FuturePacketValidBytes[index/8] = 0; - length -= 8; - index += 8; - if(index > TCP_WINDOW_SIZE) - index -= TCP_WINDOW_SIZE; - } - while(length) - { - Connection->FuturePacketData[index] = 0; - Connection->FuturePacketData[index/8] &= ~(1 << (index%8)); - index ++; - if(index > TCP_WINDOW_SIZE) - index -= TCP_WINDOW_SIZE; - length --; - } - - #else - tTCPStoredPacket *pkt; - for(;;) - { - SHORTLOCK( &Connection->lFuturePackets ); - - // Clear out duplicates from cache - // - If a packet has just been recieved, and it is expected, then - // (since NextSequenceRcv = rcvd->Sequence + rcvd->Length) all - // packets in cache that are smaller than the next expected - // are now defunct. - pkt = Connection->FuturePackets; - while(pkt && pkt->Sequence < Connection->NextSequenceRcv) - { - tTCPStoredPacket *next = pkt->Next; - free(pkt); - pkt = next; - } - - // If there's no packets left in cache, stop looking - if(!pkt || pkt->Sequence > Connection->NextSequenceRcv) { - SHORTREL( &Connection->lFuturePackets ); - return; - } - - // Delete packet from future list - Connection->FuturePackets = pkt->Next; - - // Release list - SHORTREL( &Connection->lFuturePackets ); - - // Looks like we found one - TCP_INT_AppendRecieved(Connection, pkt); - Connection->NextSequenceRcv += pkt->Length; - free(pkt); - } - #endif -} - -/** - * \fn Uint16 TCP_GetUnusedPort() - * \brief Gets an unused port and allocates it - */ -Uint16 TCP_GetUnusedPort() -{ - Uint16 ret; - - // Get Next outbound port - ret = giTCP_NextOutPort++; - while( gaTCP_PortBitmap[ret/32] & (1UL << (ret%32)) ) - { - ret ++; - giTCP_NextOutPort++; - if(giTCP_NextOutPort == 0x10000) { - ret = giTCP_NextOutPort = TCP_MIN_DYNPORT; - } - } - - // Mark the new port as used - gaTCP_PortBitmap[ret/32] |= 1 << (ret%32); - - return ret; -} - -/** - * \fn int TCP_AllocatePort(Uint16 Port) - * \brief Marks a port as used - */ -int TCP_AllocatePort(Uint16 Port) -{ - // Check if the port has already been allocated - if( gaTCP_PortBitmap[Port/32] & (1 << (Port%32)) ) - return 0; - - // Allocate - gaTCP_PortBitmap[Port/32] |= 1 << (Port%32); - - return 1; -} - -/** - * \fn int TCP_DeallocatePort(Uint16 Port) - * \brief Marks a port as unused - */ -int TCP_DeallocatePort(Uint16 Port) -{ - // Check if the port has already been allocated - if( !(gaTCP_PortBitmap[Port/32] & (1 << (Port%32))) ) - return 0; - - // Allocate - gaTCP_PortBitmap[Port/32] &= ~(1 << (Port%32)); - - return 1; -} - -// --- Server -tVFS_Node *TCP_Server_Init(tInterface *Interface) -{ - tTCPListener *srv; - - srv = calloc( 1, sizeof(tTCPListener) ); - - if( srv == NULL ) { - Log_Warning("TCP", "malloc failed for listener (%i) bytes", sizeof(tTCPListener)); - return NULL; - } - - srv->Interface = Interface; - srv->Port = 0; - srv->NextID = 0; - srv->Connections = NULL; - srv->ConnectionsTail = NULL; - srv->NewConnections = NULL; - srv->Next = NULL; - srv->Node.Flags = VFS_FFLAG_DIRECTORY; - srv->Node.Size = -1; - srv->Node.ImplPtr = srv; - srv->Node.NumACLs = 1; - srv->Node.ACLs = &gVFS_ACL_EveryoneRW; - srv->Node.Type = &gTCP_ServerNodeType; - - SHORTLOCK(&glTCP_Listeners); - srv->Next = gTCP_Listeners; - gTCP_Listeners = srv; - SHORTREL(&glTCP_Listeners); - - return &srv->Node; -} - -/** - * \brief Wait for a new connection and return the connection ID - * \note Blocks until a new connection is made - * \param Node Server node - * \param Pos Position (ignored) - */ -char *TCP_Server_ReadDir(tVFS_Node *Node, int Pos) -{ - tTCPListener *srv = Node->ImplPtr; - tTCPConnection *conn; - char *ret; - - ENTER("pNode iPos", Node, Pos); - - Log_Log("TCP", "Thread %i waiting for a connection", Threads_GetTID()); - for(;;) - { - SHORTLOCK( &srv->lConnections ); - if( srv->NewConnections != NULL ) break; - SHORTREL( &srv->lConnections ); - Threads_Yield(); // TODO: Sleep until poked - } - - - // Increment the new list (the current connection is still on the - // normal list) - conn = srv->NewConnections; - srv->NewConnections = conn->Next; - - if( srv->NewConnections == NULL ) - VFS_MarkAvaliable( Node, 0 ); - - SHORTREL( &srv->lConnections ); - - LOG("conn = %p", conn); - LOG("srv->Connections = %p", srv->Connections); - LOG("srv->NewConnections = %p", srv->NewConnections); - LOG("srv->ConnectionsTail = %p", srv->ConnectionsTail); - - ret = malloc(9); - itoa(ret, conn->Node.ImplInt, 16, 8, '0'); - Log_Log("TCP", "Thread %i got '%s'", Threads_GetTID(), ret); - LEAVE('s', ret); - return ret; -} - -/** - * \brief Gets a client connection node - * \param Node Server node - * \param Name Hexadecimal ID of the node - */ -tVFS_Node *TCP_Server_FindDir(tVFS_Node *Node, const char *Name) -{ - tTCPConnection *conn; - tTCPListener *srv = Node->ImplPtr; - char tmp[9]; - int id = atoi(Name); - - ENTER("pNode sName", Node, Name); - - // Check for a non-empty name - if( Name[0] ) - { - // Sanity Check - itoa(tmp, id, 16, 8, '0'); - if(strcmp(tmp, Name) != 0) { - LOG("'%s' != '%s' (%08x)", Name, tmp, id); - LEAVE('n'); - return NULL; - } - - Log_Debug("TCP", "srv->Connections = %p", srv->Connections); - Log_Debug("TCP", "srv->NewConnections = %p", srv->NewConnections); - Log_Debug("TCP", "srv->ConnectionsTail = %p", srv->ConnectionsTail); - - // Search - SHORTLOCK( &srv->lConnections ); - for(conn = srv->Connections; - conn; - conn = conn->Next) - { - LOG("conn->Node.ImplInt = %i", conn->Node.ImplInt); - if(conn->Node.ImplInt == id) break; - } - SHORTREL( &srv->lConnections ); - - // If not found, ret NULL - if(!conn) { - LOG("Connection %i not found", id); - LEAVE('n'); - return NULL; - } - } - // Empty Name - Check for a new connection and if it's there, open it - else - { - SHORTLOCK( &srv->lConnections ); - conn = srv->NewConnections; - if( conn != NULL ) - srv->NewConnections = conn->Next; - VFS_MarkAvaliable( Node, srv->NewConnections != NULL ); - SHORTREL( &srv->lConnections ); - if( !conn ) { - LOG("No new connections"); - LEAVE('n'); - return NULL; - } - } - - // Return node - LEAVE('p', &conn->Node); - return &conn->Node; -} - -/** - * \brief Handle IOCtl calls - */ -int TCP_Server_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - tTCPListener *srv = Node->ImplPtr; - - switch(ID) - { - case 4: // Get/Set Port - if(!Data) // Get Port - return srv->Port; - - if(srv->Port) // Wait, you can't CHANGE the port - return -1; - - if(!CheckMem(Data, sizeof(Uint16))) // Sanity check - return -1; - - // Permissions check - if(Threads_GetUID() != 0 - && *(Uint16*)Data != 0 - && *(Uint16*)Data < 1024) - return -1; - - // TODO: Check if a port is in use - - // Set Port - srv->Port = *(Uint16*)Data; - if(srv->Port == 0) // Allocate a random port - srv->Port = TCP_GetUnusedPort(); - else // Else, mark this as used - TCP_AllocatePort(srv->Port); - - Log_Log("TCP", "Server %p listening on port %i", srv, srv->Port); - - return srv->Port; - } - return 0; -} - -void TCP_Server_Close(tVFS_Node *Node) -{ - free(Node->ImplPtr); -} - -// --- Client -/** - * \brief Create a client node - */ -tVFS_Node *TCP_Client_Init(tInterface *Interface) -{ - tTCPConnection *conn = calloc( sizeof(tTCPConnection) + TCP_WINDOW_SIZE + TCP_WINDOW_SIZE/8, 1 ); - - conn->State = TCP_ST_CLOSED; - conn->Interface = Interface; - conn->LocalPort = -1; - conn->RemotePort = -1; - - conn->Node.ImplPtr = conn; - conn->Node.NumACLs = 1; - conn->Node.ACLs = &gVFS_ACL_EveryoneRW; - conn->Node.Type = &gTCP_ClientNodeType; - - conn->RecievedBuffer = RingBuffer_Create( TCP_RECIEVE_BUFFER_SIZE ); - #if 0 - conn->SentBuffer = RingBuffer_Create( TCP_SEND_BUFFER_SIZE ); - Semaphore_Init(conn->SentBufferSpace, 0, TCP_SEND_BUFFER_SIZE, "TCP SentBuffer", conn->Name); - #endif - - #if CACHE_FUTURE_PACKETS_IN_BYTES - // Future recieved data (ahead of the expected sequence number) - conn->FuturePacketData = (Uint8*)conn + sizeof(tTCPConnection); - conn->FuturePacketValidBytes = conn->FuturePacketData + TCP_WINDOW_SIZE; - #endif - - SHORTLOCK(&glTCP_OutbountCons); - conn->Next = gTCP_OutbountCons; - gTCP_OutbountCons = conn; - SHORTREL(&glTCP_OutbountCons); - - return &conn->Node; -} - -/** - * \brief Wait for a packet and return it - * \note If \a Length is smaller than the size of the packet, the rest - * of the packet's data will be discarded. - */ -Uint64 TCP_Client_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - tTCPConnection *conn = Node->ImplPtr; - size_t len; - - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - LOG("conn = %p {State:%i}", conn, conn->State); - - // Check if connection is estabilishing - // - TODO: Sleep instead (maybe using VFS_SelectNode to wait for the - // data to be availiable - while( conn->State == TCP_ST_SYN_RCVD || conn->State == TCP_ST_SYN_SENT ) - Threads_Yield(); - - // If the conneciton is not open, then clean out the recieved buffer - if( conn->State != TCP_ST_OPEN ) - { - Mutex_Acquire( &conn->lRecievedPackets ); - len = RingBuffer_Read( Buffer, conn->RecievedBuffer, Length ); - Mutex_Release( &conn->lRecievedPackets ); - - if( len == 0 ) { - VFS_MarkAvaliable(Node, 0); - LEAVE('i', -1); - return -1; - } - - LEAVE('i', len); - return len; - } - - // Wait - VFS_SelectNode(Node, VFS_SELECT_READ|VFS_SELECT_ERROR, NULL, "TCP_Client_Read"); - - // Lock list and read as much as possible (up to `Length`) - Mutex_Acquire( &conn->lRecievedPackets ); - len = RingBuffer_Read( Buffer, conn->RecievedBuffer, Length ); - - if( len == 0 || conn->RecievedBuffer->Length == 0 ) { - LOG("Marking as none avaliable (len = %i)", len); - VFS_MarkAvaliable(Node, 0); - } - - // Release the lock (we don't need it any more) - Mutex_Release( &conn->lRecievedPackets ); - - LEAVE('i', len); - return len; -} - -/** - * \brief Send a data packet on a connection - */ -void TCP_INT_SendDataPacket(tTCPConnection *Connection, size_t Length, const void *Data) -{ - char buf[sizeof(tTCPHeader)+Length]; - tTCPHeader *packet = (void*)buf; - - packet->SourcePort = htons(Connection->LocalPort); - packet->DestPort = htons(Connection->RemotePort); - packet->DataOffset = (sizeof(tTCPHeader)/4)*16; - packet->WindowSize = htons(TCP_WINDOW_SIZE); - - packet->AcknowlegementNumber = htonl(Connection->NextSequenceRcv); - packet->SequenceNumber = htonl(Connection->NextSequenceSend); - packet->Flags = TCP_FLAG_PSH|TCP_FLAG_ACK; // Hey, ACK if you can! - - memcpy(packet->Options, Data, Length); - - Log_Debug("TCP", "Send sequence 0x%08x", Connection->NextSequenceSend); -#if HEXDUMP_OUTGOING - Debug_HexDump("TCP_INT_SendDataPacket: Data = ", Data, Length); -#endif - - TCP_SendPacket( Connection, sizeof(tTCPHeader)+Length, packet ); - - Connection->NextSequenceSend += Length; -} - -/** - * \brief Send some bytes on a connection - */ -Uint64 TCP_Client_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - tTCPConnection *conn = Node->ImplPtr; - size_t rem = Length; - - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - -// #if DEBUG -// Debug_HexDump("TCP_Client_Write: Buffer = ", -// Buffer, Length); -// #endif - - // Check if connection is open - while( conn->State == TCP_ST_SYN_RCVD || conn->State == TCP_ST_SYN_SENT ) - Threads_Yield(); - - if( conn->State != TCP_ST_OPEN ) { - VFS_MarkError(Node, 1); - LEAVE('i', -1); - return -1; - } - - do - { - int len = (rem < TCP_MAX_PACKET_SIZE) ? rem : TCP_MAX_PACKET_SIZE; - - #if 0 - // Wait for space in the buffer - Semaphore_Signal( &Connection->SentBufferSpace, len ); - - // Save data to buffer (and update the length read by the ammount written) - len = RingBuffer_Write( &Connection->SentBuffer, Buffer, len); - #endif - - // Send packet - TCP_INT_SendDataPacket(conn, len, Buffer); - - Buffer += len; - rem -= len; - } while( rem > 0 ); - - LEAVE('i', Length); - return Length; -} - -/** - * \brief Open a connection to another host using TCP - * \param Conn Connection structure - */ -void TCP_StartConnection(tTCPConnection *Conn) -{ - tTCPHeader hdr = {0}; - - Conn->State = TCP_ST_SYN_SENT; - - hdr.SourcePort = htons(Conn->LocalPort); - hdr.DestPort = htons(Conn->RemotePort); - Conn->NextSequenceSend = rand(); - hdr.SequenceNumber = htonl(Conn->NextSequenceSend); - hdr.DataOffset = (sizeof(tTCPHeader)/4) << 4; - hdr.Flags = TCP_FLAG_SYN; - hdr.WindowSize = htons(TCP_WINDOW_SIZE); // Max - hdr.Checksum = 0; // TODO - - TCP_SendPacket( Conn, sizeof(tTCPHeader), &hdr ); - - Conn->NextSequenceSend ++; - Conn->State = TCP_ST_SYN_SENT; - - return ; -} - -/** - * \brief Control a client socket - */ -int TCP_Client_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - tTCPConnection *conn = Node->ImplPtr; - - ENTER("pNode iID pData", Node, ID, Data); - - switch(ID) - { - case 4: // Get/Set local port - if(!Data) - LEAVE_RET('i', conn->LocalPort); - if(conn->State != TCP_ST_CLOSED) - LEAVE_RET('i', -1); - if(!CheckMem(Data, sizeof(Uint16))) - LEAVE_RET('i', -1); - - if(Threads_GetUID() != 0 && *(Uint16*)Data < 1024) - LEAVE_RET('i', -1); - - conn->LocalPort = *(Uint16*)Data; - LEAVE_RET('i', conn->LocalPort); - - case 5: // Get/Set remote port - if(!Data) LEAVE_RET('i', conn->RemotePort); - if(conn->State != TCP_ST_CLOSED) LEAVE_RET('i', -1); - if(!CheckMem(Data, sizeof(Uint16))) LEAVE_RET('i', -1); - conn->RemotePort = *(Uint16*)Data; - LEAVE_RET('i', conn->RemotePort); - - case 6: // Set Remote IP - if( conn->State != TCP_ST_CLOSED ) - LEAVE_RET('i', -1); - if( conn->Interface->Type == 4 ) - { - if(!CheckMem(Data, sizeof(tIPv4))) LEAVE_RET('i', -1); - conn->RemoteIP.v4 = *(tIPv4*)Data; - } - else if( conn->Interface->Type == 6 ) - { - if(!CheckMem(Data, sizeof(tIPv6))) LEAVE_RET('i', -1); - conn->RemoteIP.v6 = *(tIPv6*)Data; - } - LEAVE_RET('i', 0); - - case 7: // Connect - if(conn->LocalPort == 0xFFFF) - conn->LocalPort = TCP_GetUnusedPort(); - if(conn->RemotePort == -1) - LEAVE_RET('i', 0); - - { - tTime timeout_end = now() + conn->Interface->TimeoutDelay; - - TCP_StartConnection(conn); - // TODO: Wait for connection to open - while( conn->State == TCP_ST_SYN_SENT && timeout_end > now() ) { - Threads_Yield(); - } - if( conn->State == TCP_ST_SYN_SENT ) - LEAVE_RET('i', 0); - } - - LEAVE_RET('i', 1); - - // Get recieve buffer length - case 8: - LEAVE_RET('i', conn->RecievedBuffer->Length); - } - - return 0; -} - -void TCP_Client_Close(tVFS_Node *Node) -{ - tTCPConnection *conn = Node->ImplPtr; - tTCPHeader packet; - - ENTER("pNode", Node); - - if( conn->State == TCP_ST_CLOSE_WAIT || conn->State == TCP_ST_OPEN ) - { - packet.SourcePort = htons(conn->LocalPort); - packet.DestPort = htons(conn->RemotePort); - packet.DataOffset = (sizeof(tTCPHeader)/4)*16; - packet.WindowSize = TCP_WINDOW_SIZE; - - packet.AcknowlegementNumber = 0; - packet.SequenceNumber = htonl(conn->NextSequenceSend); - packet.Flags = TCP_FLAG_FIN; - - TCP_SendPacket( conn, sizeof(tTCPHeader), &packet ); - } - - switch( conn->State ) - { - case TCP_ST_CLOSE_WAIT: - conn->State = TCP_ST_LAST_ACK; - break; - case TCP_ST_OPEN: - conn->State = TCP_ST_FIN_WAIT1; - while( conn->State == TCP_ST_FIN_WAIT1 ) Threads_Yield(); - break; - default: - Log_Warning("TCP", "Unhandled connection state in TCP_Client_Close"); - break; - } - - free(conn); - - LEAVE('-'); -} - -/** - * \brief Checks if a value is between two others (after taking into account wrapping) - */ -int WrapBetween(Uint32 Lower, Uint32 Value, Uint32 Higher, Uint32 MaxValue) -{ - if( MaxValue < 0xFFFFFFFF ) - { - Lower %= MaxValue + 1; - Value %= MaxValue + 1; - Higher %= MaxValue + 1; - } - - // Simple Case, no wrap ? - // Lower Value Higher - // | ... + ... + ... + ... | - - if( Lower < Higher ) { - return Lower < Value && Value < Higher; - } - // Higher has wrapped below lower - - // Value > Lower ? - // Higher Lower Value - // | ... + ... + ... + ... | - if( Value > Lower ) { - return 1; - } - - // Value < Higher ? - // Value Higher Lower - // | ... + ... + ... + ... | - if( Value < Higher ) { - return 1; - } - - return 0; -} diff --git a/Modules/IPStack/tcp.h b/Modules/IPStack/tcp.h deleted file mode 100644 index 6aa404f6..00000000 --- a/Modules/IPStack/tcp.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Acess2 IP Stack - * - TCP Definitions - */ -#ifndef _TCP_H_ -#define _TCP_H_ - -#include "ipstack.h" -#include // tRingBuffer - -typedef struct sTCPHeader tTCPHeader; -typedef struct sTCPListener tTCPListener; -typedef struct sTCPStoredPacket tTCPStoredPacket; -typedef struct sTCPConnection tTCPConnection; - -struct sTCPHeader -{ - Uint16 SourcePort; - Uint16 DestPort; - Uint32 SequenceNumber; - Uint32 AcknowlegementNumber; - #if 0 - struct { // Lowest to highest - unsigned Reserved: 4; - unsigned DataOffset: 4; // Size of the header in 32-bit words - } __attribute__ ((packed)); - #else - Uint8 DataOffset; - #endif - #if 0 - struct { // Lowest to Highest - unsigned FIN: 1; // Last packet - unsigned SYN: 1; // Synchronise Sequence Numbers - unsigned RST: 1; // Reset Connection - unsigned PSH: 1; // Push Function - unsigned ACK: 1; // Acknowlegement field is significant - unsigned URG: 1; // Urgent pointer is significant - unsigned ECE: 1; // ECN-Echo - unsigned CWR: 1; // Congestion Window Reduced - } __attribute__ ((packed)) Flags; - #else - Uint8 Flags; - #endif - Uint16 WindowSize; - - Uint16 Checksum; - Uint16 UrgentPointer; - - Uint8 Options[]; -} __attribute__ ((packed)); - -enum eTCPFlags -{ - TCP_FLAG_FIN = 0x01, - TCP_FLAG_SYN = 0x02, - TCP_FLAG_RST = 0x04, - TCP_FLAG_PSH = 0x08, - TCP_FLAG_ACK = 0x10, - TCP_FLAG_URG = 0x20, - TCP_FLAG_ECE = 0x40, - TCP_FLAG_CWR = 0x80 -}; - -struct sTCPListener -{ - struct sTCPListener *Next; //!< Next server in the list - Uint16 Port; //!< Listening port (0 disables the server) - tInterface *Interface; //!< Listening Interface - tVFS_Node Node; //!< Server Directory node - int NextID; //!< Name of the next connection - tShortSpinlock lConnections; //!< Spinlock for connections - tTCPConnection *Connections; //!< Connections (linked list) - tTCPConnection *volatile NewConnections; - tTCPConnection *ConnectionsTail; -}; - -struct sTCPStoredPacket -{ - struct sTCPStoredPacket *Next; - size_t Length; - Uint32 Sequence; - Uint8 Data[]; -}; - -enum eTCPConnectionState -{ - TCP_ST_CLOSED, // 0 - Connection invalid - - TCP_ST_SYN_SENT, // 1 - SYN sent by local, waiting for SYN-ACK - TCP_ST_SYN_RCVD, // 2 - SYN recieved, SYN-ACK sent - - TCP_ST_OPEN, // 3 - Connection open - - // Local Close - TCP_ST_FIN_WAIT1, // 4 - FIN sent, waiting for reply (ACK or FIN) - TCP_ST_FIN_WAIT2, // 5 - sent FIN acked, waiting for FIN from peer - TCP_ST_CLOSING, // 6 - Waiting for ACK of FIN (FIN sent and recieved) - TCP_ST_TIME_WAIT, // 7 - Waiting for timeout after local close - // Remote close - TCP_ST_CLOSE_WAIT, // 8 - FIN recieved, waiting for user to close (error set, wait for node close) - TCP_ST_LAST_ACK, // 9 - FIN sent and recieved, waiting for ACK - TCP_ST_FINISHED // 10 - Essentially closed, all packets are invalid -}; - -struct sTCPConnection -{ - struct sTCPConnection *Next; - enum eTCPConnectionState State; //!< Connection state (see ::eTCPConnectionState) - Uint16 LocalPort; //!< Local port - Uint16 RemotePort; //!< Remote port - tInterface *Interface; //!< Listening Interface - tVFS_Node Node; //!< Node - - Uint32 NextSequenceSend; //!< Next sequence value for outbound packets - Uint32 NextSequenceRcv; //!< Next expected sequence value for inbound - - #if 0 - /** - * \brief Non-ACKed packets - * \note Ring buffer - * \{ - */ - tMutex lNonACKedPackets; - tTCPStoredPacket *SentPackets; //!< Non-acknowleged packets - /** - * \} - */ - #endif - - /** - * \brief Unread Packets - * \note Ring buffer - * \{ - */ - tMutex lRecievedPackets; - tRingBuffer *RecievedBuffer; - /** - * \} - */ - - /** - * \brief Out of sequence packets - * \note Sorted list to improve times - * \todo Convert this to a ring buffer and a bitmap of valid bytes - * \{ - */ - #if CACHE_FUTURE_PACKETS_OR_BYTES == bytes - Uint32 HighestSequenceRcvd; //!< Highest sequence number (within window) recieved - Uint8 *FuturePacketData; //!< Future packet data (indexed by sequence number) - Uint8 *FuturePacketValidBytes; //!< Valid byte bitmap (WINDOW_SIZE/8 bytes) - #else - tShortSpinlock lFuturePackets; //!< Future packets spinlock - tTCPStoredPacket *FuturePackets; //!< Out of sequence packets - #endif - /** - * \} - */ - - union { - tIPv4 v4; - tIPv6 v6; - } RemoteIP; //!< Remote IP Address - // Type is determined by LocalInterface->Type -}; - -#endif diff --git a/Modules/IPStack/udp.c b/Modules/IPStack/udp.c deleted file mode 100644 index cbdcd56b..00000000 --- a/Modules/IPStack/udp.c +++ /dev/null @@ -1,442 +0,0 @@ -/* - * Acess2 IP Stack - * - UDP Handling - */ -#include "ipstack.h" -#include -#include "udp.h" - -#define UDP_ALLOC_BASE 0xC000 - -// === PROTOTYPES === -void UDP_Initialise(); -void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer); -void UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer); -void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, const void *Address, Uint16 Port, const void *Data, size_t Length); -// --- Client Channels -tVFS_Node *UDP_Channel_Init(tInterface *Interface); -Uint64 UDP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -Uint64 UDP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); - int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data); -void UDP_Channel_Close(tVFS_Node *Node); -// --- Helpers -Uint16 UDP_int_AllocatePort(); - int UDP_int_MarkPortAsUsed(Uint16 Port); -void UDP_int_FreePort(Uint16 Port); - -// === GLOBALS === -tVFS_NodeType gUDP_NodeType = { - .Read = UDP_Channel_Read, - .Write = UDP_Channel_Write, - .IOCtl = UDP_Channel_IOCtl, - .Close = UDP_Channel_Close -}; -tMutex glUDP_Channels; -tUDPChannel *gpUDP_Channels; - -tMutex glUDP_Ports; -Uint32 gUDP_Ports[0x10000/32]; - -tSocketFile gUDP_SocketFile = {NULL, "udp", UDP_Channel_Init}; - -// === CODE === -/** - * \fn void TCP_Initialise() - * \brief Initialise the TCP Layer - */ -void UDP_Initialise() -{ - IPStack_AddFile(&gUDP_SocketFile); - //IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket, UDP_Unreachable); - IPv4_RegisterCallback(IP4PROT_UDP, UDP_GetPacket); -} - -/** - * \brief Scan a list of tUDPChannels and find process the first match - * \return 0 if no match was found, -1 on error and 1 if a match was found - */ -int UDP_int_ScanList(tUDPChannel *List, tInterface *Interface, void *Address, int Length, void *Buffer) -{ - tUDPHeader *hdr = Buffer; - tUDPChannel *chan; - tUDPPacket *pack; - int len; - - for(chan = List; - chan; - chan = chan->Next) - { - // Match local endpoint - if(chan->Interface && chan->Interface != Interface) continue; - if(chan->LocalPort != ntohs(hdr->DestPort)) continue; - - // Check for remote port restriction - if(chan->Remote.Port && chan->Remote.Port != ntohs(hdr->SourcePort)) - continue; - // Check for remote address restriction - if(chan->RemoteMask) - { - if(chan->Remote.AddrType != Interface->Type) - continue; - if(!IPStack_CompareAddress(Interface->Type, Address, - &chan->Remote.Addr, chan->RemoteMask) - ) - continue; - } - - Log_Log("UDP", "Recieved packet for %p", chan); - // Create the cached packet - len = ntohs(hdr->Length); - pack = malloc(sizeof(tUDPPacket) + len); - pack->Next = NULL; - memcpy(&pack->Remote.Addr, Address, IPStack_GetAddressSize(Interface->Type)); - pack->Remote.Port = ntohs(hdr->SourcePort); - pack->Remote.AddrType = Interface->Type; - pack->Length = len; - memcpy(pack->Data, hdr->Data, len); - - // Add the packet to the channel's queue - SHORTLOCK(&chan->lQueue); - if(chan->Queue) - chan->QueueEnd->Next = pack; - else - chan->QueueEnd = chan->Queue = pack; - SHORTREL(&chan->lQueue); - VFS_MarkAvaliable(&chan->Node, 1); - Mutex_Release(&glUDP_Channels); - return 1; - } - return 0; -} - -/** - * \fn void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) - * \brief Handles a packet from the IP Layer - */ -void UDP_GetPacket(tInterface *Interface, void *Address, int Length, void *Buffer) -{ - tUDPHeader *hdr = Buffer; - - Log_Debug("UDP", "hdr->SourcePort = %i", ntohs(hdr->SourcePort)); - Log_Debug("UDP", "hdr->DestPort = %i", ntohs(hdr->DestPort)); - Log_Debug("UDP", "hdr->Length = %i", ntohs(hdr->Length)); - Log_Debug("UDP", "hdr->Checksum = 0x%x", ntohs(hdr->Checksum)); - - // Check registered connections - Mutex_Acquire(&glUDP_Channels); - UDP_int_ScanList(gpUDP_Channels, Interface, Address, Length, Buffer); - Mutex_Release(&glUDP_Channels); -} - -/** - * \brief Handle an ICMP Unrechable Error - */ -void UDP_Unreachable(tInterface *Interface, int Code, void *Address, int Length, void *Buffer) -{ - -} - -/** - * \brief Send a packet - * \param Channel Channel to send the packet from - * \param Data Packet data - * \param Length Length in bytes of packet data - */ -void UDP_SendPacketTo(tUDPChannel *Channel, int AddrType, const void *Address, Uint16 Port, const void *Data, size_t Length) -{ - tUDPHeader *hdr; - - if(Channel->Interface && Channel->Interface->Type != AddrType) return ; - - switch(AddrType) - { - case 4: - // Create the packet - hdr = malloc(sizeof(tUDPHeader)+Length); - hdr->SourcePort = htons( Channel->LocalPort ); - hdr->DestPort = htons( Port ); - hdr->Length = htons( sizeof(tUDPHeader) + Length ); - hdr->Checksum = 0; // Checksum can be zero on IPv4 - memcpy(hdr->Data, Data, Length); - // Pass on the the IPv4 Layer - // TODO: What if Channel->Interface is NULL here? - IPv4_SendPacket(Channel->Interface, *(tIPv4*)Address, IP4PROT_UDP, 0, sizeof(tUDPHeader)+Length, hdr); - // Free allocated packet - free(hdr); - break; - } -} - -// --- Client Channels -tVFS_Node *UDP_Channel_Init(tInterface *Interface) -{ - tUDPChannel *new; - new = calloc( sizeof(tUDPChannel), 1 ); - new->Interface = Interface; - new->Node.ImplPtr = new; - new->Node.NumACLs = 1; - new->Node.ACLs = &gVFS_ACL_EveryoneRW; - new->Node.Type = &gUDP_NodeType; - - Mutex_Acquire(&glUDP_Channels); - new->Next = gpUDP_Channels; - gpUDP_Channels = new; - Mutex_Release(&glUDP_Channels); - - return &new->Node; -} - -/** - * \brief Read from the channel file (wait for a packet) - */ -Uint64 UDP_Channel_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - tUDPChannel *chan = Node->ImplPtr; - tUDPPacket *pack; - tUDPEndpoint *ep; - int ofs, addrlen; - - if(chan->LocalPort == 0) { - Log_Notice("UDP", "Channel %p sent with no local port", chan); - return 0; - } - - while(chan->Queue == NULL) Threads_Yield(); - - for(;;) - { - VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "UDP_Channel_Read"); - SHORTLOCK(&chan->lQueue); - if(chan->Queue == NULL) { - SHORTREL(&chan->lQueue); - continue; - } - pack = chan->Queue; - chan->Queue = pack->Next; - if(!chan->Queue) { - chan->QueueEnd = NULL; - VFS_MarkAvaliable(Node, 0); // Nothing left - } - SHORTREL(&chan->lQueue); - break; - } - - // Check that the header fits - addrlen = IPStack_GetAddressSize(pack->Remote.AddrType); - ep = Buffer; - ofs = 4 + addrlen; - if(Length < ofs) { - free(pack); - Log_Notice("UDP", "Insuficient space for header in buffer (%i < %i)", (int)Length, ofs); - return 0; - } - - // Fill header - ep->Port = pack->Remote.Port; - ep->AddrType = pack->Remote.AddrType; - memcpy(&ep->Addr, &pack->Remote.Addr, addrlen); - - // Copy packet data - if(Length > ofs + pack->Length) Length = ofs + pack->Length; - memcpy((char*)Buffer + ofs, pack->Data, Length - ofs); - - // Free cached packet - free(pack); - - return Length; -} - -/** - * \brief Write to the channel file (send a packet) - */ -Uint64 UDP_Channel_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - tUDPChannel *chan = Node->ImplPtr; - const tUDPEndpoint *ep; - const void *data; - int ofs; - if(chan->LocalPort == 0) return 0; - - ep = Buffer; - ofs = 2 + 2 + IPStack_GetAddressSize( ep->AddrType ); - - data = (const char *)Buffer + ofs; - - UDP_SendPacketTo(chan, ep->AddrType, &ep->Addr, ep->Port, data, (size_t)Length - ofs); - - return 0; -} - -/** - * \brief Names for channel IOCtl Calls - */ -static const char *casIOCtls_Channel[] = { - DRV_IOCTLNAMES, - "getset_localport", - "getset_remoteport", - "getset_remotemask", - "set_remoteaddr", - NULL - }; -/** - * \brief Channel IOCtls - */ -int UDP_Channel_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - tUDPChannel *chan = Node->ImplPtr; - ENTER("pNode iID pData", Node, ID, Data); - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_MISC, "UDP Channel", 0x100, casIOCtls_Channel); - - case 4: // getset_localport (returns bool success) - if(!Data) LEAVE_RET('i', chan->LocalPort); - if(!CheckMem( Data, sizeof(Uint16) ) ) { - LOG("Invalid pointer %p", Data); - LEAVE_RET('i', -1); - } - // Set port - chan->LocalPort = *(Uint16*)Data; - // Permissions check (Ports lower than 1024 are root-only) - if(chan->LocalPort != 0 && chan->LocalPort < 1024) { - if( Threads_GetUID() != 0 ) { - LOG("Attempt by non-superuser to listen on port %i", chan->LocalPort); - chan->LocalPort = 0; - LEAVE_RET('i', -1); - } - } - // Allocate a random port if requested - if( chan->LocalPort == 0 ) - chan->LocalPort = UDP_int_AllocatePort(); - else - { - // Else, mark the requested port as used - if( UDP_int_MarkPortAsUsed(chan->LocalPort) == 0 ) { - LOG("Port %i us currently in use", chan->LocalPort); - chan->LocalPort = 0; - LEAVE_RET('i', 0); - } - LEAVE_RET('i', 1); - } - LEAVE_RET('i', 1); - - case 5: // getset_remoteport (returns bool success) - if(!Data) LEAVE_RET('i', chan->Remote.Port); - if(!CheckMem( Data, sizeof(Uint16) ) ) { - LOG("Invalid pointer %p", Data); - LEAVE_RET('i', -1); - } - chan->Remote.Port = *(Uint16*)Data; - return 1; - - case 6: // getset_remotemask (returns bool success) - if(!Data) LEAVE_RET('i', chan->RemoteMask); - if(!CheckMem(Data, sizeof(int))) LEAVE_RET('i', -1); - if( !chan->Interface ) { - LOG("Can't set remote mask on NULL interface"); - LEAVE_RET('i', -1); - } - if( *(int*)Data > IPStack_GetAddressSize(chan->Interface->Type) ) - LEAVE_RET('i', -1); - chan->RemoteMask = *(int*)Data; - return 1; - - case 7: // set_remoteaddr (returns bool success) - if( !chan->Interface ) { - LOG("Can't set remote address on NULL interface"); - LEAVE_RET('i', -1); - } - if(!CheckMem(Data, IPStack_GetAddressSize(chan->Interface->Type))) { - LOG("Invalid pointer"); - LEAVE_RET('i', -1); - } - memcpy(&chan->Remote.Addr, Data, IPStack_GetAddressSize(chan->Interface->Type)); - return 0; - } - LEAVE_RET('i', 0); -} - -/** - * \brief Close and destroy an open channel - */ -void UDP_Channel_Close(tVFS_Node *Node) -{ - tUDPChannel *chan = Node->ImplPtr; - tUDPChannel *prev; - - // Remove from the main list first - Mutex_Acquire(&glUDP_Channels); - if(gpUDP_Channels == chan) - gpUDP_Channels = gpUDP_Channels->Next; - else - { - for(prev = gpUDP_Channels; - prev->Next && prev->Next != chan; - prev = prev->Next); - if(!prev->Next) - Log_Warning("UDP", "Bookeeping Fail, channel %p is not in main list", chan); - else - prev->Next = prev->Next->Next; - } - Mutex_Release(&glUDP_Channels); - - // Clear Queue - SHORTLOCK(&chan->lQueue); - while(chan->Queue) - { - tUDPPacket *tmp; - tmp = chan->Queue; - chan->Queue = tmp->Next; - free(tmp); - } - SHORTREL(&chan->lQueue); - - // Free channel structure - free(chan); -} - -/** - * \return Port Number on success, or zero on failure - */ -Uint16 UDP_int_AllocatePort() -{ - int i; - Mutex_Acquire(&glUDP_Ports); - // Fast Search - for( i = UDP_ALLOC_BASE; i < 0x10000; i += 32 ) - if( gUDP_Ports[i/32] != 0xFFFFFFFF ) - break; - if(i == 0x10000) return 0; - for( ;; i++ ) - { - if( !(gUDP_Ports[i/32] & (1 << (i%32))) ) - return i; - } - Mutex_Release(&glUDP_Ports); -} - -/** - * \brief Allocate a specific port - * \return Boolean Success - */ -int UDP_int_MarkPortAsUsed(Uint16 Port) -{ - Mutex_Acquire(&glUDP_Ports); - if( gUDP_Ports[Port/32] & (1 << (Port%32)) ) { - return 0; - Mutex_Release(&glUDP_Ports); - } - gUDP_Ports[Port/32] |= 1 << (Port%32); - Mutex_Release(&glUDP_Ports); - return 1; -} - -/** - * \brief Free an allocated port - */ -void UDP_int_FreePort(Uint16 Port) -{ - Mutex_Acquire(&glUDP_Ports); - gUDP_Ports[Port/32] &= ~(1 << (Port%32)); - Mutex_Release(&glUDP_Ports); -} diff --git a/Modules/IPStack/udp.h b/Modules/IPStack/udp.h deleted file mode 100644 index 39147894..00000000 --- a/Modules/IPStack/udp.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Acess2 IP Stack - * - UDP Definitions - */ -#ifndef _UDP_H_ -#define _UDP_H_ - -#include "ipstack.h" -#include "ipv4.h" - -typedef struct sUDPHeader tUDPHeader; -typedef struct sUDPEndpoint tUDPEndpoint; -typedef struct sUDPPacket tUDPPacket; -typedef struct sUDPChannel tUDPChannel; - -struct sUDPHeader -{ - Uint16 SourcePort; - Uint16 DestPort; - Uint16 Length; - Uint16 Checksum; - Uint8 Data[]; -}; - -struct sUDPEndpoint -{ - Uint16 Port; - Uint16 AddrType; - union { - tIPv4 v4; - tIPv6 v6; - } Addr; -}; - -struct sUDPPacket -{ - struct sUDPPacket *Next; - tUDPEndpoint Remote; - size_t Length; - Uint8 Data[]; -}; - -struct sUDPChannel -{ - struct sUDPChannel *Next; - tInterface *Interface; - Uint16 LocalPort; - - tUDPEndpoint Remote; // Only accept packets form this address/port pair - int RemoteMask; // Mask on the address - - tVFS_Node Node; - tShortSpinlock lQueue; - tUDPPacket * volatile Queue; - tUDPPacket *QueueEnd; -}; - -#endif - diff --git a/Modules/Input/Makefile.tpl b/Modules/Input/Makefile.tpl deleted file mode 100644 index c36c928b..00000000 --- a/Modules/Input/Makefile.tpl +++ /dev/null @@ -1,3 +0,0 @@ -CATEGORY = Input - --include ../../Makefile.tpl diff --git a/Modules/Input/PS2KbMouse/8042.c b/Modules/Input/PS2KbMouse/8042.c deleted file mode 100644 index e11bc74f..00000000 --- a/Modules/Input/PS2KbMouse/8042.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Acess2 - * - By thePowersGang (John Hodge) - * - * 8042 (or comaptible) Driver - */ -#include -#include "common.h" - -// === PROTOTYPES === -void KBC8042_Init(void); -void KBC8042_KeyboardHandler(int IRQ, void *Ptr); -void KBC8042_MouseHandler(int IRQ, void *Ptr); -void KBC8042_EnableMouse(void); -static inline void KBC8042_SendDataAlt(Uint8 data); -static inline void KBC8042_SendData(Uint8 data); -static inline Uint8 KBC8042_ReadData(void); -static void KBC8042_SendMouseCommand(Uint8 cmd); - -// === CODE === -void KBC8042_Init(void) -{ - IRQ_AddHandler(1, KBC8042_KeyboardHandler, NULL); - IRQ_AddHandler(12, KBC8042_MouseHandler, NULL); // Set IRQ - - { - Uint8 temp; - // Attempt to get around a strange bug in Bochs/Qemu by toggling - // the controller on and off - temp = inb(0x61); - outb(0x61, temp | 0x80); - outb(0x61, temp & 0x7F); - inb(0x60); // Clear keyboard buffer - } -} - -void KBC8042_KeyboardHandler(int IRQ, void *Ptr) -{ - Uint8 scancode; - -// Log("KBC8042_KeyboardHandler: (IRQ=%i, Ptr=%p)", IRQ, Ptr); - - scancode = inb(0x60); - KB_HandleScancode( scancode ); -} - -void KBC8042_MouseHandler(int IRQ, void *Ptr) -{ - PS2Mouse_HandleInterrupt( inb(0x60) ); -} - -void KBC8042_SetLEDs(Uint8 leds) -{ - while( inb(0x64) & 2 ); // Wait for bit 2 to unset - outb(0x60, 0xED); // Send update command - - while( inb(0x64) & 2 ); // Wait for bit 2 to unset - outb(0x60, leds); -} - -void KBC8042_EnableMouse(void) -{ - Uint8 status; - Log_Log("8042", "Enabling Mouse..."); - - // Enable AUX PS/2 - KBC8042_SendDataAlt(0xA8); - - // Enable AUX PS/2 (Compaq Status Byte) - KBC8042_SendDataAlt(0x20); // Send Command - status = KBC8042_ReadData(); // Get Status - status &= ~0x20; // Clear "Disable Mouse Clock" - status |= 0x02; // Set IRQ12 Enable - KBC8042_SendDataAlt(0x60); // Send Command - KBC8042_SendData(status); // Set Status - - //mouseSendCommand(0xF6); // Set Default Settings - KBC8042_SendMouseCommand(0xF4); // Enable Packets -} - -static inline void KBC8042_SendDataAlt(Uint8 data) -{ - int timeout=100000; - while( timeout-- && inb(0x64) & 2 ); // Wait for Flag to clear - outb(0x64, data); // Send Command -} -static inline void KBC8042_SendData(Uint8 data) -{ - int timeout=100000; - while( timeout-- && inb(0x64) & 2 ); // Wait for Flag to clear - outb(0x60, data); // Send Command -} -static inline Uint8 KBC8042_ReadData(void) -{ - int timeout=100000; - while( timeout-- && (inb(0x64) & 1) == 0); // Wait for Flag to set - return inb(0x60); -} -static inline void KBC8042_SendMouseCommand(Uint8 cmd) -{ - KBC8042_SendDataAlt(0xD4); - KBC8042_SendData(cmd); -} - diff --git a/Modules/Input/PS2KbMouse/Makefile b/Modules/Input/PS2KbMouse/Makefile deleted file mode 100644 index 306e2b41..00000000 --- a/Modules/Input/PS2KbMouse/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# - -OBJ = main.o kb.o ps2mouse.o -OBJ += 8042.o pl050.o -NAME = PS2KbMouse - --include ../Makefile.tpl diff --git a/Modules/Input/PS2KbMouse/common.h b/Modules/Input/PS2KbMouse/common.h deleted file mode 100644 index 7cbffe9c..00000000 --- a/Modules/Input/PS2KbMouse/common.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Acess2 - * - * PS2 Keyboard/Mouse Driver - * - * common.h - * - Shared definitions - */ -#ifndef _COMMON_H_ -#define _COMMON_H_ - -extern int KB_Install(char **Arguments); -extern int PS2Mouse_Install(char **Arguments); - -extern void KBC8042_Init(void); -extern void KBC8042_EnableMouse(void); - -extern void PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ); -extern void PL050_EnableMouse(void); - -extern void KB_HandleScancode(Uint8 scancode); -extern void PS2Mouse_HandleInterrupt(Uint8 InputByte); - -extern void (*gpMouse_EnableFcn)(void); - -#endif diff --git a/Modules/Input/PS2KbMouse/kb.c b/Modules/Input/PS2KbMouse/kb.c deleted file mode 100644 index a121b611..00000000 --- a/Modules/Input/PS2KbMouse/kb.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Acess2 - * PS2 Keyboard Driver - */ -#include -#include -#include -#include -#include -#include "kb_kbdus.h" - -// === CONSTANTS === -#define KB_BUFFER_SIZE 1024 -#define USE_KERNEL_MAGIC 1 - -// === IMPORTS === -extern void Threads_ToggleTrace(int TID); -extern void Threads_Dump(void); -extern void Heap_Stats(void); - -// === PROTOTYPES === - int KB_Install(char **Arguments); -void KB_HandleScancode(Uint8 scancode); -void KB_UpdateLEDs(void); - int KB_IOCtl(tVFS_Node *Node, int Id, void *Data); - -// === GLOBALS === -tVFS_NodeType gKB_NodeType = { - .IOCtl = KB_IOCtl -}; -tDevFS_Driver gKB_DevInfo = { - NULL, "PS2Keyboard", - { .Type = &gKB_NodeType } -}; -tKeybardCallback gKB_Callback = NULL; -Uint32 **gpKB_Map = gpKBDUS; -Uint8 gbaKB_States[3][256]; - int gbKB_ShiftState = 0; - int gbKB_CapsState = 0; - int gbKB_KeyUp = 0; - int giKB_KeyLayer = 0; -#if USE_KERNEL_MAGIC - int gbKB_MagicState = 0; - int giKB_MagicAddress = 0; - int giKB_MagicAddressPos = 0; -#endif - -// === CODE === -/** - * \brief Install the keyboard driver - */ -int KB_Install(char **Arguments) -{ - DevFS_AddDevice( &gKB_DevInfo ); - //Log("KB_Install: Installed"); - return MODULE_ERR_OK; -} - -/** - * \brief Called on a keyboard IRQ - * \param IRQNum IRQ number (unused) - */ -void KB_HandleScancode(Uint8 scancode) -{ - Uint32 ch; - int bCaseSwitch = (gbKB_ShiftState != 0) != (gbKB_CapsState != 0); - - //Log_Debug("Keyboard", "scancode = %02x", scancode); - - // Ignore ACKs - if(scancode == 0xFA) { - // Oh man! This is anarchic (I'm leaving it here to represent - // the mess that Acess once was) - //kb_lastChar = KB_ACK; - return; - } - - // Layer +1 - if(scancode == 0xE0) { - giKB_KeyLayer = 1; - return; - } - // Layer +2 - if(scancode == 0xE1) { - giKB_KeyLayer = 2; - return; - } - - #if KB_ALT_SCANCODES - if(scancode == 0xF0) - { - gbKB_KeyUp = 1; - return; - } - #else - if(scancode & 0x80) - { - scancode &= 0x7F; - gbKB_KeyUp = 1; - } - #endif - - if( gKB_Callback ) - gKB_Callback( (giKB_KeyLayer << 8) | scancode | KEY_ACTION_RAWSYM ); - - // Translate - ch = gpKB_Map[giKB_KeyLayer*2+bCaseSwitch][scancode]; - // - Unknown characters in the shift layer fall through to lower - if(bCaseSwitch && ch == 0) - ch = gpKB_Map[giKB_KeyLayer*2][scancode]; - // Check for unknown key - if(!ch) - { - if(!gbKB_KeyUp) - Log_Warning("Keyboard", "UNK %i %x", giKB_KeyLayer, scancode); -// return ; - // Can pass through to ensure each raw message has a up/down with it - } - - // Key Up? - if (gbKB_KeyUp) - { - gbKB_KeyUp = 0; - gbaKB_States[giKB_KeyLayer][scancode] = 0; // Unset key state flag - - #if USE_KERNEL_MAGIC - if(ch == KEY_LCTRL) gbKB_MagicState &= ~1; - if(ch == KEY_LALT) gbKB_MagicState &= ~2; - #endif - - if(ch == KEY_LSHIFT) gbKB_ShiftState &= ~1; - if(ch == KEY_RSHIFT) gbKB_ShiftState &= ~2; - - // Call callback - if(gKB_Callback) gKB_Callback( ch | KEY_ACTION_RELEASE ); - - // Reset Layer - giKB_KeyLayer = 0; - return; - } - - // Refire? - if( gbaKB_States[giKB_KeyLayer][scancode] == 1 ) - { - if(gKB_Callback) gKB_Callback(ch | KEY_ACTION_REFIRE); - giKB_KeyLayer = 0; - return ; - } - - // Set the bit relating to the key - gbaKB_States[giKB_KeyLayer][scancode] = 1; - // Set shift key bits - if(ch == KEY_LSHIFT) gbKB_ShiftState |= 1; - if(ch == KEY_RSHIFT) gbKB_ShiftState |= 2; - - // Check for Caps Lock - if(ch == KEY_CAPSLOCK) { - gbKB_CapsState = !gbKB_CapsState; - KB_UpdateLEDs(); - } - - // --- Check for Kernel Magic Combos - #if USE_KERNEL_MAGIC - if(ch == KEY_LCTRL) { - gbKB_MagicState |= 1; - //Log_Log("Keyboard", "Kernel Magic LCTRL Down\n"); - } - if(ch == KEY_LALT) { - gbKB_MagicState |= 2; - //Log_Log("Keyboard", "Kernel Magic LALT Down\n"); - } - if(gbKB_MagicState == 3) - { - switch(ch) - { - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - case '8': case '9': case 'a': case 'b': - case 'c': case 'd': case 'e': case 'f': - { - char str[4] = {'0', 'x', ch, 0}; - if(giKB_MagicAddressPos == BITS/4) return; - giKB_MagicAddress |= atoi(str) << giKB_MagicAddressPos; - giKB_MagicAddressPos ++; - } - return; - - // Instruction Tracing - case 't': - Log("Toggle instruction tracing on %i\n", giKB_MagicAddress); - Threads_ToggleTrace( giKB_MagicAddress ); - giKB_MagicAddress = 0; giKB_MagicAddressPos = 0; - return; - - // Thread List Dump - case 'p': Threads_Dump(); return; - // Heap Statistics - case 'h': Heap_Stats(); return; - // Dump Structure - case 's': return; - } - } - #endif - - if(gKB_Callback) - gKB_Callback(ch | KEY_ACTION_PRESS); - - // Reset Layer - giKB_KeyLayer = 0; -} - -/** - * \fn void KB_UpdateLEDs(void) - * \brief Updates the status of the keyboard LEDs - */ -void KB_UpdateLEDs(void) -{ - Uint8 leds; - - leds = (gbKB_CapsState ? 4 : 0); - - // TODO: Update LEDS - Log_Warning("Keyboard", "TODO: Update LEDs"); -} - -static const char *csaIOCTL_NAMES[] = {DRV_IOCTLNAMES, DRV_KEYBAORD_IOCTLNAMES, NULL}; - -/** - * \fn int KB_IOCtl(tVFS_Node *Node, int Id, void *Data) - * \brief Calls an IOCtl Command - */ -int KB_IOCtl(tVFS_Node *Node, int Id, void *Data) -{ - switch(Id) - { - BASE_IOCTLS(DRV_TYPE_KEYBOARD, "KB", 0x100, csaIOCTL_NAMES); - - // Sets the Keyboard Callback - case KB_IOCTL_SETCALLBACK: - // Sanity Check - if((Uint)Data < KERNEL_BASE) return 0; - // Can only be set once - if(gKB_Callback != NULL) return 0; - // Set Callback - gKB_Callback = Data; - return 1; - - default: - return 0; - } -} diff --git a/Modules/Input/PS2KbMouse/kb_kbdus.h b/Modules/Input/PS2KbMouse/kb_kbdus.h deleted file mode 100644 index 5df32573..00000000 --- a/Modules/Input/PS2KbMouse/kb_kbdus.h +++ /dev/null @@ -1,61 +0,0 @@ - -#ifndef _KBDUS_H -#define _KBDUS_H - -// - Base (NO PREFIX) -Uint32 gpKBDUS1[256] = { - 0, - KEY_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', // 0x01 - 0x0e - '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', // 0x0f - 0x1c - KEY_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', // 0x1d - 0x28 - '`', KEY_LSHIFT,'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KEY_RSHIFT, // 0x29 - 0x3e - KEY_KPSTAR, - KEY_LALT, ' ', KEY_CAPSLOCK, - KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, - KEY_NUMLOCK, KEY_SCROLLLOCK, - KEY_KPHOME, KEY_KPUP, KEY_KPPGUP, KEY_KPMINUS, - KEY_KPLEFT, KEY_KP5, KEY_KPRIGHT, KEY_KPPLUS, - KEY_KPEND, KEY_KPDOWN, KEY_KPPGDN, - KEY_KPINS, KEY_KPDEL, - 0, 0, 0, KEY_F11, KEY_F12, 0, 0, 0, 0, 0, 0, 0, -/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; -// Shift Key pressed -Uint32 gpKBDUS1s[256] = { - 0, - KEY_ESC, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', // 0x01 - 0x0e - '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', // 0x0f - 0x1c - KEY_LCTRL, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':','"', // 0x1d - 0x28 - '~', KEY_LSHIFT,'|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', KEY_RSHIFT, // 0x29 - 0x3e - 0 - }; -// - 0xE0 Prefixed -Uint32 gpKBDUS2[256] = { -// 0 1 2 3 4 5 6 7 8 9 A B C D E F -/*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-F -/*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_KPENTER, KEY_RCTRL, 0, 0, -/*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*30*/ 0, 0, 0, 0, 0, KEY_KPSLASH, 0, 0, KEY_RALT, 0, 0, 0, 0, 0, 0, 0, -/*40*/ 0, 0, 0, 0, 0, 0, 0, KEY_HOME, KEY_UP, KEY_PGUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END, -/*50*/ KEY_DOWN, KEY_PGDOWN, KEY_INS, KEY_DEL, 0, 0, 0, 0, 0, 0, 0, KEY_LWIN, KEY_RWIN, KEY_MENU, 0, 0, -/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; -// - 0xE1 Prefixed -Uint32 gpKBDUS3[256] = { -// 0 1 2 3 4 5 6 7 8 9 A B C D E F -/*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-F -/*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PAUSE, 0, 0, -/*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*30*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*50*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - - -Uint32 *gpKBDUS[6] = { gpKBDUS1, gpKBDUS1s, gpKBDUS2, gpKBDUS2, gpKBDUS3, gpKBDUS3 }; - -#endif diff --git a/Modules/Input/PS2KbMouse/main.c b/Modules/Input/PS2KbMouse/main.c deleted file mode 100644 index c5ad4e8b..00000000 --- a/Modules/Input/PS2KbMouse/main.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Acess2 - * - * PS/2 Keboard / Mouse Driver - */ -#include -#include -#include "common.h" - -// === IMPORTS === -// TODO: Allow runtime/compile-time switching -// Maybe PCI will have it? -// Integrator-CP -#if 0 -#define KEYBOARD_IRQ 3 -#define KEYBOARD_BASE 0x18000000 -#define MOUSE_IRQ 4 -#define MOUSE_BASE 0x19000000 -#endif -// Realview -#if 1 -#define KEYBOARD_IRQ 20 -#define KEYBOARD_BASE 0x10006000 -#define MOUSE_IRQ 21 -#define MOUSE_BASE 0x10007000 -#endif - -// === PROTOTYPES === - int PS2_Install(char **Arguments); - -// === GLOBALS === -MODULE_DEFINE(0, 0x0100, Input_PS2KbMouse, PS2_Install, NULL, NULL); // Shuts the makefile up -MODULE_DEFINE(0, 0x0100, PS2Keyboard, KB_Install, NULL, "Input_PS2KbMouse", NULL); -MODULE_DEFINE(0, 0x0100, PS2Mouse, PS2Mouse_Install, NULL, "Input_PS2KbMouse", NULL); - -// === CODE === -int PS2_Install(char **Arguments) -{ - #if ARCHDIR_is_x86 || ARCHDIR_is_x86_64 - KBC8042_Init(); - gpMouse_EnableFcn = KBC8042_EnableMouse; - #elif ARCHDIR_is_armv7 - PL050_Init(KEYBOARD_BASE, KEYBOARD_IRQ, MOUSE_BASE, MOUSE_IRQ); - gpMouse_EnableFcn = PL050_EnableMouse; - #endif - - return MODULE_ERR_OK; -} diff --git a/Modules/Input/PS2KbMouse/pl050.c b/Modules/Input/PS2KbMouse/pl050.c deleted file mode 100644 index 839c0bda..00000000 --- a/Modules/Input/PS2KbMouse/pl050.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Acess2 - * - By thePowersGang (John Hodge) - * - * PL050 (or comaptible) Driver - */ -#define DEBUG 1 - -#include -#include "common.h" - -// === CONSTANTS === -#define PL050_TXBUSY 0x20 - -// === PROTOTYPES === -void PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ); -void PL050_KeyboardHandler(int IRQ, void *Ptr); -void PL050_MouseHandler(int IRQ, void *Ptr); -void PL050_EnableMouse(void); -static inline void PL050_WriteMouseData(Uint8 data); -static inline void PL050_WriteKeyboardData(Uint8 data); -static inline Uint8 PL050_ReadMouseData(void); -static inline Uint8 PL050_ReadKeyboardData(void); - -// === GLOBALS === -Uint32 *gpPL050_KeyboardBase; -Uint32 *gpPL050_MouseBase; - -// === CODE === -void PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ) -{ - if( KeyboardBase ) { - LOG("KeyboardBase = 0x%x", KeyboardBase); - gpPL050_KeyboardBase = (void*)MM_MapHWPages(KeyboardBase, 1); - LOG("gpPL050_KeyboardBase = %p", gpPL050_KeyboardBase); - IRQ_AddHandler(KeyboardIRQ, PL050_KeyboardHandler, NULL); - - gpPL050_KeyboardBase[0] = 0x10; - } - if( MouseBase ) { - gpPL050_MouseBase = (void*)MM_MapHWPages(MouseBase, 1); - IRQ_AddHandler(MouseIRQ, PL050_MouseHandler, NULL); - - gpPL050_MouseBase[0] = 0x10; - } -} - -void PL050_KeyboardHandler(int IRQ, void *Ptr) -{ - Uint8 scancode; - - scancode = PL050_ReadKeyboardData(); - KB_HandleScancode( scancode ); -} - -void PL050_MouseHandler(int IRQ, void *Ptr) -{ - PS2Mouse_HandleInterrupt( PL050_ReadMouseData() ); -} - -void PL050_SetLEDs(Uint8 leds) -{ - PL050_WriteKeyboardData(0xED); - PL050_WriteKeyboardData(leds); -} - -void PL050_EnableMouse(void) -{ - Log_Log("PL050", "Enabling Mouse..."); - - //PL050_WriteMouseData(0xD4); - //PL050_WriteMouseData(0xF6); // Set Default Settings - PL050_WriteMouseData(0xD4); - PL050_WriteMouseData(0xF4); // Enable Packets - LOG("Done"); -} - -static inline void PL050_WriteMouseData(Uint8 Data) -{ - int timeout = 10000; - - if( !gpPL050_MouseBase ) { - Log_Error("PL050", "Mouse disabled (gpPL050_MouseBase = NULL)"); - return ; - } - - ENTER("xData", Data); - - while( --timeout && (gpPL050_MouseBase[1] & PL050_TXBUSY) ); - if(timeout) - gpPL050_MouseBase[2] = Data; - else - Log_Error("PL050", "Write to mouse timed out"); - LEAVE('-'); -} - -static inline Uint8 PL050_ReadMouseData(void) -{ - if( !gpPL050_MouseBase ) { - Log_Error("PL050", "Mouse disabled (gpPL050_MouseBase = NULL)"); - return 0; - } - return gpPL050_MouseBase[2]; -} -static inline void PL050_WriteKeyboardData(Uint8 Data) -{ - int timeout = 10000; - - if( !gpPL050_KeyboardBase ) { - Log_Error("PL050", "Keyboard disabled (gpPL050_KeyboardBase = NULL)"); - return ; - } - - while( --timeout && gpPL050_KeyboardBase[1] & PL050_TXBUSY ); - if(timeout) - gpPL050_KeyboardBase[2] = Data; - else - Log_Error("PL050", "Write to keyboard timed out"); -} -static inline Uint8 PL050_ReadKeyboardData(void) -{ - if( !gpPL050_KeyboardBase ) { - Log_Error("PL050", "Keyboard disabled (gpPL050_KeyboardBase = NULL)"); - return 0; - } - - return gpPL050_KeyboardBase[2]; -} - diff --git a/Modules/Input/PS2KbMouse/ps2mouse.c b/Modules/Input/PS2KbMouse/ps2mouse.c deleted file mode 100644 index 44be3b7b..00000000 --- a/Modules/Input/PS2KbMouse/ps2mouse.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Acess2 Mouse Driver - */ -#define DEBUG 0 -#include -#include -#include -#include -#include -#include -#include "common.h" - -// == CONSTANTS == -#define NUM_AXIES 2 // X+Y -#define NUM_BUTTONS 5 // Left, Right, Scroll Click, Scroll Up, Scroll Down - -// == PROTOTYPES == -// - Internal - - int PS2Mouse_Install(char **Arguments); -void PS2Mouse_HandleInterrupt(Uint8 InputByte); -// - Filesystem - -Uint64 PS2Mouse_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -int PS2Mouse_IOCtl(tVFS_Node *Node, int ID, void *Data); - -// == GLOBALS == -void (*gpMouse_EnableFcn)(void); -// - Settings - int giMouse_Sensitivity = 1; - int giMouse_MaxX = 640, giMouse_MaxY = 480; -// - File Data -Uint8 gMouse_FileData[sizeof(tJoystick_FileHeader) + NUM_AXIES*sizeof(tJoystick_Axis) + NUM_BUTTONS]; -tJoystick_FileHeader *gMouse_FileHeader = (void *)gMouse_FileData; -tJoystick_Axis *gMouse_Axies; -Uint8 *gMouse_Buttons; -tJoystick_Callback gMouse_Callback; - int gMouse_CallbackArg; - int giMouse_AxisLimits[2]; -// - Internal State - int giMouse_Cycle = 0; // IRQ Position -Uint8 gaMouse_Bytes[4] = {0,0,0,0}; -// - Driver definition -tVFS_NodeType gMouse_NodeType = { - .Read = PS2Mouse_Read, - .IOCtl = PS2Mouse_IOCtl -}; -tDevFS_Driver gMouse_DriverStruct = { - NULL, "PS2Mouse", - { - .NumACLs = 1, .ACLs = &gVFS_ACL_EveryoneRX, - .Type = &gMouse_NodeType - } -}; - -// == CODE == -int PS2Mouse_Install(char **Arguments) -{ - - - // Set up variables - gMouse_Axies = (void*)&gMouse_FileData[4]; - gMouse_Buttons = (void*)&gMouse_Axies[NUM_AXIES]; - - gMouse_FileHeader->NAxies = 2; gMouse_FileHeader->NButtons = 3; - gMouse_Axies[0].MinValue = -10; gMouse_Axies[0].MaxValue = 10; - gMouse_Axies[1].MinValue = -10; gMouse_Axies[1].MaxValue = 10; - - // Initialise Mouse Controller - giMouse_Cycle = 0; // Set Current Cycle position - gpMouse_EnableFcn(); - - DevFS_AddDevice(&gMouse_DriverStruct); - - return MODULE_ERR_OK; -} - -/* Handle Mouse Interrupt - */ -void PS2Mouse_HandleInterrupt(Uint8 InputByte) -{ - Uint8 flags; - int d[2], d_accel[2]; - int i; - - // Gather mouse data - gaMouse_Bytes[giMouse_Cycle] = InputByte; - LOG("gaMouse_Bytes[%i] = 0x%02x", gMouse_Axies[0].MaxValue); - // - If bit 3 of the first byte is not set, it's not a valid packet? - if(giMouse_Cycle == 0 && !(gaMouse_Bytes[0] & 0x08) ) - return ; - giMouse_Cycle++; - if(giMouse_Cycle < 3) - return ; - - giMouse_Cycle = 0; - - // Actual Processing (once we have all bytes) - flags = gaMouse_Bytes[0]; - - LOG("flags = 0x%x", flags); - - // Check for X/Y Overflow - if(flags & 0xC0) return; - - // Calculate dX and dY - d[0] = gaMouse_Bytes[1]; if(flags & 0x10) d[0] = -(256-d[0]); // x - d[1] = gaMouse_Bytes[2]; if(flags & 0x20) d[1] = -(256-d[1]); // y - d[1] = -d[1]; // Y is negated - LOG("RAW dx=%i, dy=%i\n", d[0], d[1]); - // Apply scaling - // TODO: Apply a form of curve to the mouse movement (dx*log(dx), dx^k?) - // TODO: Independent sensitivities? - // TODO: Disable acceleration via a flag? - d_accel[0] = d[0]*giMouse_Sensitivity; - d_accel[1] = d[1]*giMouse_Sensitivity; - - // Set Buttons (Primary) - for( i = 0; i < 3; i ++ ) - { - Uint8 newVal = (flags & (1 << i)) ? 0xFF : 0; - if(newVal != gMouse_Buttons[i]) { - if( gMouse_Callback ) - gMouse_Callback(gMouse_CallbackArg, 0, i, newVal - gMouse_Buttons[i]); - gMouse_Buttons[i] = newVal; - } - } - - // Update X and Y Positions - for( i = 0; i < 2; i ++ ) - { - Sint16 newCursor = 0; - if( giMouse_AxisLimits[i] ) - newCursor = MIN( MAX(0, gMouse_Axies[i].CursorPos + d_accel[i]), giMouse_AxisLimits[i] );; - - if( gMouse_Callback ) - { - if(giMouse_AxisLimits[i] && gMouse_Axies[i].CursorPos != newCursor) - gMouse_Callback(gMouse_CallbackArg, 1, i, newCursor - gMouse_Axies[i].CursorPos); - if(!giMouse_AxisLimits[i] && gMouse_Axies[i].CurValue != d_accel[i]) - gMouse_Callback(gMouse_CallbackArg, 1, i, d_accel[i] - gMouse_Axies[i].CurValue); - } - - gMouse_Axies[i].CurValue = d_accel[i]; - gMouse_Axies[i].CursorPos = newCursor; - } - -// Log("Mouse at %ix%i", gMouse_Axies[0].CursorPos, gMouse_Axies[1].CursorPos); - - VFS_MarkAvaliable(&gMouse_DriverStruct.RootNode, 1); -} - -/* Read mouse state (coordinates) - */ -Uint64 PS2Mouse_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - if(Offset > sizeof(gMouse_FileData)) return 0; - if(Length > sizeof(gMouse_FileData)) Length = sizeof(gMouse_FileData); - if(Offset + Length > sizeof(gMouse_FileData)) Length = sizeof(gMouse_FileData) - Offset; - - memcpy(Buffer, &gMouse_FileData[Offset], Length); - - VFS_MarkAvaliable(Node, 0); - return Length; -} - -static const char *csaIOCtls[] = {DRV_IOCTLNAMES, DRV_JOY_IOCTLNAMES, NULL}; -/* Handle messages to the device - */ -int PS2Mouse_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - tJoystick_NumValue *info = Data; - - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_JOYSTICK, "PS2Mouse", 0x100, csaIOCtls); - - case JOY_IOCTL_SETCALLBACK: // TODO: Implement - return -1; - - case JOY_IOCTL_SETCALLBACKARG: // TODO: Implement - return -1; - - case JOY_IOCTL_GETSETAXISLIMIT: - if(!info) return 0; - if(info->Num < 0 || info->Num >= 2) return 0; - if(info->Value != -1) - giMouse_AxisLimits[info->Num] = info->Value; - return giMouse_AxisLimits[info->Num]; - - case JOY_IOCTL_GETSETAXISPOSITION: - if(!info) return 0; - if(info->Num < 0 || info->Num >= 2) return 0; - if(info->Value != -1) - gMouse_Axies[info->Num].CursorPos = info->Value; - return gMouse_Axies[info->Num].CursorPos; - - case JOY_IOCTL_GETSETAXISFLAGS: - return -1; - - case JOY_IOCTL_GETSETBUTTONFLAGS: - return -1; - - default: - return 0; - } -} - diff --git a/Modules/Interfaces/EDI/Makefile b/Modules/Interfaces/EDI/Makefile deleted file mode 100644 index a93a8b7c..00000000 --- a/Modules/Interfaces/EDI/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# -# EDI - Extensible Driver Interface -# -# Acess Interface - - -OBJ = main.o edi.o -NAME = EDI - --include ../Makefile.tpl diff --git a/Modules/Interfaces/EDI/edi/acess-edi.h b/Modules/Interfaces/EDI/edi/acess-edi.h deleted file mode 100644 index 40713886..00000000 --- a/Modules/Interfaces/EDI/edi/acess-edi.h +++ /dev/null @@ -1,41 +0,0 @@ -/*! \file acess-edi.h - * \brief Acess Specific EDI Objects - * - * Contains documentation and information for - * - Timers - */ - -/* Copyright (c) 2006 John Hodge - * Permission is granted to copy, distribute and/or modify this document - * under the terms of the GNU Free Documentation License, Version 1.2 - * or any later version published by the Free Software Foundation; - * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover - * Texts. A copy of the license is included in the file entitled "COPYING". */ - -#ifndef ACESS_EDI_H -#define ACESS_EDI_H - -#include "edi_objects.h" - -/// \brief Name of Acess EDI Time Class -#define ACESS_TIMER_CLASS "ACESSEDI-TIMER" - -#ifndef IMPLEMENTING_EDI -/*! \brief int32_t ACESSEDI-TIMER.init_timer(uint32_t Delay, void (*Callback)(int), int Arg); - * - * Takes a timer pointer and intialises the timer object to fire after \a Delay ms - * When the timer fires, \a Callback is called with \a Arg passed to it. - */ -EDI_DEFVAR int32_t (*init_timer)(object_pointer port_object, uint32_t Delay, void (*fcn)(int), int arg); - -/*! \brief void ACESSEDI-TIMER.disable_timer(); - * - * Disables the timer and prevents it from firing - * After this has been called, the timer can then be initialised again. - */ -EDI_DEFVAR void (*disable_timer)(object_pointer port_object); - - -#endif // defined(IMPLEMENTING_EDI) - -#endif diff --git a/Modules/Interfaces/EDI/edi/edi.h b/Modules/Interfaces/EDI/edi/edi.h deleted file mode 100644 index 273f7a3d..00000000 --- a/Modules/Interfaces/EDI/edi/edi.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef EDI_H - -/* Copyright (c) 2006 Eli Gottlieb. - * Permission is granted to copy, distribute and/or modify this document - * under the terms of the GNU Free Documentation License, Version 1.2 - * or any later version published by the Free Software Foundation; - * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover - * Texts. A copy of the license is included in the file entitled "COPYING". */ - -#define EDI_H -/*! \file edi.h - * \brief The unitive EDI header to include others, start EDI, and stop EDI. - * - * Data structures and algorithms this header represents: - * DATA STRUCTURE: CLASS QUOTAS - The runtime and the driver have the right to set a quota on how many objects of a given class - * owned by that party the other may construct. These quotas are kept internally by the driver or runtime, are optional and are - * exposed to the other party via the quota() function (for quotas of runtime-owned classes) and the k_quota() function pointer given - * to the runtime by the driver. - * - * ALGORITHMS: INITIALIZATION AND SHUTDOWN - On initialization of the runtime's EDI environment for this driver it calls the - * driver's driver_init() routine (which must match driver_init_t) to initialize the driver with a list of EDI objects the runtime - * thinks the driver should run with. The driver then initializes. This can include calling edi_negotiate_resources() to try and - * obtain more or different objects. Eventually driver_init() returns an edi_initialization_t structure containing its quota - * function and the list of classes belonging to the driver which the runtime can construct. Either the driver or the runtime can - * shut down EDI by calling edi_shutdown(), which in turn calls the driver's driver_finish() routine. On shutdown all objects, of - * classes belonging to both the runtime and driver, are destroyed. */ - -#include "edi_objects.h" -#include "edi_dma_streams.h" -#include "edi_pthreads.h" -#include "edi_port_io.h" -#include "edi_memory_mapping.h" -#include "edi_devices.h" -#include "edi_interrupts.h" - -/*! \brief A pointer to a function the runtime can call if it fails to construct one of the driver's classes to find out what the - * runtime's quota is for that class. - * - * A pointer to a function which takes an edi_string_t as a parameter and returns in int32_t. This function follows the same - * semantics as the quota() function, returning the number of objects of the given class that can be constructed, -1 for infinity or - * -2 for an erroneous class name. It is used to tell the runtime the location of such a function in the driver so that the runtime - * can check quotas on driver-owned classes. */ -typedef int32_t (*k_quota_t)(edi_string_t resource_class); -/*!\struct edi_initialization_t - * \brief Structure containing driver classes available to the runtime and the driver's quota function after the driver has initialized. - * - * Structure containing driver classes available to runtime, the driver's quota function and the driver's name provided to the runtime - * after the driver has initialized. driver_bus, vendor_id, and device_id are all optional fields which coders should consider - * supplementary information. Kernels can require these fields if they so please, but doing so for devices which don't run on a Vendor - * ID/Product ID supporting bus is rather unwise. */ -typedef struct { - /*!\brief The number of driver classes in the driver_classes array. */ - int32_t num_driver_classes; - /*!\brief An array of declarations of driver classes available to the runtime. - * - * This array should not necessarily contain the entire list of EDI classes implemented by the driver. Instead, it should - * contain a list of those classes which the driver has correctly initialized itself to provide instances of with full - * functionality. */ - edi_class_declaration_t *driver_classes; - /*!\brief The driver's quota function. */ - k_quota_t k_quota; - /*!\brief The driver's name. */ - edi_string_t driver_name; - /*!\brief The bus of the device this driver wants to drive, if applicable. - * - * The driver does not have to supply this field, and can also supply "MULTIPLE BUSES" here to indicate that it drives devices - * on multiple buses. */ - edi_string_t driver_bus; - /*!\brief The driver's vendor ID, if applicable. - * - * The driver does not need to supply this field, and should supply -1 to indicate that it does not wish to. */ - int16_t vendor_id; - /*!\brief The driver's device ID, if applicable. - * - * The driver does not need to supply this field, but can supply it along with vendor_id. If either vendor_id or this field are - * set to -1 the runtime should consider this field not supplied. */ - int16_t driver_id; -} edi_initialization_t; -/*!\brief A pointer to a driver's initialization function. - * - * The protocol for the driver's initialization function. The runtime gives the driver a set of EDI objects representing the - * resources it thinks the driver should run with. This function returns an edi_initialization_t structure containing declarations - * of the EDI classes the driver can make available to the runtime after initialization. If any member of that structure contains 0 - * or NULL, it is considered invalid and the runtime should destroy the driver without calling its driver_finish() routine. */ -typedef edi_initialization_t (*driver_init_t)(int32_t num_resources,edi_object_metadata_t *resources); -/*!\brief Requests more resources from the runtime. Can be called during driver initialization. - * - * Called to negotiate with the runtime for the right to create further EDI objects/obtain further resources owned by the runtime. - * When the driver calls this routine, the runtime decides whether to grant more resources. If yes, this call returns true, and the - * driver can proceed to try and create the objects it desires, in addition to destroying EDI objects it doesn't want. Otherwise, - * it returns false. - * The driver must deal with whatever value this routine returns. */ -bool edi_negotiate_resources(); - -/*! \brief Returns the driver's quota of objects for a given runtime-owned class. - * - * This function takes an edi_string_t with the name of a runtime-owned class in it and returns the number of objects of that class - * which drivers can construct, -1 for infinity, or -2 for an erroneous class name. */ -int32_t quota(edi_string_t resource_class); -/*! \brief Sends a string to the operating systems debug output or logging facilities. */ -void edi_debug_write(uint32_t debug_string_length, char *debug_string); -/*! \brief This call destroys all objects and shuts down the entire EDI environment of the driver. - * - * This function shuts down EDI as described in INITIALIZATION AND SHUTDOWN above. All objects are destroyed, EDI functions can no - * longer be successfully called, etc. This function only succeeds when EDI has already been initialized, so it returns -1 when EDI - * hasn't been, 1 on success, or 0 for all other errors. */ -int32_t shutdown_edi(void); - -/*!\brief A pointer to the driver's finishing/shutdown function. - * - * The protocol for the driver's shutting down. This function should do anything the driver wants done before it dies. */ -typedef void (*driver_finish_t)(); - -#endif diff --git a/Modules/Interfaces/EDI/edi/edi_devices.h b/Modules/Interfaces/EDI/edi/edi_devices.h deleted file mode 100644 index 245e01f3..00000000 --- a/Modules/Interfaces/EDI/edi/edi_devices.h +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef EDI_DEVICES_H - -/* Copyright (c) 2006 Eli Gottlieb. - * Permission is granted to copy, distribute and/or modify this document - * under the terms of the GNU Free Documentation License, Version 1.2 - * or any later version published by the Free Software Foundation; - * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover - * Texts. A copy of the license is included in the file entitled "COPYING". */ - -/* Edited by thePowersGang (John Hodge) June 2009 - * - Add #ifdef EDI_MAIN_FILE - */ - -#define EDI_DEVICES_H - -/*! \file edi_devices.h - * \brief Declaration and description of simple classes for implementation by EDI drivers to represent hardware devices. - * - * Data structures and algorithms this header represents: - * - * DATA STRUCTURE AND ALGORITHM: BASIC DEVICES - There are two functions, select() for waiting on devices and ioctl() for - * controlling them, common to many POSIX devices. Implementations of EDI-CHARACTER-DEVICE or EDI-BLOCK-DEVICE may implement either of - * these or both, and users of such objects much query for the methods to see if they're supported. Obviously, runtime or driver - * developers don't *need* to support these. - * - * DATA STRUCTURE AND ALGORITHM: CHARACTER DEVICES - The class EDI-CHARACTER-DEVICE provides a very basic interface to character - * devices, which read and write streams of characters. As such, this class only provides read() and write(). The calls attempt a - * likeness to POSIX. - * - * DATA STRUCTURE AND ALGORITHM: BLOCK DEVICES - The class EDI-BLOCK-DEVICE provides a very basic interface to block devices, which - * can read(), write() and seek() to blocks of a specific size in an array of blocks with a specific size. Its declarations and - * semantics should behave like those of most POSIX operating systems. - * - * Note that EDI runtimes should not implement these classes. Their declarations are provided for drivers to implement. */ - -#include "edi_objects.h" - -/* Methods common to all EDI device classes specified in this header. */ - -/*!\brief EAGAIN returned by functions for block and character devices. - * - * Means that the amount of data the device has ready is less than count. */ -#define EAGAIN -1 -/*!\brief EBADOBJ returned by functions for block and character devices. - * - * Means that the object passed as the method's this point was not a valid object of the needed class. */ -#define EBADOBJ -2 -/*!\brief EINVAL returned by functions for block and character devices. - * - * Means that the method got passed invalid parameters. */ -#ifdef EINVAL -# undef EINVAL -#endif -#define EINVAL -3 - -/*!\brief select() type to wait until device is writable. */ -#define EDI_SELECT_WRITABLE 0 -/*!\brief select() type to wait until device is readable. */ -#define EDI_SELECT_READABLE 1 - -/*!\brief Argument to seek(). Sets the block offset (ie: the "current block" index) to the given whence value. */ -#define EDI_SEEK_SET 0 -/*!\brief Argument to seek(). Sets the block offset (ie: the "current block" index) to its current value + whence. */ -#define EDI_SEEK_CURRENT 1 - -#ifdef EDI_MAIN_FILE -/*!\brief Arguments to EDI's basic select() function. */ -edi_variable_declaration_t select_arguments[2] = {{"pointer void","device",1}, - {"unsigned int32_t","select_type",1}}; -/*!\brief Declaration of EDI's basic select() function. - * - * Contrary to the POSIX version, this select() puts its error codes in its return value. */ -edi_function_declaration_t select_declaration = {"int32_t","edi_device_select",0,2,select_arguments,NULL}; -#else -extern edi_function_declaration_t select_declaration; // Declare for non main files -#endif - -#ifdef EDI_MAIN_FILE -/*!\brief Arguments to EDI's basic ioctl() function. */ -edi_variable_declaration_t ioctl_arguments[3] = {{"pointer void","device",1},{"int32_t","request",1},{"pointer void","argp",1}}; -/*!\brief Declaration of EDI's basic ioctl() function. - * - * Contrary to the POSIX version, this ioctl() puts its error codes in its return value. */ -edi_function_declaration_t ioctl_declaration = {"int32_t","edi_device_ioctl",0,3,ioctl_arguments,NULL}; -#else -extern edi_class_declaration_t ioctl_declaration; // Declare for non main files -#endif - -#ifdef EDI_MAIN_FILE -/*!\brief Declaration of the arguments EDI-CHARACTER-DEVICE's read() and write() methods. */ -edi_variable_declaration_t chardev_read_write_arguments[3] = {{"pointer void","chardev",1}, - {"pointer void","buffer",1}, - {"unsigned int32_t","char_count",1}}; -/*!\brief Declarations of the methods of EDI-CHARACTER-DEVICE, read() and write(). - * - * The code pointers of these function declarations are all given as NULL. Driver developers implementing EDI-CHARACTER-DEVICE should - * fill in these entries with pointers to their own functions. */ -EDI_DEFVAR edi_function_declaration_t chardev_methods[2]= {{"int32_t","edi_chardev_read",0,3,chardev_read_write_arguments,NULL}, - {"int32_t","edi_chardev_write",0,3,chardev_read_write_arguments,NULL}}; -/*!\brief Declaration of the EDI-CHARACTER-DEVICE class. - * - * Driver developers implementing this class should fill in their own values for constructor, destructor, and possibly even parent - * before passing the filled-in structure to the EDI runtime. */ -EDI_DEFVAR edi_class_declaration_t chardev_class = {"EDI-CHARACTER-DEVICE",0,2,chardev_methods,NULL,NULL,NULL}; -#else -extern edi_class_declaration_t chardev_class; // Declare for non main files -#endif - -#ifdef EDI_MAIN_FILE -/*!\brief Arguments to EDI-BLOCK-DEVICE's read() and write() methods. */ -edi_variable_declaration_t blockdev_read_write_arguments[3] = {{"pointer void","blockdev",1}, - {"pointer void","buffer",1}, - {"unsigned int32_t","blocks",1}}; -/*!\brief Arguments to EDI-BLOCK-DEVICE's seek() method. */ -edi_variable_declaration_t blockdev_seek_arguments[3] = {{"pointer void","blockdev",1}, - {"int32_t","offset",1}, - {"int32_t","whence",1}}; -/*!\brief Declaration of the methods of EDI-BLOCK-DEVICE, read(), write(), seek(), and get_block_size(). - * - * The code pointers of these function declarations are all given as NULL. Driver developers implementing EDI-BLOCK-DEVICE should fill - * these entries in with pointers to their own functions. */ -edi_function_declaration_t blockdev_methods[4] = {{"int32_t","edi_blockdev_read",0,3,blockdev_read_write_arguments,NULL}, - {"int32_t","edi_blockdev_write",0,3,blockdev_read_write_arguments,NULL}, - {"int32_t","edi_blockdev_seek",0,3,blockdev_seek_arguments,NULL}, - {"unsigned int32_t","edi_blockdev_get_block_size",0,0,NULL,NULL}}; -/*!\brief Declaration of the EDI-BLOCK-DEVICE class. - * - * Driver developers implementing this class should fill in their own values for constructor, destructor, and possibly even parent - * before passing the filled-in structure to the EDI runtime. */ -edi_class_declaration_t blockdev_class = {"EDI-BLOCK-DEVICE",0,4,blockdev_methods,NULL,NULL,NULL}; -#else -extern edi_class_declaration_t blockdev_class; // Declare for non main files -#endif - -#endif diff --git a/Modules/Interfaces/EDI/edi/edi_dma_streams.h b/Modules/Interfaces/EDI/edi/edi_dma_streams.h deleted file mode 100644 index 8ab80ccb..00000000 --- a/Modules/Interfaces/EDI/edi/edi_dma_streams.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef EDI_DMA_STREAMS_H - -/* Copyright (c) 2006 Eli Gottlieb. - * Permission is granted to copy, distribute and/or modify this document - * under the terms of the GNU Free Documentation License, Version 1.2 - * or any later version published by the Free Software Foundation; - * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover - * Texts. A copy of the license is included in the file entitled "COPYING". */ - -#define EDI_DMA_STREAMS_H - -/*! \file edi_dma_streams.h - * \brief EDI's stream subclass for handling Direct Memory Access hardware. - * - * Data structures and algorithms this header represents: - * - * DATA STRUCTURE: DMA STREAMS - DMA streams are objects of the class EDI-STREAM-DMA used to pass data between a buffer of - * memory and the computer's DMA hardware. It is the responsibility of the object to allocate memory for its stream memory buffer - * which can be used with DMA hardware and to program the DMA hardware for transmissions. DMA streams can be bidirectional if the - * correct DMA mode is used. */ - -#include "edi_objects.h" - -#define DMA_STREAM_CLASS "EDI-STREAM-DMA" - -/*! \brief The name of the EDI DMA stream class. - * - * An edi_string_t with the class name "EDI-STREAM-DMA" in it. */ -#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI) -const edi_string_t dma_stream_class = DMA_STREAM_CLASS; -#else -extern const edi_string_t dma_stream_class; -#endif - -#ifndef IMPLEMENTING_EDI -/*! \brief int32_t EDI-STREAM-DMA.init_dma_stream(unsigned int32_t channel,unsigned int32_t mode,unsigned int32_t buffer_pages); - * - * Pointer to the init_dma_stream() method of class EDI-STREAM-DMA, which initializes a DMA stream with a DMA channel, DMA mode, and - * the number of DMA-accessible memory pages to keep as a buffer. It will only work once per stream object. It's possible return - * values are 1 for sucess, -1 for invalid DMA channel, -2 for invalid DMA mode, -3 for inability to allocate enough buffer pages and - * 0 for all other errors. */ -EDI_DEFVAR int32_t (*init_dma_stream)(object_pointer stream, uint32_t channel, uint32_t mode, uint32_t buffer_pages); -/*! \brief int32_t EDI-STREAM-DMA.transmit(data_pointer *anchor,unsigned int32 num_bytes,bool sending); - * - * Pointer to the dma_stream_transmit() method of class EDI-STREAM-DMA, which transmits the given number of bytes of data through - * the DMA stream to/from the given anchor (either source or destination), in the given direction. It returns 1 on success, -1 on - * an uninitialized or invalid DMA stream object, -2 when the anchor was NULL or otherwise invalid, -3 if the DMA stream can't - * transmit in the given direction, and 0 for all other errors. */ -EDI_DEFVAR int32_t (*dma_stream_transmit)(object_pointer stream, data_pointer anchor, uint32_t num_bytes, bool sending); -#endif - -#endif diff --git a/Modules/Interfaces/EDI/edi/edi_interrupts.h b/Modules/Interfaces/EDI/edi/edi_interrupts.h deleted file mode 100644 index ef2ffc9f..00000000 --- a/Modules/Interfaces/EDI/edi/edi_interrupts.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef EDI_INTERRUPTS_H - -/* Copyright (c) 2006 Eli Gottlieb. - * Permission is granted to copy, distribute and/or modify this document - * under the terms of the GNU Free Documentation License, Version 1.2 - * or any later version published by the Free Software Foundation; - * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover - * Texts. A copy of the license is included in the file entitled "COPYING". */ - -#define EDI_INTERRUPTS_H - -/*! \file edi_interrupts.h - * \brief Declaration and description of EDI's interrupt handling class. - * - * Data structures and algorithms this header represents: - * DATA STRUCTURE AND ALGORITHM: INTERRUPT OBJECTS - The class EDI-INTERRUPT encapsulates the handling of machine interrupts. - * It is initialized with an interrupt number to handle and a handler routine to call when that interrupt occurs. Only a couple of - * guarantees are made to the driver regarding the runtime's implementation of interrupt handling: 1) That the driver's handler is - * called for every time the interrupt associated with a valid and initialized interrupt object occurs, in the order of the - * occurences, 2) That the runtime handle the architecture-specific (general to the entire machine, not just this device) - * end-of-interrupt code when the driver is called without first returning from the machine interrupt. Note that the runtime hands - * out interrupt numbers at its own discretion and policy. */ - -#include "edi_objects.h" - -/*! \brief Macro constant containing the name of the interrupt class - */ -#define INTERRUPTS_CLASS "EDI-INTERRUPT" -/*! \brief The name of EDI's interrupt-handling class. - * - * An edi_string_t holding the name of the runtime-implemented interrupt object class. It's value is "EDI-INTERRUPT". */ -#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI) -const edi_string_t interrupts_class = INTERRUPTS_CLASS; -#else -extern const edi_string_t interrupts_class; -#endif - -/*! \brief A pointer to an interrupt handling function. - * - * A pointer to a function called to handle interrupts. Its unsigned int32_t parameter is the interrupt number that is being - * handled. */ -typedef void (*interrupt_handler_t)(uint32_t interrupt_number); - -#ifndef IMPLEMENTING_EDI -/*! \brief Initializes an interrupt object with an interrupt number and a pointer to a handler function. - * - * A pointer to the init_interrupt() method of class EDI-INTERRUPT. This method initializes a newly-created interrupt object with an - * interrupt number and a pointer to the driver's handler of type interrupt_handler_t. It can only be called once per object, and - * returns 1 on success, fails with -1 when the interrupt number is invalid or unacceptable to the runtime, fails with -2 when the - * pointer to the driver's interrupt handler is invalid, and fails with -3 for all other errors. */ -EDI_DEFVAR int32_t (*init_interrupt)(object_pointer interrupt, uint32_t interrupt_number, interrupt_handler_t handler); -/*! \brief Get this interrupt object's interrupt number. */ -EDI_DEFVAR uint32_t (*interrupt_get_irq)(object_pointer interrupt); -/*! \brief Set a new handler for this interrupt object. */ -EDI_DEFVAR void (*interrupt_set_handler)(object_pointer interrupt, interrupt_handler_t handler); -/*! \brief Return from this interrupt, letting the runtime run any necessary End-Of-Interrupt code. - * - * A pointer to the interrupt_return() method of class EDI-INTERRUPT. This method returns from the interrupt designated by the - * calling interrupt object. If there is a machine-wide end-of-interrupt procedure and the driver was called during the handling of - * the machine interrupt (as opposed to delaying the handling and letting the runtime EOI), the runtime runs it during this method. - * This method has no return value, since once it's called control leaves the calling thread. */ -EDI_DEFVAR void (*interrupt_return)(object_pointer interrupt); -#endif - -#endif diff --git a/Modules/Interfaces/EDI/edi/edi_memory_mapping.h b/Modules/Interfaces/EDI/edi/edi_memory_mapping.h deleted file mode 100644 index ba8dff35..00000000 --- a/Modules/Interfaces/EDI/edi/edi_memory_mapping.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef EDI_MEMORY_MAPPING_H - -/* Copyright (c) 2006 Eli Gottlieb. - * Permission is granted to copy, distribute and/or modify this document - * under the terms of the GNU Free Documentation License, Version 1.2 - * or any later version published by the Free Software Foundation; - * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover - * Texts. A copy of the license is included in the file entitled "COPYING". */ - -#define EDI_MEMORY_MAPPING_H - -/*! \file edi_memory_mapping.h - * \brief Declaration and description of EDI's class for mapping physical pages into the driver's address space. - * - * Data structures and algorithms this header represents: - * ALGORITHM: MEMORY MAPPINGS - Memory mapping objects of the class EDI-MEMORY-MAPPING are used to give virtual (driver-visible) - * addresses to sections of physical memory. These can either be memory mappings belonging to hardware devices or plain RAM which - * the driver wants page-aligned. A memory mapping object is initialized with the physical address for the memory mapping and the - * number of pages the mapping takes up, or simply the desired length of the a physically contiguous buffer in pages. The class's - * two methods map the section of memory into and out of the driver's virtual address space. */ - -#include "edi_objects.h" - -/*! \brief The name of EDI's memory mapping class. - * - * An edi_string_t with the name of the memory mapping class, "EDI-MEMORY-MAPPING". */ -#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI) -const edi_string_t memory_mapping_class = "EDI-MEMORY-MAPPING"; -#else -extern const edi_string_t memory_mapping_class; -#endif - -/*! \brief Flag representing Strong Uncacheable caching method. */ -#define CACHING_STRONG_UNCACHEABLE 0 -/*! \brief Flag representing Uncacheable caching method. */ -#define CACHING_UNCACHEABLE 1 -/*! \brief Flag representing Write combining caching method. */ -#define CACHING_WRITE_COMBINING 2 -/*! \brief Flag representing Write Through caching method. */ -#define CACHING_WRITE_THROUGH 3 -/*! \brief Flag representing Write Back caching method. */ -#define CACHING_WRITE_BACK 3 -/*! \brief Flag representing Write Protected caching method. */ -#define CACHING_WRITE_PROTECTED 3 - -#ifndef IMPLEMENTING_EDI -/*! \brief Initialize an EDI-MEMORY-MAPPING object with a physical address range. - * - * This method takes the start_physical_address of a memory mapping and the number of pages in that mapping and uses these arguments - * to initialize an EDI-MEMORY-MAPPING object. It can only be called once per object. It returns 1 when successful, -1 when an - * invalid physical address is given (one that the runtime knows is neither a physical memory mapping belonging to a device nor - * normal RAM), -2 when the number of pages requested is bad (for the same reasons as the starting address can be bad), and 0 for - * all other errors. - * - * Note that this method can't be invoked on an object which has already initialized via init_memory_mapping_with_pages(). */ -EDI_DEFVAR int32_t (*init_memory_mapping_with_address)(object_pointer mapping, data_pointer start_physical_address, uint32_t pages); -/*! \brief Initialize an EDI-MEMORY-MAPPING object by requesting a number of new physical pages. - * - * This method takes a desired number of physical pages for a memory mapping, and uses that number to initialize an - * EDI-MEMORY-MAPPING object by creating a buffer of contiguous physical pages. It can only be called once per object. It returns - * 1 when successful, -1 when the request for pages cannot be fulfilled, and 0 for all other errors. - * - * Note that this method cannot be called if init_memory_mapping_with_address() has already been used on the given object. */ -EDI_DEFVAR int32_t (*init_memory_mapping_with_pages)(object_pointer mapping, uint32_t pages); -/*! \brief Map the memory-mapping into this driver's visible address space. - * - * This asks the runtime to map a given memory mapping into the driver's virtual address space. Its parameter is the address of a - * data_pointer to place the virtual address of the mapping into. This method returns 1 on success, -1 on an invalid argument, -2 - * for an uninitialized object, and 0 for all other errors. */ -EDI_DEFVAR int32_t (*map_in_mapping)(object_pointer mapping, data_pointer *address_mapped_to); -/*! \brief Unmap the memory mapping from this driver's visible address space. - * - * This method tries to map the given memory mapping out of the driver's virtual address space. It returns 1 for success, -1 - * for an uninitialized memory mapping object, -2 if the mapping isn't mapped into the driver's address space already, and 0 - * for all other errors. */ -EDI_DEFVAR int32_t (*map_out_mapping)(object_pointer mapping); - -/*! \brief Set the caching flags for a memory mapping. */ -EDI_DEFVAR void (*mapping_set_caching_method)(object_pointer mapping, uint32_t caching_method); -/*! \brief Get the current caching method for a memory mapping. */ -EDI_DEFVAR uint32_t (*mapping_get_caching_method)(object_pointer mapping); -/*! \brief Flush write-combining buffers on CPU to make sure changes to memory mapping actually get written. Only applies to a Write Combining caching method (I think.).*/ -EDI_DEFVAR void (*flush_write_combining_mapping)(object_pointer mapping); -#endif - -#endif diff --git a/Modules/Interfaces/EDI/edi/edi_objects.h b/Modules/Interfaces/EDI/edi/edi_objects.h deleted file mode 100644 index 0e479517..00000000 --- a/Modules/Interfaces/EDI/edi/edi_objects.h +++ /dev/null @@ -1,257 +0,0 @@ -#ifndef EDI_OBJECTS_H - -/* Copyright (c) 2006 Eli Gottlieb. - * Permission is granted to copy, distribute and/or modify this document - * under the terms of the GNU Free Documentation License, Version 1.2 - * or any later version published by the Free Software Foundation; - * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover - * Texts. A copy of the license is included in the file entitled "COPYING". */ - -#define EDI_OBJECTS_H - -/*! \file edi_objects.h - * \brief The header file for basic EDI types and the object system. - * - * This file contains declarations of EDI's primitive data types, as well as structures and functions for with the object system. - * It represents these data structures and algorithms: - * - * DATA STRUCTURE: THE CLASS LIST - EDI implementing runtime's must keep an internal list of classes implemented by the runtime - * and separate lists of classes implemented by each driver. Whoever implements a class is said to "own" that class. The - * internal format of this list is up to the runtime coders, but it must be possible to recreate the original list of - * edi_class_declaration structures the driver declared to the runtime from it. This list is declared to the runtime in an - * initialization function in the header edi.h. The object_class member of an edi_object_metadata structure must point to that - * object's class's entry in this list. - * - * ALGORITHM AND DATA STRUCTURE: CLASSES AND INHERITANCE - Classes are described using edi_class_declaration_t structures and - * follow very simple rules. All data is private and EDI provides no way to access instance data, so there are no member - * variable declarations. However, if the data isn't memory-protected (for example, driver data on the driver heap) EDI allows - * the possibility of pointer access to data, since runtime and driver coders could make use of that behavior. Classes may have - * one ancestor by declaring so in their class declaration structure, and if child methods are different then parent methods - * the children always override their parents. An EDI runtime must also be able to check the existence and ownership of a given - * class given its name in an edi_string_t. - * - * ALGORITHM: OBJECT CREATION AND DESTRUCTION - An EDI runtime should be able to call the constructor of a named class, put the - * resulting object_pointer into an edi_object_metadata_t and return that structure. The runtime should also be able to call an - * object's class's destructor when given a pointer to a valid edi_metadata_t for an already-existing object. Data equivalent - * to an edi_object_metadata_t should also be tracked by the runtime for every object in existence in case of sudden EDI shutdown - * (see edi.h). - * - * ALGORITHM: RUNTIME TYPE INFORMATION - When passed the data_pointer member of an edi_object_metadata_t to a valid object, an - * EDI runtime must be able to return an edi_string_t containing the name of that object's class and to return function_pointers - * to methods when the required information to find the correct method is given by calling a class's method getting function.*/ - -/* If the EDI headers are linked with the standard C library, they use its type definitions. Otherwise, equivalent definitions are - * made.*/ -#if __STDC_VERSION__ == 199901L -# include -# include -#else -# ifndef NULL -# define NULL ((void*)0) -# endif -typedef unsigned char bool; -# define true 1 -# define false 0 -typedef char int8_t; -typedef short int16_t; -typedef long int32_t; -typedef long long int64_t; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned long uint32_t; -typedef unsigned long long uint64_t; -#endif - -/*! \brief Define a variable in the header - */ -#ifdef EDI_MAIN_FILE -# define EDI_DEFVAR -#else -# define EDI_DEFVAR extern -#endif - -/*! \brief A pointer to the in-memory instance of an object. - * - * This type is sized just like a general C pointer type (whatever*) for the target architecture. It's passed as a first parameter - * to all methods, thus allowing EDI classes to be implemented as C++ classes and providing some protection from confusing objects - * with normal pointers. Equivalent to a C++ this pointer or an Object Pascal Self argument. */ -typedef void *object_pointer; -/*! \brief A basic pointer type pointing to arbitrary data in an arbitrary location. */ -typedef void *data_pointer; -/*! \brief A basic function pointer type. - * - * A pointer to a piece of code which can be called and return to its caller, used to distinguish between pointers to code and - * pointers to data. Its size is hardware-dependent. */ -typedef void (*function_pointer)(void); -/*! \brief The length of an EDI string without its null character. */ -#define EDI_STRING_LENGTH 31 -/*! \brief A type representing a 31-character long string with a terminating NULL character at the end. All of EDI uses this type - * for strings. - * - * A null-terminated string type which stores characters in int8s. It allows for 31 characters in each string, with the final - * character being the NULL terminator. Functions which use this type must check that its final character is NULL, a string which - * doesn't not have this property is invalid and insecure. I (the author of EDI) know and understand that this form of a string - * suffers from C programmer's disease, but I can't use anything else without either making string use far buggier or dragging - * everyone onto a better language than C. */ -typedef int8_t edi_string_t[0x20]; -/*! \brief A type representing a pointer form of #edi_string_t suitable for function returns - */ -typedef int8_t *edi_string_ptr_t; - -/*! \var EDI_BASE_TYPES - * \brief A constant array of edi_string_t's holding every available EDI primitive type. */ -/*! \var EDI_TYPE_MODIFIERS - * \brief A constant array of edi_string_t's holding available modifiers for EDI primitive types. */ -#ifdef IMPLEMENTING_EDI - const edi_string_t EDI_BASE_TYPES[9] = {"void","bool","int8_t","int16_t","int32_t","int64_t","function_pointer","intreg","edi_string_t"}; - const edi_string_t EDI_TYPE_MODIFIERS[2] = {"pointer","unsigned"}; -#else - //extern const edi_string_t EDI_BASE_TYPES[9] = {"void","bool","int8_t","int16_t","int32_t","int64_t","function_pointer","intreg", "edi_string_t"}; - //extern const edi_string_t EDI_TYPE_MODIFIERS[2] = {"pointer","unsigned"}; - extern const edi_string_t EDI_BASE_TYPES[9]; - extern const edi_string_t EDI_TYPE_MODIFIERS[2]; -#endif - -/*! \struct edi_object_metadata_t - * \brief A packed structure holding all data to identify an object to the EDI object system. */ -typedef struct { - /*! \brief Points to the instance data of the object represented by this structure. - * - * An object_pointer to the object this structure refers to. The this pointer, so to speak. */ - object_pointer object; - /*! \brief Points the internal record kept by the runtime describing the object's class. - * - * Points to wherever the runtime has stored the class data this object was built from. The class data doesn't need to be - * readable to the driver, and so this pointer can point to an arbitrary runtime-reachable location. */ - data_pointer object_class; -} edi_object_metadata_t; - -/*! \struct edi_variable_declaration_t - * \brief The data structure used to describe a variable declaration to the EDI object system. - * - * The data structure used to describe a variable declaration to the EDI object system. The context of the declaration depends on - * where the data structure appears, ie: alone, in a class declaration, in a parameter list, etc. */ -typedef struct { - /*! \brief The type of the declared variable. - * - * The type of the variable, which must be a valid EDI primitive type as specified in the constant EDI_BASE_TYPES and - * possibly modified by a modifier specified in the constant EDI_TYPE_MODIFIERS. */ - edi_string_t type; - /*! \brief The name of the declared variable. */ - edi_string_t name; - /*! \brief Number of array entries if this variable is an array declaration. - * - * An int32_t specifying the number of variables of 'type' in the array 'name'. For a single variable this value should - * simply be set to 1, for values greater than 1 a packed array of contiguous variables is being declared, and a value of 0 - * is invalid. */ - int32_t array_length; -} edi_variable_declaration_t; - -/*! \struct edi_function_declaration_t - * \brief The data structure used to declare a function to the EDI object system. */ -typedef struct { - /*! \brief The return type of the function. The same type rules which govern variable definitions apply here. */ - edi_string_t return_type; - /*! \brief The name of the declared function. */ - edi_string_t name; - /*! \brief The version number of the function, used to tell different implementations of the same function apart. */ - uint32_t version; - /*! \brief The number of arguments passed to the function. - * - * The number of entries in the member arguments that the object system should care about. Caring about less misses - * parameters to functions, caring about more results in buffer overflows. */ - uint32_t num_arguments; - /*! \brief An array of the declared function's arguments. - * - * A pointer to an array num_arguments long containing edi_variable_declaration_t's for each argument to the declared - * function.*/ - edi_variable_declaration_t *arguments; - /*!\brief A pointer to the declared function's code in memory. */ - function_pointer code; -} edi_function_declaration_t; - -/*! \brief A pointer to a function for constructing instances of a class. - * - * A pointer to a function which takes no parameters and returns an object_pointer pointing to the newly made instance of a class. - * It is the constructor's responsibility to allocate memory for the new object. Each EDI class needs one of these. */ -typedef object_pointer (*edi_constructor_t)(void); -/*! \brief A pointer to a function for destroying instances of a class. - * - * A pointer to a function which takes an object_pointer as a parameter and returns void. This is the destructor counterpart to a - * class's edi_constructor_t, it destroys the object pointed to by its parameter and frees the object's memory. Every class must - * have one */ -typedef void (*edi_destructor_t)(object_pointer); - -/*! \brief Information the driver must give the runtime about its classes so EDI can construct them and call their methods. - * - * A structure used to declare a class to an EDI runtime so instances of it can be constructed by the EDI object system. */ -typedef struct { - /*! \brief The name of the class declared by the structure. */ - edi_string_t name; - /*! \brief The version of the class. This number is used to tell identically named but differently - * implemented classes apart.*/ - uint32_t version; - /*! \brief The number of methods in the 'methods' function declaration array. */ - uint32_t num_methods; - /*! \brief An array of edi_function_declaration_t declaring the methods of this class. */ - edi_function_declaration_t *methods; - /*! \brief Allocates the memory for a new object of the declared class and constructs the object. Absolutely required.*/ - edi_constructor_t constructor; - /*! \brief Destroys the given object of the declared class and frees its memory. Absolutely required. */ - edi_destructor_t destructor; - /*! \brief A pointer to another EDI class declaration structure specifying the declared class's parent class. - * - * Points to a parent class declared in another class declaration. It can be NULL to mean this class has no parent. */ - struct edi_class_declaration_t *parent; -} edi_class_declaration_t; - -/*! \brief Checks the existence of the named class. - * - * This checks for the existence on THE CLASS LIST of the class named by its edi_string_t parameter and returns a signed int32_t. If - * the class isn't found (ie: it doesn't exist as far as EDI is concerned) -1 is returned, if the class is owned by the driver - * (implemented by the driver and declared to the runtime by the driver) 0, and if the class is owned by the runtime (implemented by - * the runtime) 1. */ -int32_t check_class_existence(edi_string_t class_name); -/*! \brief Constructs an object of the named class and returns its object_pointer and a data_pointer to its class data. - * - * Given a valid class name in an edi_string_t this function constructs the specified class and returns an edi_metadata_t describing - * the new object as detailed in OBJECT CREATION AND DESTRUCTION. If the construction fails it returns a structure full of NULL - * pointers. */ -edi_object_metadata_t construct_object(edi_string_t class_name); -/*! \brief Destroys the given object using its class data. - * - * As specified in OBJECT CREATION AND DESTRUCTION this function should destroy an object when given its valid edi_metadata_t. The - * destruction is accomplished by calling the class's destructor. */ -void destroy_object(edi_object_metadata_t object); -/*! \brief Obtains a function pointer to a named method of a given class. - * - * When given a valid data_pointer object_class from an edi_object_metadata_t and an edi_string_t representing the name of the - * desired method retrieves a function_pointer to the method's machine code in memory. If the desired method isn't found, NULL is - * returned. */ -function_pointer get_method_by_name(data_pointer object_class,edi_string_t method_name); -/*! \brief Obtains a function pointer to a method given by a declaration of the given class if the class's method matches the - * declaration. - * - * Works just like get_method_by_name(), but by giving an edi_function_declaration_t for the desired method instead of just its name. - * Performs detailed checking against THE CLASS LIST to make sure that the method returned exactly matches the declaration passed - * in. */ -function_pointer get_method_by_declaration(data_pointer object_class,edi_function_declaration_t declaration); - -/* Runtime typing information. */ -/*! \brief Returns the name of the class specified by a pointer to class data. - * - * Given the data_pointer to an object's class data as stored in an edi_object_metadata_t retrieves the name of the object's class - * and returns it in an edi_string_t. */ -edi_string_ptr_t get_object_class(data_pointer object_class); -/*! \brief Returns the name of a class's parent class. - * - * When given an edi_string_t with a class name in it, returns another edi_string_t containing the name of the class's parent, or an - * empty string. */ -edi_string_ptr_t get_class_parent(edi_string_t some_class); -/*! \brief Returns the internal class data of a named class (if it exists) or NULL. - * - * When given an edi_string_t with a valid class name in it, returns a pointer to the runtime's internal class data for that class. - * Otherwise, it returns NULL. */ -data_pointer get_internal_class(edi_string_t some_class); - -#endif diff --git a/Modules/Interfaces/EDI/edi/edi_port_io.h b/Modules/Interfaces/EDI/edi/edi_port_io.h deleted file mode 100644 index a2a1773d..00000000 --- a/Modules/Interfaces/EDI/edi/edi_port_io.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef EDI_PORT_IO_H - -/* Copyright (c) 2006 Eli Gottlieb. - * Permission is granted to copy, distribute and/or modify this document - * under the terms of the GNU Free Documentation License, Version 1.2 - * or any later version published by the Free Software Foundation; - * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover - * Texts. A copy of the license is included in the file entitled "COPYING". */ - -/* Modified by thePowersGang (John Hodge) - * - Surround variable definitions with an #ifdef IMPLEMENTING_EDI - */ - -#define EDI_PORT_IO_H - -/*! \file edi_port_io.h - * \brief Declaration and description of EDI's port I/O class. - * - * Data structures and algorithms this header represents: - * - * DATA STRUCTURE AND ALGORITHM: PORT I/O OBJECTS - A class named "EDI-IO-PORT" is defined as an encapsulation of the port I/O - * used on some machine architectures. Each object of this class represents a single I/O port which can be read from and written to - * in various sizes. Each port can be held by one object only at a time. */ - -#include "edi_objects.h" - -/*! \brief Macro to create methods for reading from ports. - * - * This macro creates four similar methods, differing in the size of the type they read from the I/O port held by the object. Their - * parameter is a pointer to the output type, which is filled with the value read from the I/O port. They return 1 for success, -1 - * for an uninitialized I/O port object, and 0 for other errors. */ -#define port_read_method(type,name) int32_t (*name)(object_pointer port_object, type *out) -/*! \brief Macro to create methods for writing to ports. - * - * This macro creates four more similar methods, differing in the size of the type they write to the I/O port held by the object. - * Their parameter is the value to write to the port. They return 1 for success, -1 for an uninitialized I/O port object and 0 for - * other errors. */ -#define port_write_method(type,name) int32_t (*name)(object_pointer port_object, type in) - -/*! \brief Name of EDI I/O port class. (Constant) - * - * A CPP constant with the value of #io_port_class */ -#define IO_PORT_CLASS "EDI-IO-PORT" -/*! \brief Name of EDI I/O port class. - * - * An edi_string_t containing the class name "EDI-IO-PORT". */ -#if defined(EDI_MAIN_FILE) || defined(IMPLEMENTING_EDI) -const edi_string_t io_port_class = IO_PORT_CLASS; -#else -extern const edi_string_t io_port_class; -#endif - -#ifndef IMPLEMENTING_EDI -/*! \brief int32_t EDI-IO-PORT.init_io_port(unsigned int16_t port); - * - * This method takes an unsigned int16_t representing a particular I/O port and initializes the invoked EDI-IO-PORT object with it. - * The method returns 1 if successful, -1 if the I/O port could not be obtained for the object, and 0 for all other errors. */ -EDI_DEFVAR int32_t (*init_io_port)(object_pointer port_object, uint16_t port); -/*! \brief Get the port number from a port object. */ -EDI_DEFVAR uint16_t (*get_port_number)(object_pointer port); -/*! \brief Method created by port_read_method() in order to read bytes (int8s) from I/O ports. */ -EDI_DEFVAR int32_t (*read_byte_io_port)(object_pointer port_object, int8_t *out); -/*! \brief Method created by port_read_method() in order to read words (int16s) from I/O ports. */ -EDI_DEFVAR int32_t (*read_word_io_port)(object_pointer port_object, int16_t *out); -/*! \brief Method created by port_read_method() in order to read longwords (int32s) from I/O ports. */ -EDI_DEFVAR int32_t (*read_long_io_port)(object_pointer port_object, int32_t *out); -/*! \brief Method created by port_read_method() in order to read long longwords (int64s) from I/O ports. */ -EDI_DEFVAR int32_t (*read_longlong_io_port)(object_pointer port_object,int64_t *out); -/*! \brief Method of EDI-IO-PORT to read long strings of data from I/O ports. - * - * Reads arbitrarily long strings of data from the given I/O port. Returns 1 for success, -1 for an uninitialized port object, -2 - * for a bad pointer to the destination buffer, and 0 for all other errors. */ -EDI_DEFVAR int32_t (*read_string_io_port)(object_pointer port_object, uint32_t data_length, uint8_t *out); -/*! \brief Method created by port_write_method() in order to write bytes (int8s) to I/O ports. */ -EDI_DEFVAR int32_t (*write_byte_io_port)(object_pointer port_object, int8_t in); -/*! \brief Method created by port_write_method() in order to write words (int16s) to I/O ports. */ -EDI_DEFVAR int32_t (*write_word_io_port)(object_pointer port_object, int16_t in); -/*! \brief Method created by port_write_method() in order to write longwords (int32s) to I/O ports. */ -EDI_DEFVAR int32_t (*write_long_io_port)(object_pointer port_object, int32_t in); -/*! \brief Method created by port_write_method() in order to write long longwords (int64s) to I/O ports. */ -EDI_DEFVAR int32_t (*write_longlong_io_port)(object_pointer port_object, int64_t in); -/*! \brief Method of EDI-IO-PORT to write long strings of data to I/O ports. - * - * Writes arbitrarily long strings of data to the given I/O port. Returns 1 for success, -1 for an uninitialized port object, -2 - * for a bad pointer to the source buffer, and 0 for all other errors. */ -EDI_DEFVAR int32_t (*write_string_io_port)(object_pointer port_object, uint32_t data_length, uint8_t *in); - -#endif // defined(IMPLEMENTING_EDI) - -#endif diff --git a/Modules/Interfaces/EDI/edi/edi_pthreads.h b/Modules/Interfaces/EDI/edi/edi_pthreads.h deleted file mode 100644 index c21fa751..00000000 --- a/Modules/Interfaces/EDI/edi/edi_pthreads.h +++ /dev/null @@ -1,175 +0,0 @@ -#ifndef EDI_PTHREADS - -/* Copyright (c) 2006 Eli Gottlieb. - * Permission is granted to copy, distribute and/or modify this document - * under the terms of the GNU Free Documentation License, Version 1.2 - * or any later version published by the Free Software Foundation; - * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover - * Texts. A copy of the license is included in the file entitled "COPYING". */ - -#define EDI_PTHREADS -/*!\file edi_pthreads.h - * \brief A basic subset of POSIX Threads functionality, providing threading and thread synchronization. - * - * A very basic POSIX Threads interface. Note that pthreads are not a class, because none of these calls really gels with - * object-oriented programming. Also, if drivers aren't processes or threads under the implementing operating system a small - * threading system must be implemented in-runtime just to multiplex the pthreads of EDI drivers. Sorry about that. - * - * Data structures and algorithms this header represents: - * - * ALGORITHM AND DATA STRUCTURE: POSIX Threading - The runtime must provide enough of a POSIX threading interface to implement - * the calls described here. The actual multithreading must be performed by the runtime, and the runtime can implement that - * multithreading however it likes as long as the given POSIX Threads subset works. There is, however, a caveat: since the runtime - * calls the driver like it would a library, the driver must perceive all calls made to it by the runtime as running under one thread. - * From this thread the driver can create others. Such behavior is a quirk of EDI, and does not come from the POSIX standard. - * However, it is necessary to provide the driver with a thread for its own main codepaths. For further details on a given POSIX - * Threading routine, consult its Unix manual page. */ - -#include "edi_objects.h" - -/* Placeholder type definitions. Users of the PThreads interface only ever need to define pointers to these types. */ -/*!\brief Opaque POSIX Threading thread attribute type. */ -typedef void pthread_attr_t; -/*!\brief Opaque POSIX Threading mutex (mutual exclusion semaphore) type. */ -typedef void pthread_mutex_t; -/*!\brief Opaque POSIX Threading mutex attribute type. */ -typedef void pthread_mutex_attr_t; - -/*!\struct sched_param - * \brief POSIX Threading scheduler parameters for a thread. */ -typedef struct { - /*!\brief The priority of the thread. */ - int32_t sched_priority; -} sched_param; - -/*!\brief POSIX Threading thread identifier. */ -typedef uint32_t pthread_t; -/*!\brief POSIX Threading thread function type. - * - * A function pointer to a thread function, with the required signature of a thread function. A thread function takes one untyped - * pointer as an argument and returns an untyped pointer. Such a function is a thread's main routine: it's started with the thread, - * and the thread exits if it returns. */ -typedef void *(*pthread_function_t)(void*); - -/*!\brief Insufficient resources. */ -#define EAGAIN -1 -/*!\brief Invalid parameter. */ -#define EINVAL -2 -/*!\brief Permission denied. */ -#define EPERM -3 -/*!\brief Operation not supported. */ -#define ENOTSUP -4 -/*!\brief Priority scheduling for POSIX/multiple schedulers is not implemented. */ -#define ENOSYS -5 -/*!\brief Out of memory. */ -#define ENOMEM -6 -/*!\brief Deadlock. Crap. */ -#define EDEADLK -7 -/*!\brief Busy. Mutex already locked. */ -#define EBUSY -8 - -/*!\brief Scheduling policy for regular, non-realtime scheduling. The default. */ -#define SCHED_OTHER 0 -/*!\brief Real-time, first-in first-out scheduling policy. Requires special (superuser, where such a thing exists) permissions. */ -#define SCHED_FIFO 1 -/*!\brief Real-time, round-robin scheduling policy. Requires special (superuser, where such a thing exists) permissions. */ -#define SCHED_RR 0 - -/*!\brief Creates a new thread with the given attributes, thread function and arguments, giving back the thread ID of the new - * thread. - * - * pthread_create() creates a new thread of control that executes concurrently with the calling thread. The new thread applies the - * function start_routine, passing it arg as its argument. The attr argument specifies thread attributes to apply to the new thread; - * it can also be NULL for the default thread attributes (joinable with default scheduling policy). On success this function returns - * 0 and places the identifier of the new thread into thread_id. On an error, pthread_create() can return EAGAIN if insufficient - * runtime resources are available to create the requested thread, EINVAL a value specified by attributes is invalid, or EPERM if the - * caller doesn't have permissions to set the given attributes. - * - * For further information: man 3 pthread_create */ -int32_t pthread_create(pthread_t *thread_id, const pthread_attr_t *attributes, pthread_function_t thread_function, void *arguments); -/*!\brief Terminates the execution of the calling thread. The thread's exit code with by status, and this routine never returns. */ -void pthread_exit(void *status); -/*!\brief Returns the thread identifier of the calling thread. */ -pthread_t pthread_self(); -/*!\brief Compares two thread identifiers. - * - * Determines of the given two thread identifiers refer to the same thread. If so, returns non-zero. Otherwise, 0 is returned. */ -int32_t pthread_equal(pthread_t thread1, pthread_t thread2); -/*!\brief Used by the calling thread to relinquish use of the processor. The thread then waits in the run queue to be scheduled - * again. */ -void pthread_yield(); - -/*!\brief Gets the scheduling policy of the given attributes. - * - * Places the scheduling policy for attributes into policy. Returns 0 on success, EINVAL if attributes was invalid, and ENOSYS if - * priority scheduling/multiple scheduler support is not implemented. */ -int32_t pthread_attr_getschedpolicy(const pthread_attr_t *attributes, int32_t *policy); -/*!\brief Sets the scheduling policy of the given attributes. - * - * Requests a switch of scheduling policy to policy for the given attributes. Can return 0 for success, EINVAL if the given policy - * is not one of SCHED_OTHER, SCHED_FIFO or SCHED_RR or ENOTSUP if policy is either SCHED_FIFO or SCHED_RR and the driver is not - * running with correct privileges. */ -int32_t pthread_attr_setschedpolicy(pthread_attr_t *attributes, int32_t policy); - -/*!\brief Gets the scheduling paramaters (priority) from the given attributes. - * - * On success, stores scheduling parameters in param from attributes, and returns 0. Otherwise, returns non-zero error code, such - * as EINVAL if the attributes object is invalid. */ -int32_t pthread_attr_getschedparam(const pthread_attr_t *attributes, sched_param *param); -/*!\brief Sets the scheduling parameters (priority) of the given attributes. - * - * Requests that the runtime set the scheduling parameters (priority) of attributes from param. Returns 0 for success, EINVAL for an - * invalid attributes object, ENOSYS when multiple schedulers/priority scheduling is not implemented, and ENOTSUP when the value of - * param isn't supported/allowed. */ -int32_t pthread_attr_setschedparam(pthread_attr_t *attributes, const sched_param *param); - -/*!\brief The thread obtains its scheduling properties explicitly from its attributes structure. */ -#define PTHREAD_EXPLICIT_SCHED 1 -/*!\brief The thread inherits its scheduling properties from its parent thread. */ -#define PTHREAD_INHERIT_SCHED 0 - -/*!\brief Returns the inheritsched attribute of the given attributes. - * - * On success, returns 0 and places the inheritsched attribute from attributes into inherit. This attribute specifies where the - * thread's scheduling properites shall come from, and can be set to PTHREAD_EXPLICIT_SCHED or PTHREAD_INHERIT_SCHED. On failure it - * returns EINVAL if attributes was invalid or ENOSYS if multiple schedulers/priority scheduling isn't implemented. */ -int32_t pthread_attr_getinheritsched(const pthread_attr_t *attributes, int32_t *inherit); -/*!\brief Sets the inheritsched attribute of the given attributes. - * - * On success, places inherit into the inheritsched attribute of attributes and returns 0. inherit must either contain - * PTHREAD_EXPLICIT_SCHED or PTHREAD_INHERIT_SCHED. On failure, this routine returns EINVAL if attributes is invalid, ENOSYS when - * multiple schedulers/priority scheduling isn't implemented, and ENOSUP if the inheritsched attribute isn't supported. */ -int32_t pthread_attr_setinheritsched(pthread_attr_t *attributes, int32_t inherit); - -/*!\brief Creates a new POSIX Threads mutex, which will initially be unlocked. - * - * Creates a new mutex with the given attributes. If attributes is NULL, the default attributes will be used. The mutex starts out - * unlocked. On success, the new mutex resides in the mutex structure pointed to by mutex, and this routine routines 0. On failure, - * it returns EAGAIN if the system lacked sufficient non-memory resources to initialize the mutex, EBUSY if the given mutex is - * already initialized and in use, EINVAL if either parameter is invalid, and ENOMEM if the system lacks the memory for a new - * mutex. Note: All EDI mutexes are created with the default attributes, and are of type PTHREAD_MUTEX_ERRORCHECK. This means - * undefined behavior can never result from an badly placed function call. */ -int32_t pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutex_attr_t *attributes); -/*!\brief Locks the given mutex. - * - * Locks the given mutex. If the mutex is already locked, blocks the calling thread until it can acquire the lock. When this - * routine returns successfully, it will return 0 and the calling thread will own the lock of the mutex. If the call fails, it can - * return EINVAL when mutex is invalid or EDEADLK if the calling thread already owns the mutex. */ -int32_t pthread_mutex_lock(pthread_mutex_t *mutex); -/*!\brief Unlocks the given mutex. - * - * Unlocks the given mutex, returning 0 on success. On failure, it can return EINVAL when mutex is invalid or EPERM when the - * calling thread doesn't own the mutex. */ -int32_t pthread_mutex_unlock(pthread_mutex_t *mutex); -/*!\brief Tries to lock the given mutex, returning immediately even if the mutex is already locked. - * - * Attempts to lock the given mutex, but returns immediately if it can't acquire a lock. Returns 0 when it has acquired a lock, - * EBUSY if the mutex is already locked, or EINVAL if mutex is invalid. */ -int32_t pthread_mutex_trylock(pthread_mutex_t *mutex); -/*!\brief Destroys the given mutex, or at least the internal structure of it. - * - * Deletes the given mutex, making mutex invalid until it should be initialized by pthread_mutex_init(). Returns 0 on success, - * EINVAL when mutex is invalid, or EBUSY when mutex is locked or referenced by another thread. */ -int32_t pthread_mutex_destroy (pthread_mutex_t *mutex); - -#endif diff --git a/Modules/Interfaces/EDI/edi/helpers.h b/Modules/Interfaces/EDI/edi/helpers.h deleted file mode 100644 index d7bc1388..00000000 --- a/Modules/Interfaces/EDI/edi/helpers.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef HELPERS_H - -#define HELPERS_H - -#include - -// Locally Defined -bool edi_string_equal(edi_string_t x,edi_string_t y); -bool descends_from(data_pointer object_class,edi_string_t desired_class); -data_pointer get_actual_class(edi_string_t ancestor,int32_t num_objects,edi_object_metadata_t *objects); - -// Local Copy/set -void *memcpyd(void *dest, void *src, unsigned int count); - -// Implementation Defined Common functions -void *memcpy(void *dest, void *src, unsigned int count); -void *memmove(void *dest, void *src, unsigned int count); -void *realloc(void *ptr, unsigned int size); - -#endif diff --git a/Modules/Interfaces/EDI/edi_int.inc.c b/Modules/Interfaces/EDI/edi_int.inc.c deleted file mode 100644 index 0ab3359e..00000000 --- a/Modules/Interfaces/EDI/edi_int.inc.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * AcessOS EDI Interface - * - IRQ Class - * - * By John Hodge (thePowersGang) - * - * This file has been released into the public domain. - * You are free to use it as you wish. - */ -#include "edi/edi.h" - -// === TYPES === -typedef struct { - uint16_t State; // 0: Unallocated, 1: Allocated, 2: Initialised, (Bit 0x8000 set if in heap) - uint16_t Num; - interrupt_handler_t Handler; -} tEdiIRQ; - -// === PROTOTYPES === -void EDI_Int_IRQ_Handler(tRegs *Regs); - -// === GLOBALS === -tEdiIRQ gEdi_IRQObjects[16]; - -// === FUNCTIONS === -/** - * \fn object_pointer Edi_Int_IRQ_Construct(void) - * \brief Creates a new IRQ Object - * \return Pointer to object - */ -object_pointer Edi_Int_IRQ_Construct(void) -{ - int i; - // Search for a free irq - for( i = 0; i < 16; i ++ ) - { - if(gEdi_IRQObjects[i].State) continue; - gEdi_IRQObjects[i].State = 1; - gEdi_IRQObjects[i].Num = 0; - gEdi_IRQObjects[i].Handler = NULL; - return &gEdi_IRQObjects[i]; - } - return NULL; -} - -/** - * \fn void Edi_Int_IRQ_Destruct(object_pointer Object) - * \brief Destruct an IRQ Object - * \param Object Object to destroy - */ -void Edi_Int_IRQ_Destruct(object_pointer Object) -{ - tEdiIRQ *obj; - - VALIDATE_PTR(Object,); - obj = GET_DATA(Object); - - if( !obj->State ) return; - - if( obj->Handler ) - irq_uninstall_handler( obj->Num ); - - if( obj->State & 0x8000 ) { // If in heap, free - free(Object); - } else { // Otherwise, mark as unallocated - obj->State = 0; - } -} - -/** - * \fn int32_t Edi_Int_IRQ_InitInt(object_pointer Object, uint16_t Num, interrupt_handler_t Handler) - * \brief Initialises an IRQ - * \param Object Object Pointer (this) - * \param Num IRQ Number to use - * \param Handler Callback for IRQ - */ -int32_t Edi_Int_IRQ_InitInt(object_pointer Object, uint16_t Num, interrupt_handler_t Handler) -{ - tEdiIRQ *obj; - - //LogF("Edi_Int_IRQ_InitInt: (Object=0x%x, Num=%i, Handler=0x%x)\n", Object, Num, Handler); - - VALIDATE_PTR(Object,0); - obj = GET_DATA(Object); - - if( !obj->State ) return 0; - - if(Num > 15) return 0; - - // Install the IRQ if a handler is passed - if(Handler) { - if( !irq_install_handler(Num, Edi_Int_IRQ_Handler) ) - return 0; - obj->Handler = Handler; - } - - obj->Num = Num; - obj->State &= ~0x3FFF; - obj->State |= 2; // Set initialised flag - return 1; -} - -/** - * \fn uint16_t Edi_Int_IRQ_GetInt(object_pointer Object) - * \brief Returns the irq number associated with the object - * \param Object IRQ Object to get number from - * \return IRQ Number - */ -uint16_t Edi_Int_IRQ_GetInt(object_pointer Object) -{ - tEdiIRQ *obj; - - VALIDATE_PTR(Object,0); - obj = GET_DATA(Object); - - if( !obj->State ) return 0; - return obj->Num; -} - -/** - * \fn void EDI_Int_IRQ_SetHandler(object_pointer Object, interrupt_handler_t Handler) - * \brief Set the IRQ handler for an IRQ object - * \param Object IRQ Object to alter - * \param Handler Function to use as handler - */ -void EDI_Int_IRQ_SetHandler(object_pointer Object, interrupt_handler_t Handler) -{ - tEdiIRQ *obj; - - // Get Data Pointer - VALIDATE_PTR(Object,); - obj = GET_DATA(Object); - - // Sanity Check arguments - if( !obj->State ) return ; - - // Only register the mediator if it is not already - if( Handler && !obj->Handler ) - if( !irq_install_handler(obj->Num, Edi_Int_IRQ_Handler) ) - return ; - obj->Handler = Handler; -} - -/** - * \fn void EDI_Int_IRQ_Return(object_pointer Object) - * \brief Return from interrupt - * \param Object IRQ Object - * \note Due to the structure of acess interrupts, this is a dummy - */ -void EDI_Int_IRQ_Return(object_pointer Object) -{ -} - -/** - * \fn void Edi_Int_IRQ_Handler(struct regs *Regs) - * \brief EDI IRQ Handler - Calls the handler - * \param Regs Register state at IRQ call - */ -void Edi_Int_IRQ_Handler(struct regs *Regs) -{ - int i; - for( i = 0; i < 16; i ++ ) - { - if(!gEdi_IRQObjects[i].State) continue; // Unused, Skip - if(gEdi_IRQObjects[i].Num != Regs->int_no) continue; // Another IRQ, Skip - if(!gEdi_IRQObjects[i].Handler) continue; // No Handler, Skip - gEdi_IRQObjects[i].Handler( Regs->int_no ); // Call Handler - return; - } -} - - -// === CLASS DECLARATION === -static edi_function_declaration_t scEdi_Int_Functions_IRQ[] = { - {"int32_t", "init_interrupt", 1, 3, NULL, //scEdi_Int_Variables_IO[0], - (function_pointer)Edi_Int_IRQ_InitInt - }, - {"uint32_t", "interrupt_get_irq", 1, 1, NULL, //scEdi_Int_Variables_IO[1], - (function_pointer)Edi_Int_IRQ_GetInt - }, - {"void", "interrupt_set_handler", 1, 2, NULL, //scEdi_Int_Variables_IO[2], - (function_pointer)Edi_Int_IRQ_GetInt - }, - {"void", "interrupt_return", 1, 1, NULL, //scEdi_Int_Variables_IO[3], - (function_pointer)Edi_Int_IRQ_GetInt - } - }; -static edi_class_declaration_t scEdi_Int_Class_IRQ = - { - INTERRUPTS_CLASS, 1, 12, - scEdi_Int_Functions_IRQ, - Edi_Int_IRQ_Construct, - Edi_Int_IRQ_Destruct, - NULL - }; diff --git a/Modules/Interfaces/EDI/edi_io.inc.c b/Modules/Interfaces/EDI/edi_io.inc.c deleted file mode 100644 index 8a6ef22e..00000000 --- a/Modules/Interfaces/EDI/edi_io.inc.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * AcessOS EDI Interface - * - IO Port Class - * - * By John Hodge (thePowersGang) - * - * This file has been released into the public domain. - * You are free to use it as you wish. - */ -#include "edi/edi.h" - -// === TYPES === -typedef struct { - uint16_t State; // 0: Unallocated, 1: Allocated, 2: Initialised, (Bit 0x8000 set if in heap) - uint16_t Num; -} tEdiPort; - -// === GLOBALS === -#define NUM_PREALLOC_PORTS 128 -tEdiPort gEdi_PortObjects[NUM_PREALLOC_PORTS]; - -// === FUNCTIONS === -/** - * \fn object_pointer Edi_Int_IO_Construct(void) - * \brief Creates a new IO Port Object - * \return Pointer to object - */ -object_pointer Edi_Int_IO_Construct(void) -{ - tEdiPort *ret; - int i; - // Search for a free preallocated port - for( i = 0; i < NUM_PREALLOC_PORTS; i ++ ) - { - if(gEdi_PortObjects[i].State) continue; - gEdi_PortObjects[i].State = 1; - gEdi_PortObjects[i].Num = 0; - return &gEdi_PortObjects[i]; - } - // Else, use heap space - ret = malloc( sizeof(tEdiPort) ); - ret->State = 0x8001; - ret->Num = 0; - return ret; -} - -/** - * \fn void Edi_Int_IO_Destruct(object_pointer Object) - * \brief Destruct an IO Port Object - * \param Object Object to destroy - */ -void Edi_Int_IO_Destruct(object_pointer Object) -{ - tEdiPort *obj; - // Get Data Pointer - VALIDATE_PTR(Object,); - obj = GET_DATA(Object); - - if(obj->State & 0x8000) { // If in heap, free - free(Object); - } else { // Otherwise, mark as unallocated - obj->State = 0; - } -} - -/** - * \fn int32_t Edi_Int_IO_InitPort(object_pointer Object, uint16_t Port) - * \brief Initialises an IO Port - * \param Object Object Pointer (this) - * \param Port Port Number to use - */ -int32_t Edi_Int_IO_InitPort(object_pointer Object, uint16_t Port) -{ - tEdiPort *obj; - // Get Data Pointer - VALIDATE_PTR(Object, 0); - obj = GET_DATA(Object); - - if( !obj->State ) return 0; - obj->Num = Port; - obj->State &= ~0x3FFF; - obj->State |= 2; // Set initialised flag - return 1; -} - -/** - * \fn uint16_t Edi_Int_IO_GetPortNum(object_pointer Object) - * \brief Returns the port number associated with the object - * \param Object Port Object to get number from - * \return Port Number - */ -uint16_t Edi_Int_IO_GetPortNum(object_pointer Object) -{ - tEdiPort *obj; - // Get Data Pointer - VALIDATE_PTR(Object, 0); - obj = GET_DATA(Object); - // Check if valid - if( !obj->State ) return 0; - // Return Port No - return obj->Num; -} - -/** - * \fn int32_t Edi_Int_IO_ReadByte(object_pointer Object, uint8_t *out) - * \brief Read a byte from an IO port - * \param Object Port Object - * \param out Pointer to put read data - */ -int32_t Edi_Int_IO_ReadByte(object_pointer Object, uint8_t *out) -{ - tEdiPort *obj; - // Get Data Pointer - VALIDATE_PTR(Object, 0); - obj = GET_DATA(Object); - - if( !obj->State ) return 0; - if( obj->State & 1 ) return -1; // Unintialised - - __asm__ __volatile__ ( "inb %%dx, %%al" : "=a" (*out) : "d" ( obj->Num ) ); - - return 1; -} - -/** - * \fn int32_t Edi_Int_IO_ReadWord(object_pointer Object, uint16_t *out) - * \brief Read a word from an IO port - * \param Object Port Object - * \param out Pointer to put read data - */ -int32_t Edi_Int_IO_ReadWord(object_pointer Object, uint16_t *out) -{ - - tEdiPort *obj; - // Get Data Pointer - VALIDATE_PTR(Object, 0); - obj = GET_DATA(Object); - if( !obj->State ) return 0; - if( obj->State & 1 ) return -1; // Unintialised - - __asm__ __volatile__ ( "inw %%dx, %%ax" : "=a" (*out) : "d" ( obj->Num ) ); - - return 1; -} - -/** - * \fn int32_t Edi_Int_IO_ReadDWord(object_pointer Object, uint32_t *out) - * \brief Read a double word from an IO port - * \param Object Port Object - * \param out Pointer to put read data - */ -int32_t Edi_Int_IO_ReadDWord(object_pointer Object, uint32_t *out) -{ - - tEdiPort *obj; - // Get Data Pointer - VALIDATE_PTR(Object, 0); - obj = GET_DATA(Object); - if( !obj->State ) return 0; - if( obj->State & 1 ) return -1; // Unintialised - - __asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*out) : "d" ( obj->Num ) ); - - return 1; -} - -/** - * \fn int32_t Edi_Int_IO_ReadQWord(object_pointer Object, uint64_t *out) - * \brief Read a quad word from an IO port - * \param Object Port Object - * \param out Pointer to put read data - */ -int32_t Edi_Int_IO_ReadQWord(object_pointer Object, uint64_t *out) -{ - uint32_t *out32 = (uint32_t*)out; - tEdiPort *obj; - // Get Data Pointer - VALIDATE_PTR(Object, 0); - obj = GET_DATA(Object); - if( !obj->State ) return 0; - if( obj->State & 1 ) return -1; // Unintialised - - __asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*out32) : "d" ( obj->Num ) ); - __asm__ __volatile__ ( "inl %%dx, %%eax" : "=a" (*(out32+1)) : "d" ( obj->Num+4 ) ); - - return 1; -} - -/** - * \fn int32_t Edi_Int_IO_ReadString(object_pointer Object, uint32_t Length, uint8_t *out) - * \brief Read a byte from an IO port - * \param Object Port Object - * \param Length Number of bytes to read - * \param out Pointer to put read data - */ -int32_t Edi_Int_IO_ReadString(object_pointer Object, uint32_t Length, uint8_t *out) -{ - tEdiPort *obj; - // Get Data Pointer - VALIDATE_PTR(Object, 0); - obj = GET_DATA(Object); - if( !obj->State ) return 0; - if( obj->State & 1 ) return -1; // Unintialised - - __asm__ __volatile__ ( "rep insb" : : "c" (Length), "D" (out), "d" ( obj->Num ) ); - - return 1; -} - -/** - * \fn int32_t Edi_Int_IO_WriteByte(object_pointer Object, uint8_t in) - * \brief Write a byte from an IO port - * \param Object Port Object - * \param in Data to write - */ -int32_t Edi_Int_IO_WriteByte(object_pointer Object, uint8_t in) -{ - tEdiPort *obj; - // Get Data Pointer - VALIDATE_PTR(Object, 0); - obj = GET_DATA(Object); - if( !obj->State ) return 0; - if( obj->State & 1 ) return -1; // Unintialised - - __asm__ __volatile__ ( "outb %%al, %%dx" : : "a" (in), "d" ( obj->Num ) ); - - return 1; -} - -/** - * \fn int32_t Edi_Int_IO_WriteWord(object_pointer Object, uint16_t in) - * \brief Write a word from an IO port - * \param Object Port Object - * \param in Data to write - */ -int32_t Edi_Int_IO_WriteWord(object_pointer Object, uint16_t in) -{ - tEdiPort *obj; - // Get Data Pointer - VALIDATE_PTR(Object, 0); - obj = GET_DATA(Object); - if( !obj->State ) return 0; - if( obj->State & 1 ) return -1; // Unintialised - - __asm__ __volatile__ ( "outw %%ax, %%dx" : : "a" (in), "d" ( obj->Num ) ); - - return 1; -} - -/** - * \fn int32_t Edi_Int_IO_WriteDWord(object_pointer Object, uint32_t in) - * \brief Write a double word from an IO port - * \param Object Port Object - * \param in Data to write - */ -int32_t Edi_Int_IO_WriteDWord(object_pointer Object, uint32_t in) -{ - tEdiPort *obj; - // Get Data Pointer - VALIDATE_PTR(Object, 0); - obj = GET_DATA(Object); - if( !obj->State ) return 0; - if( obj->State & 1 ) return -1; // Unintialised - - __asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (in), "d" ( obj->Num ) ); - - return 1; -} - -/** - * \fn int32_t Edi_Int_IO_WriteQWord(object_pointer Object, uint64_t in) - * \brief Write a quad word from an IO port - * \param Object Port Object - * \param in Data to write - */ -int32_t Edi_Int_IO_WriteQWord(object_pointer Object, uint64_t in) -{ - uint32_t *in32 = (uint32_t*)∈ - tEdiPort *obj; - // Get Data Pointer - VALIDATE_PTR(Object, 0); - obj = GET_DATA(Object); - if( !obj->State ) return 0; - if( obj->State & 1 ) return -1; // Unintialised - - __asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (*in32), "d" ( obj->Num ) ); - __asm__ __volatile__ ( "outl %%eax, %%dx" : : "a" (*(in32+1)), "d" ( obj->Num+4 ) ); - - return 1; -} - -/** - * \fn int32_t Edi_Int_IO_WriteString(object_pointer Object, uint32_t Length, uint8_t *in) - * \brief Read a byte from an IO port - * \param Object Port Object - * \param Length Number of bytes to write - * \param in Pointer to of data to write - */ -int32_t Edi_Int_IO_WriteString(object_pointer Object, uint32_t Length, uint8_t *in) -{ - tEdiPort *obj; - // Get Data Pointer - VALIDATE_PTR(Object, 0); - obj = GET_DATA(Object); - if( !obj->State ) return 0; - if( obj->State & 1 ) return -1; // Unintialised - - __asm__ __volatile__ ( "rep outsb" : : "c" (Length), "D" (in), "d" ( obj->Num ) ); - - return 1; -} - -// === CLASS DECLARATION === -/*static edi_variable_declaration_t *scEdi_Int_Variables_IO[] = { - { - {"pointer", "port_object", 0}, - {"uint16_t", "port", 0} - }, - { - {"pointer", "port_object", 0} - }, - { - {"pointer", "port_object", 0}, - {"pointer int8_t", "out", 0} - } -};*/ -static edi_function_declaration_t scEdi_Int_Functions_IO[] = { - {"int32_t", "init_io_port", 1, 2, NULL, //scEdi_Int_Variables_IO[0], - (function_pointer)Edi_Int_IO_InitPort - }, - {"uint16_t", "get_port_number", 1, 1, NULL, //scEdi_Int_Variables_IO[1], - (function_pointer)Edi_Int_IO_GetPortNum - }, - {"int32_t", "read_byte_io_port", 1, 2, NULL, //scEdi_Int_Variables_IO[2], - (function_pointer)Edi_Int_IO_ReadByte - }, - {"int32_t", "read_word_io_port", 1, 2, NULL/*{ - {"pointer", "port_object", 0}, - {"pointer int16_t", "out", 0} - }*/, - (function_pointer)Edi_Int_IO_ReadWord - }, - {"int32_t", "read_long_io_port", 1, 2, NULL/*{ - {"pointer", "port_object", 0}, - {"pointer int32_t", "out", 0} - }*/, - (function_pointer)Edi_Int_IO_ReadDWord - }, - {"int32_t", "read_longlong_io_port", 1, 2, NULL/*{ - {"pointer", "port_object", 0}, - {"pointer int64_t", "out", 0} - }*/, - (function_pointer)Edi_Int_IO_ReadQWord - }, - {"int32_t", "read_string_io_port", 1, 3, NULL/*{ - {"pointer", "port_object", 0}, - {"int32_T", "data_length", 0}, - {"pointer int64_t", "out", 0} - }*/, - (function_pointer)Edi_Int_IO_ReadString - }, - - {"int32_t", "write_byte_io_port", 1, 2, NULL/*{ - {"pointer", "port_object", 0}, - {"int8_t", "in", 0} - }*/, - (function_pointer)Edi_Int_IO_WriteByte}, - {"int32_t", "write_word_io_port", 1, 2, NULL/*{ - {"pointer", "port_object", 0}, - {"int16_t", "in", 0} - }*/, - (function_pointer)Edi_Int_IO_WriteWord}, - {"int32_t", "write_long_io_port", 1, 2, NULL/*{ - {"pointer", "port_object", 0}, - {"int32_t", "in", 0} - }*/, - (function_pointer)Edi_Int_IO_WriteDWord}, - {"int32_t", "write_longlong_io_port", 1, 2, NULL/*{ - {"pointer", "port_object", 0}, - {"int64_t", "in", 0} - }*/, - (function_pointer)Edi_Int_IO_WriteQWord} - }; -static edi_class_declaration_t scEdi_Int_Class_IO = - { - IO_PORT_CLASS, 1, 12, - scEdi_Int_Functions_IO, - Edi_Int_IO_Construct, - Edi_Int_IO_Destruct, - NULL - }; diff --git a/Modules/Interfaces/EDI/main.c b/Modules/Interfaces/EDI/main.c deleted file mode 100644 index c3d0272a..00000000 --- a/Modules/Interfaces/EDI/main.c +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Acess2 EDI Layer - */ -#define DEBUG 0 -#define VERSION ((0<<8)|1) -#include -#include -#include -#define IMPLEMENTING_EDI 1 -#include "edi/edi.h" - -#define VALIDATE_PTR(_ptr,_err) do{if(!(_ptr))return _err; }while(0) -#define GET_DATA(_ptr) (Object) - -#include "edi_io.inc.c" -#include "edi_int.inc.c" - -// === STRUCTURES === -typedef struct sAcessEdiDriver { - struct sAcessEdiDriver *Next; - tDevFS_Driver Driver; - int FileCount; - struct { - char *Name; - tVFS_Node Node; - } *Files; - edi_object_metadata_t *Objects; - edi_initialization_t Init; - driver_finish_t Finish; -} tAcessEdiDriver; - -// === PROTOTYPES === - int EDI_Install(char **Arguments); - int EDI_DetectDriver(void *Base); - int EDI_LoadDriver(void *Base); -vfs_node *EDI_FS_ReadDir(vfs_node *Node, int Pos); -vfs_node *EDI_FS_FindDir(vfs_node *Node, char *Name); - int EDI_FS_CharRead(vfs_node *Node, Uint64 Offset, Uint64 Length, void *Buffer); - int EDI_FS_CharWrite(vfs_node *Node, Uint64 Offset, Uint64 Length, void *Buffer); - int EDI_FS_IOCtl(vfs_node *Node, int Id, void *Data); -data_pointer EDI_GetInternalClass(edi_string_t ClassName); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, EDI, EDI_Install, NULL, NULL); -tModuleLoader gEDI_Loader = { - NULL, "EDI", EDI_DetectDriver, EDI_LoadDriver, NULL -}; -tSpinlock glEDI_Drivers; -tAcessEdiDriver *gEdi_Drivers = NULL; -edi_class_declaration_t *gcEdi_IntClasses[] = { - &scEdi_Int_Class_IO, &scEdi_Int_Class_IRQ -}; -#define NUM_INT_CLASSES (sizeof(gcEdi_IntClasses)/sizeof(gcEdi_IntClasses[0])) -char *csCharNumbers[] = {"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9"}; -char *csBlockNumbers[] = {"blk0", "blk1", "blk2", "blk3", "blk4", "blk5", "blk6", "blk7", "blk8", "blk9"}; - -// === CODE === -/** - * \fn int EDI_Install(char **Arguments) - * \brief Stub intialisation routine - */ -int EDI_Install(char **Arguments) -{ - Module_RegisterLoader( &gEDI_Loader ); - return 1; -} - -/** - * \brief Detects if a driver should be loaded by the EDI subsystem - */ -int EDI_DetectDriver(void *Base) -{ - if( Binary_FindSymbol(BASE, "driver_init", NULL) == 0 ) - return 0; - - return 1; -} - -/** - * \fn int Edi_LoadDriver(void *Base) - * \brief Load an EDI Driver from a loaded binary - * \param Base Binary Handle - * \return 0 on success, non zero on error - */ -int EDI_LoadDriver(void *Base) -{ - driver_init_t init; - driver_finish_t finish; - tAcessEdiDriver *info; - int i, j; - int devfsId; - edi_class_declaration_t *classes; - - ENTER("pBase", Base); - - // Get Functions - if( !Binary_FindSymbol(Base, "driver_init", (Uint*)&init) - || !Binary_FindSymbol(Base, "driver_finish", (Uint*)&finish) ) - { - Warning("[EDI ] Driver %p does not provide both `driver_init` and `driver_finish`\n", Base); - Binary_Unload(Base); - return 0; - } - - // Allocate Driver Information - info = malloc( sizeof(tAcessEdiDriver) ); - info->Finish = finish; - - // Initialise Driver - info->Init = init( 0, NULL ); // TODO: Implement Resources - - LOG("info->Init.driver_name = '%s'", info->Init.driver_name); - LOG("info->Init.num_driver_classes = %i", info->Init.num_driver_classes); - - // Count mappable classes - classes = info->Init.driver_classes; - info->FileCount = 0; - info->Objects = NULL; - for( i = 0, j = 0; i < info->Init.num_driver_classes; i++ ) - { - if( strncmp(classes[i].name, "EDI-CHARACTER-DEVICE", 32) == 0 ) - { - data_pointer *obj; - // Initialise Object Instances - for( ; (obj = classes[i].constructor()); j++ ) { - LOG("%i - Constructed '%s'", j, classes[i].name); - info->FileCount ++; - info->Objects = realloc(info->Objects, sizeof(*info->Objects)*info->FileCount); - info->Objects[j].object = obj; - info->Objects[j].object_class = &classes[i]; - } - } - else - LOG("%i - %s", i, classes[i].name); - } - - if(info->FileCount) - { - int iNumChar = 0; - // Create VFS Nodes - info->Files = malloc( info->FileCount * sizeof(*info->Files) ); - memset(info->Files, 0, info->FileCount * sizeof(*info->Files)); - j = 0; - for( j = 0; j < info->FileCount; j++ ) - { - classes = info->Objects[j].object_class; - if( strncmp(classes->name, "EDI-CHARACTER-DEVICE", 32) == 0 ) - { - LOG("%i - %s", j, csCharNumbers[iNumChar]); - info->Files[j].Name = csCharNumbers[iNumChar]; - info->Files[j].Node.NumACLs = 1; - info->Files[j].Node.ACLs = &gVFS_ACL_EveryoneRW; - info->Files[j].Node.ImplPtr = &info->Objects[j]; - info->Files[j].Node.Read = EDI_FS_CharRead; - info->Files[j].Node.Write = EDI_FS_CharWrite; - info->Files[j].Node.IOCtl = EDI_FS_IOCtl; - info->Files[j].Node.CTime = - info->Files[j].Node.MTime = - info->Files[j].Node.ATime = now(); - - iNumChar ++; - continue; - } - } - - // Create DevFS Driver - info->Driver.ioctl = EDI_FS_IOCtl; - memsetda(&info->Driver.rootNode, 0, sizeof(vfs_node) / 4); - info->Driver.Name = info->Init.driver_name; - info->Driver.RootNode.Flags = VFS_FFLAG_DIRECTORY; - info->Driver.RootNode.NumACLs = 1; - info->Driver.RootNode.ACLs = &gVFS_ACL_EveryoneRX; - info->Driver.RootNode.Length = info->FileCount; - info->Driver.RootNode.ImplPtr = info; - info->Driver.RootNode.ReadDir = EDI_FS_ReadDir; - info->Driver.RootNode.FindDir = EDI_FS_FindDir; - info->Driver.RootNode.IOCtl = EDI_FS_IOCtl; - - // Register - devfsId = dev_addDevice( &info->Driver ); - if(devfsId == -1) { - free(info->Files); // Free Files - info->Finish(); // Clean up driver - free(info); // Free info structure - Binary_Unload(iDriverBase); // Unload library - return -3; // Return error - } - } - - // Append to loaded list; - LOCK(&glEDI_Drivers); - info->Next = gEDI_Drivers; - gEDI_Drivers = info; - RELEASE(&glEDI_Drivers); - - LogF("[EDI ] Loaded Driver '%s' (%s)\n", info->Init.driver_name, Path); - LEAVE('i', 1); - return 1; -} - -// --- Filesystem Interaction --- -/** - * \brief Read from a drivers class list - * \param Node Driver's Root Node - * \param Pos Index of file to get - */ -char *EDI_FS_ReadDir(tVFS_Node *Node, int Pos) -{ - tAcessEdiDriver *info; - - // Sanity Check - if(!Node) return NULL; - - // Get Information Structure - info = (void *) Node->ImplPtr; - if(!info) return NULL; - - // Check Position - if(Pos < 0) return NULL; - if(Pos >= info->FileCount) return NULL; - - return strdup( info->Files[Pos].Name ); -} - -/** - * \fn tVFS_Node *EDI_FS_FindDir(tVFS_Node *Node, char *Name) - * \brief Find a named file in a driver - * \param Node Driver's Root Node - * \param Name Name of file to find - */ -tVFS_Node *EDI_FS_FindDir(tVFS_Node *Node, char *Name) -{ - tAcessEdiDriver *info; - int i; - - // Sanity Check - if(!Node) return NULL; - if(!Name) return NULL; - - // Get Information Structure - info = (void *) Node->ImplPtr; - if(!info) return NULL; - - for( i = 0; i < info->FileCount; i++ ) - { - if(strcmp(info->Files[i].name, Name) == 0) - return &info->Files[i].Node; - } - - return NULL; -} - -/** - * \fn Uint64 EDI_FS_CharRead(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Read from an EDI Character Device - * \param Node File Node - * \param Offset Offset into file (ignored) - * \param Length Number of characters to read - * \param Buffer Destination for data - * \return Number of characters read - */ -Uint64 EDI_FS_CharRead(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - edi_object_metadata_t *meta; - edi_class_declaration_t *class; - - // Sanity Check - if(!Node || !Buffer) return 0; - if(Length <= 0) return 0; - // Get Object Metadata - meta = (void *) Node->ImplPtr; - if(!meta) return 0; - - // Get Class - class = meta->object_class; - if(!class) return 0; - - // Read from object - if( ((tAnyFunction) class->methods[0].code)( meta->object, Buffer, Length )) - return Length; - - return 0; -} - -/** - * \fn Uint64 EDI_FS_CharWrite(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Write to an EDI Character Device - * \param Node File Node - * \param Offset Offset into file (ignored) - * \param Length Number of characters to write - * \param Buffer Source for data - * \return Number of characters written - */ -Uint64 EDI_FS_CharWrite(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - edi_object_metadata_t *meta; - edi_class_declaration_t *class; - - // Sanity Check - if(!Node || !Buffer) return 0; - if(Length <= 0) return 0; - // Get Object Metadata - meta = (void *) Node->ImplPtr; - if(!meta) return 0; - - // Get Class - class = meta->object_class; - if(!class) return 0; - - // Write to object - if( ((tAnyFunction) class->methods[1].code)( meta->object, Buffer, Length )) - return Length; - - return 0; -} - -/** - * \fn int EDI_FS_IOCtl(tVFS_Node *Node, int Id, void *Data) - * \brief Perfom an IOCtl call on the object - */ -int EDI_FS_IOCtl(tVFS_Node *Node, int Id, void *Data) -{ - return 0; -} - -// --- EDI Functions --- -/** - * \fn data_pointer EDI_GetDefinedClass(edi_string_t ClassName) - * \brief Gets the structure of a driver defined class - * \param ClassName Name of class to find - * \return Class definition or NULL - */ -data_pointer EDI_GetDefinedClass(edi_string_t ClassName) -{ - int i; - tAcessEdiDriver *drv; - edi_class_declaration_t *classes; - - for(drv = gEdi_Drivers; - drv; - drv = drv->Next ) - { - classes = drv->Init.driver_classes; - for( i = 0; i < drv->Init.num_driver_classes; i++ ) - { - if( strncmp(classes[i].name, ClassName, 32) == 0 ) - return &classes[i]; - } - } - return NULL; -} - -/** - * \fn int32_t EDI_CheckClassExistence(edi_string_t ClassName) - * \brief Checks if a class exists - * \param ClassName Name of class - * \return 1 if the class exists, -1 otherwise - */ -int32_t EDI_CheckClassExistence(edi_string_t ClassName) -{ - //LogF("check_class_existence: (ClassName='%s')\n", ClassName); - if(EDI_GetInternalClass(ClassName)) - return 1; - - if(EDI_GetDefinedClass(ClassName)) // Driver Defined - return 1; - - return -1; -} - -/** - * \fn edi_object_metadata_t EDI_ConstructObject(edi_string_t ClassName) - * \brief Construct an instance of an class (an object) - * \param ClassName Name of the class to construct - */ -edi_object_metadata_t EDI_ConstructObject(edi_string_t ClassName) -{ - edi_object_metadata_t ret = {0, 0}; - edi_class_declaration_t *class; - - //LogF("EDI_ConstructObject: (ClassName='%s')\n", ClassName); - - // Get class definition - if( !(class = EDI_GetInternalClass(ClassName)) ) // Internal - if( !(class = EDI_GetDefinedClass(ClassName)) ) // Driver Defined - return ret; // Return ERROR - - // Initialise - ret.object = class->constructor(); - if( !ret.object ) - return ret; // Return ERROR - - // Set declaration pointer - ret.object_class = class; - - //LogF("EDI_ConstructObject: RETURN {0x%x,0x%x}\n", ret.object, ret.object_class); - return ret; -} - -/** - * \fn void EDI_DestroyObject(edi_object_metadata_t Object) - * \brief Destroy an instance of a class - * \param Object Object to destroy - */ -void EDI_DestroyObject(edi_object_metadata_t Object) -{ - if( !Object.object ) return; - if( !Object.object_class ) return; - - ((edi_class_declaration_t*)(Object.object_class))->destructor( &Object ); -} - -/** - * \fn function_pointer EDI_GetMethodByName(data_pointer ObjectClass, edi_string_t MethodName) - * \brief Get a method of a class by it's name - * \param ObjectClass Pointer to a ::edi_object_metadata_t of the object - * \param MethodName Name of the desired method - * \return Function address or NULL - */ -function_pointer EDI_GetMethodByName(data_pointer ObjectClass, edi_string_t MethodName) -{ - edi_class_declaration_t *dec = ObjectClass; - int i; - - //LogF("get_method_by_name: (ObjectClass=0x%x, MethodName='%s')\n", ObjectClass, MethodName); - - if(!ObjectClass) return NULL; - - for(i = 0; i < dec->num_methods; i++) - { - if(strncmp(MethodName, dec->methods[i].name, 32) == 0) - return dec->methods[i].code; - } - return NULL; -} - -#if 0 -function_pointer get_method_by_declaration(data_pointer Class, edi_function_declaration_t Declaration); -#endif - -/** - * \fn edi_string_ptr_t EDI_GetClassParent(edi_string_t ClassName) - * \brief Get the parent of the named class - * \todo Implement - */ -edi_string_ptr_t EDI_GetClassParent(edi_string_t ClassName) -{ - WarningEx("EDI", "`get_class_parent` is unimplemented"); - return NULL; -} - -/** - * \fn data_pointer EDI_GetInternalClass(edi_string_t ClassName) - * \brief Get a builtin class - * \param ClassName Name of class to find - * \return Pointer to the ::edi_class_declaration_t of the class - */ -data_pointer EDI_GetInternalClass(edi_string_t ClassName) -{ - int i; - //LogF("get_internal_class: (ClassName='%s')\n", ClassName); - for( i = 0; i < NUM_INT_CLASSES; i++ ) - { - if( strncmp( gcEdi_IntClasses[i]->name, ClassName, 32 ) == 0 ) { - return gcEdi_IntClasses[i]; - } - } - //LogF("get_internal_class: RETURN NULL\n"); - return NULL; -} - -/** - * \fn edi_string_ptr_t EDI_GetObjectClass(data_pointer Object) - * \brief Get the name of the object of \a Object - * \param Object Object to get name of - * \return Pointer to the class name - */ -edi_string_ptr_t EDI_GetObjectClass(data_pointer ObjectClass) -{ - edi_object_metadata_t *Metadata = ObjectClass; - // Sanity Check - if(!ObjectClass) return NULL; - if(!(edi_class_declaration_t*) Metadata->object_class) return NULL; - - // Return Class Name - return ((edi_class_declaration_t*) Metadata->object_class)->name; -} - -// === EXPORTS === -EXPORTAS(EDI_CheckClassExistence, check_class_existence); -EXPORTAS(EDI_ConstructObject, construct_object); -EXPORTAS(EDI_DestroyObject, destroy_object); -EXPORTAS(EDI_GetMethodByName, get_method_by_name); -EXPORTAS(EDI_GetClassParent, get_class_parent); -EXPORTAS(EDI_GetInternalClass, get_internal_class); -EXPORTAS(EDI_GetObjectClass, get_object_class); diff --git a/Modules/Interfaces/Makefile.tpl b/Modules/Interfaces/Makefile.tpl deleted file mode 100644 index 80c6d4dd..00000000 --- a/Modules/Interfaces/Makefile.tpl +++ /dev/null @@ -1 +0,0 @@ --include ../../Makefile.tpl diff --git a/Modules/Interfaces/UDI/Makefile b/Modules/Interfaces/UDI/Makefile deleted file mode 100644 index 4c199f18..00000000 --- a/Modules/Interfaces/UDI/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# -# - -CPPFLAGS = -I./include -OBJ = main.o logging.o strmem.o imc.o mem.o buf.o cb.o -OBJ += meta_mgmt.o meta_gio.o -OBJ += physio.o physio/meta_bus.o physio/meta_intr.o -NAME = UDI - --include ../Makefile.tpl diff --git a/Modules/Interfaces/UDI/Notes.txt b/Modules/Interfaces/UDI/Notes.txt deleted file mode 100644 index 1e2e9d45..00000000 --- a/Modules/Interfaces/UDI/Notes.txt +++ /dev/null @@ -1,36 +0,0 @@ - - logical volume metalanguage - for file system drivers to use, network protocol - metalanguage and audio support - madeofstaples expressed his interest in reviewing the OpenUSBDI specification to update - it for USB 3.0 - - -=== Initialisation === -udi_init_t - > udi_init_info - > This is the only defined symbol in a UDI driver - -udi_primary_init_t -- UDI_MAX_SCRATCH -- UDI_OP_LONG_EXEC - -udi_secondary_init_t - -udi_ops_init_t - -udi_cb_init_t - -udi_cb_select_t - -udi_gcb_init_t - -udi_init_context_t - -udi_limits_t -- UDI_MIN_ALLOC_LIMIT -- UDI_MIN_TRACE_LOG_LIMIT -- UDI_MIN_INSTANCE_ATTR_LIMIT - -udi_chan_context_t - -udi_child_chan_context_t diff --git a/Modules/Interfaces/UDI/buf.c b/Modules/Interfaces/UDI/buf.c deleted file mode 100644 index 5327cc8b..00000000 --- a/Modules/Interfaces/UDI/buf.c +++ /dev/null @@ -1,70 +0,0 @@ -/** - * \file buf.c - * \author John Hodge (thePowersGang) - * - * Buffer Manipulation - */ -#include -#include - -// === EXPORTS === -EXPORT(udi_buf_copy); -EXPORT(udi_buf_write); -EXPORT(udi_buf_read); -EXPORT(udi_buf_free); - -// === CODE === -void udi_buf_copy( - udi_buf_copy_call_t *callback, - udi_cb_t *gcb, - udi_buf_t *src_buf, - udi_size_t src_off, - udi_size_t src_len, - udi_buf_t *dst_buf, - udi_size_t dst_off, - udi_size_t dst_len, - udi_buf_path_t path_handle - ) -{ - UNIMPLEMENTED(); -} - -/** - * \brief Write to a buffer - * \param callback Function to call once the write has completed - * \param gcb Control Block - * \param src_mem Source Data - * \param src_len Length of source data - * \param dst_buf Destination buffer - * \param dst_off Destination offset in the buffer - * \param dst_len Length of destination area (What the?, Redundant - * Department of redundacny department) - * \param path_handle ??? - */ -void udi_buf_write( - udi_buf_write_call_t *callback, - udi_cb_t *gcb, - const void *src_mem, - udi_size_t src_len, - udi_buf_t *dst_buf, - udi_size_t dst_off, - udi_size_t dst_len, - udi_buf_path_t path_handle - ) -{ - UNIMPLEMENTED(); -} - -void udi_buf_read( - udi_buf_t *src_buf, - udi_size_t src_off, - udi_size_t src_len, - void *dst_mem ) -{ - UNIMPLEMENTED(); -} - -void udi_buf_free(udi_buf_t *buf) -{ - UNIMPLEMENTED(); -} diff --git a/Modules/Interfaces/UDI/cb.c b/Modules/Interfaces/UDI/cb.c deleted file mode 100644 index b615dfeb..00000000 --- a/Modules/Interfaces/UDI/cb.c +++ /dev/null @@ -1,60 +0,0 @@ -/** - * \file cb.c - * \author John Hodge (thePowersGang) - * \brief Control block code - */ -#include -#include - -// === CODE === -void udi_cb_alloc ( - udi_cb_alloc_call_t *callback, //!< Function to be called when the CB is allocated - udi_cb_t *gcb, //!< Parent Control Block - udi_index_t cb_idx, - udi_channel_t default_channel - ) -{ - UNIMPLEMENTED(); -} - -void udi_cb_alloc_dynamic( - udi_cb_alloc_call_t *callback, - udi_cb_t *gcb, - udi_index_t cb_idx, - udi_channel_t default_channel, - udi_size_t inline_size, - udi_layout_t *inline_layout - ) -{ - UNIMPLEMENTED(); -} - -void udi_cb_alloc_batch( - udi_cb_alloc_batch_call_t *callback, //!< - udi_cb_t *gcb, //!< - udi_index_t cb_idx, - udi_index_t count, - udi_boolean_t with_buf, - udi_size_t buf_size, - udi_buf_path_t path_handle - ) -{ - UNIMPLEMENTED(); -} - -void udi_cb_free(udi_cb_t *cb) -{ - UNIMPLEMENTED(); -} - -void udi_cancel(udi_cancel_call_t *callback, udi_cb_t *gcb) -{ - UNIMPLEMENTED(); -} - -// === EXPORTS === -EXPORT(udi_cb_alloc); -EXPORT(udi_cb_alloc_dynamic); -EXPORT(udi_cb_alloc_batch); -EXPORT(udi_cb_free); -EXPORT(udi_cancel); diff --git a/Modules/Interfaces/UDI/imc.c b/Modules/Interfaces/UDI/imc.c deleted file mode 100644 index cd15e070..00000000 --- a/Modules/Interfaces/UDI/imc.c +++ /dev/null @@ -1,69 +0,0 @@ -/** - * \file imc.c - * \author John Hodge (thePowersGang) - */ -#include -#include - -// === EXPORTS === -EXPORT(udi_channel_anchor); -EXPORT(udi_channel_spawn); -EXPORT(udi_channel_set_context); -EXPORT(udi_channel_op_abort); -EXPORT(udi_channel_close); -EXPORT(udi_channel_event_ind); -EXPORT(udi_channel_event_complete); - -// === CODE === -/** - */ -void udi_channel_anchor( - udi_channel_anchor_call_t *callback, udi_cb_t *gcb, - udi_channel_t channel, udi_index_t ops_idx, void *channel_context - ) -{ - Warning("%s Unimplemented", __func__); -} - -/** - */ -extern void udi_channel_spawn( - udi_channel_spawn_call_t *callback, udi_cb_t *gcb, - udi_channel_t channel, udi_index_t spawn_idx, - udi_index_t ops_idx, void *channel_context - ) -{ - Warning("%s Unimplemented", __func__); -} - -/** - * - */ -void udi_channel_set_context( - udi_channel_t target_channel, void *channel_context - ) -{ - Warning("%s Unimplemented", __func__); -} - -void udi_channel_op_abort( - udi_channel_t target_channel, udi_cb_t *orig_cb - ) -{ - Warning("%s Unimplemented", __func__); -} - -void udi_channel_close(udi_channel_t channel) -{ - Warning("%s Unimplemented", __func__); -} - -void udi_channel_event_ind(udi_channel_event_cb_t *cb) -{ - udi_channel_event_complete(cb, UDI_OK); -} - -void udi_channel_event_complete(udi_channel_event_cb_t *cb, udi_status_t status) -{ - Warning("%s Unimplemented", __func__); -} diff --git a/Modules/Interfaces/UDI/include/physio/meta_bus.h b/Modules/Interfaces/UDI/include/physio/meta_bus.h deleted file mode 100644 index 7b88ece8..00000000 --- a/Modules/Interfaces/UDI/include/physio/meta_bus.h +++ /dev/null @@ -1,74 +0,0 @@ -/** - * \file physio/meta_bus.h - */ -#ifndef _PHYSIO_META_BUS_H_ -#define _PHYSIO_META_BUS_H_ - -#include -#include - -typedef const struct udi_bus_device_ops_s udi_bus_device_ops_t; -typedef const struct udi_bus_bridge_ops_s udi_bus_bridge_ops_t; -typedef struct udi_bus_bind_cb_s udi_bus_bind_cb_t; -typedef void udi_bus_unbind_req_op_t(udi_bus_bind_cb_t *cb); -typedef void udi_bus_unbind_ack_op_t(udi_bus_bind_cb_t *cb); -typedef void udi_bus_bind_req_op_t(udi_bus_bind_cb_t *cb); -typedef void udi_bus_bind_ack_op_t( - udi_bus_bind_cb_t *cb, - udi_dma_constraints_t dma_constraints, - udi_ubit8_t preferred_endianness, - udi_status_t status - ); - - -struct udi_bus_device_ops_s -{ - udi_channel_event_ind_op_t *channel_event_ind_op; - udi_bus_bind_ack_op_t *bus_bind_ack_op; - udi_bus_unbind_ack_op_t *bus_unbind_ack_op; - udi_intr_attach_ack_op_t *intr_attach_ack_op; - udi_intr_detach_ack_op_t *intr_detach_ack_op; -}; -/* Bus Device Ops Vector Number */ -#define UDI_BUS_DEVICE_OPS_NUM 1 - -struct udi_bus_bridge_ops_s -{ - udi_channel_event_ind_op_t *channel_event_ind_op; - udi_bus_bind_req_op_t *bus_bind_req_op; - udi_bus_unbind_req_op_t *bus_unbind_req_op; - udi_intr_attach_req_op_t *intr_attach_req_op; - udi_intr_detach_req_op_t *intr_detach_req_op; -}; -/* Bus Bridge Ops Vector Number */ -#define UDI_BUS_BRIDGE_OPS_NUM - -struct udi_bus_bind_cb_s -{ - udi_cb_t gcb; -}; -/* Bus Bind Control Block Group Number */ -#define UDI_BUS_BIND_CB_NUM 1 - - -extern void udi_bus_bind_req(udi_bus_bind_cb_t *cb); - -extern void udi_bus_bind_ack( - udi_bus_bind_cb_t *cb, - udi_dma_constraints_t dma_constraints, - udi_ubit8_t preferred_endianness, - udi_status_t status - ); -/* Values for preferred_endianness */ -#define UDI_DMA_BIG_ENDIAN (1U<<5) -#define UDI_DMA_LITTLE_ENDIAN (1U<<6) -#define UDI_DMA_ANY_ENDIAN (1U<<0) - -extern void udi_bus_unbind_req(udi_bus_bind_cb_t *cb); -extern void udi_bus_unbind_ack(udi_bus_bind_cb_t *cb); - - - - - -#endif diff --git a/Modules/Interfaces/UDI/include/physio/meta_intr.h b/Modules/Interfaces/UDI/include/physio/meta_intr.h deleted file mode 100644 index d5dd3941..00000000 --- a/Modules/Interfaces/UDI/include/physio/meta_intr.h +++ /dev/null @@ -1,100 +0,0 @@ -/** - * \file physio/meta_intr.h - */ -#ifndef _PHYSIO_META_INTR_H_ -#define _PHYSIO_META_INTR_H_ - -#include -#include -#include "pio.h" - -typedef struct udi_intr_attach_cb_s udi_intr_attach_cb_t; -typedef void udi_intr_attach_req_op_t(udi_intr_attach_cb_t *intr_attach_cb); -typedef void udi_intr_attach_ack_op_t( - udi_intr_attach_cb_t *intr_attach_cb, - udi_status_t status - ); -typedef struct udi_intr_detach_cb_s udi_intr_detach_cb_t; -typedef void udi_intr_detach_req_op_t(udi_intr_detach_cb_t *intr_detach_cb); -typedef void udi_intr_detach_ack_op_t(udi_intr_detach_cb_t *intr_detach_cb); -typedef const struct udi_intr_handler_ops_s udi_intr_handler_ops_t; -typedef const struct udi_intr_dispatcher_ops_s udi_intr_dispatcher_ops_t; -typedef struct udi_intr_event_cb_s udi_intr_event_cb_t; -typedef void udi_intr_event_ind_op_t(udi_intr_event_cb_t *intr_event_cb, udi_ubit8_t flags); -typedef void udi_intr_event_rdy_op_t(udi_intr_event_cb_t *intr_event_cb); - - -struct udi_intr_attach_cb_s -{ - udi_cb_t gcb; - udi_index_t interrupt_idx; - udi_ubit8_t min_event_pend; - udi_pio_handle_t preprocessing_handle; -}; -/* Bridge Attach Control Block Group Number */ -#define UDI_BUS_INTR_ATTACH_CB_NUM 2 - -struct udi_intr_detach_cb_s -{ - udi_cb_t gcb; - udi_index_t interrupt_idx; -}; -/* Bridge Detach Control Block Group Number */ -#define UDI_BUS_INTR_DETACH_CB_NUM 3 - -struct udi_intr_handler_ops_s -{ - udi_channel_event_ind_op_t *channel_event_ind_op; - udi_intr_event_ind_op_t *intr_event_ind_op; -}; -/* Interrupt Handler Ops Vector Number */ -#define UDI_BUS_INTR_HANDLER_OPS_NUM 3 - -struct udi_intr_dispatcher_ops_s -{ - udi_channel_event_ind_op_t *channel_event_ind_op; - udi_intr_event_rdy_op_t *intr_event_rdy_op; -}; -/* Interrupt Dispatcher Ops Vector Number */ -#define UDI_BUS_INTR_DISPATCH_OPS_NUM 4 - -struct udi_intr_event_cb_s -{ - udi_cb_t gcb; - udi_buf_t *event_buf; - udi_ubit16_t intr_result; -}; -/* Flag values for interrupt handling */ -#define UDI_INTR_UNCLAIMED (1U<<0) -#define UDI_INTR_NO_EVENT (1U<<1) -/* Bus Interrupt Event Control Block Group Number */ -#define UDI_BUS_INTR_EVENT_CB_NUM 4 - - - -extern void udi_intr_attach_req(udi_intr_attach_cb_t *intr_attach_cb); -extern void udi_intr_attach_ack(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status); -extern void udi_intr_attach_ack_unused(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status); - -extern void udi_intr_detach_req(udi_intr_detach_cb_t *intr_detach_cb); -extern void udi_intr_detach_ack(udi_intr_detach_cb_t *intr_detach_cb); -extern void udi_intr_detach_ack_unused(udi_intr_detach_cb_t *intr_detach_cb); - - -extern void udi_intr_event_ind(udi_intr_event_cb_t *intr_event_cb, udi_ubit8_t flags); -/** - * \brief Values for ::udi_intr_event_ind \a flags - * \{ - */ -#define UDI_INTR_MASKING_NOT_REQUIRED (1U<<0) -#define UDI_INTR_OVERRUN_OCCURRED (1U<<1) -#define UDI_INTR_PREPROCESSED (1U<<2) -/** - * \} - */ - -extern void udi_intr_event_rdy(udi_intr_event_cb_t *intr_event_cb); - - - -#endif diff --git a/Modules/Interfaces/UDI/include/physio/pio.h b/Modules/Interfaces/UDI/include/physio/pio.h deleted file mode 100644 index 1ce305f2..00000000 --- a/Modules/Interfaces/UDI/include/physio/pio.h +++ /dev/null @@ -1,15 +0,0 @@ -/** - * \file physio/pio.h - */ -#ifndef _PHYSIO_PIO_H_ -#define _PHYSIO_PIO_H_ - -#include -#include - - -typedef _udi_handle_t udi_pio_handle_t; -/* Null handle value for udi_pio_handle_t */ -#define UDI_NULL_PIO_HANDLE _NULL_HANDLE - -#endif diff --git a/Modules/Interfaces/UDI/include/udi.h b/Modules/Interfaces/UDI/include/udi.h deleted file mode 100644 index 53953c07..00000000 --- a/Modules/Interfaces/UDI/include/udi.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * \file udi.h - */ -#ifndef _UDI_H_ -#define _UDI_H_ - -// Use the core acess file to use the specific size types (plus va_arg) -#include - -#include "udi/arch/x86.h" - -/** - * \name Values and Flags for udi_status_t - * \{ - */ -#define UDI_STATUS_CODE_MASK 0x0000FFFF -#define UDI_STAT_META_SPECIFIC 0x00008000 -#define UDI_SPECIFIC_STATUS_MASK 0x00007FFF -#define UDI_CORRELATE_OFFSET 16 -#define UDI_CORRELATE_MASK 0xFFFF0000 -/* Common Status Values */ -#define UDI_OK 0 -#define UDI_STAT_NOT_SUPPORTED 1 -#define UDI_STAT_NOT_UNDERSTOOD 2 -#define UDI_STAT_INVALID_STATE 3 -#define UDI_STAT_MISTAKEN_IDENTITY 4 -#define UDI_STAT_ABORTED 5 -#define UDI_STAT_TIMEOUT 6 -#define UDI_STAT_BUSY 7 -#define UDI_STAT_RESOURCE_UNAVAIL 8 -#define UDI_STAT_HW_PROBLEM 9 -#define UDI_STAT_NOT_RESPONDING 10 -#define UDI_STAT_DATA_UNDERRUN 11 -#define UDI_STAT_DATA_OVERRUN 12 -#define UDI_STAT_DATA_ERROR 13 -#define UDI_STAT_PARENT_DRV_ERROR 14 -#define UDI_STAT_CANNOT_BIND 15 -#define UDI_STAT_CANNOT_BIND_EXCL 16 -#define UDI_STAT_TOO_MANY_PARENTS 17 -#define UDI_STAT_BAD_PARENT_TYPE 18 -#define UDI_STAT_TERMINATED 19 -#define UDI_STAT_ATTR_MISMATCH 20 -/** - * \} - */ - -/** - * \name Data Layout Specifiers - * \{ - */ -typedef const udi_ubit8_t udi_layout_t; -/* Specific-Length Layout Type Codes */ -#define UDI_DL_UBIT8_T 1 -#define UDI_DL_SBIT8_T 2 -#define UDI_DL_UBIT16_T 3 -#define UDI_DL_SBIT16_T 4 -#define UDI_DL_UBIT32_T 5 -#define UDI_DL_SBIT32_T 6 -#define UDI_DL_BOOLEAN_T 7 -#define UDI_DL_STATUS_T 8 -/* Abstract Element Layout Type Codes */ -#define UDI_DL_INDEX_T 20 -/* Opaque Handle Element Layout Type Codes */ -#define UDI_DL_CHANNEL_T 30 -#define UDI_DL_ORIGIN_T 32 -/* Indirect Element Layout Type Codes */ -#define UDI_DL_BUF 40 -#define UDI_DL_CB 41 -#define UDI_DL_INLINE_UNTYPED 42 -#define UDI_DL_INLINE_DRIVER_TYPED 43 -#define UDI_DL_MOVABLE_UNTYPED 44 -/* Nested Element Layout Type Codes */ -#define UDI_DL_INLINE_TYPED 50 -#define UDI_DL_MOVABLE_TYPED 51 -#define UDI_DL_ARRAY 52 -#define UDI_DL_END 0 -/** - * \} - */ - - -// === INCLUDE SUB-SECTIONS === -#include "udi/cb.h" // Control Blocks -#include "udi/log.h" // Logging -#include "udi/attr.h" // Attributes -#include "udi/strmem.h" // String/Memory -#include "udi/buf.h" // Buffers -#include "udi/mem.h" // Memory Management -#include "udi/imc.h" // Inter-module Communication -#include "udi/meta_mgmt.h" // Management Metalanguage -#include "udi/meta_gio.h" // General IO Metalanguage -#include "udi/init.h" // Init - -#endif diff --git a/Modules/Interfaces/UDI/include/udi/arch/x86.h b/Modules/Interfaces/UDI/include/udi/arch/x86.h deleted file mode 100644 index 9c505d33..00000000 --- a/Modules/Interfaces/UDI/include/udi/arch/x86.h +++ /dev/null @@ -1,67 +0,0 @@ - -#ifndef _UDI_ARCH_x86_H_ -#define _UDI_ARCH_x86_H_ - -typedef Sint8 udi_sbit8_t; /* signed 8-bit: -2^7..2^7-1 */ -typedef Sint16 udi_sbit16_t; /* signed 16-bit: -2^15..2^15-1 */ -typedef Sint32 udi_sbit32_t; /* signed 32-bit: -2^31..2^31-1 */ -typedef Uint8 udi_ubit8_t; /* unsigned 8-bit: 0..28-1 */ -typedef Uint16 udi_ubit16_t; /* unsigned 16-bit: 0..216-1 */ -typedef Uint32 udi_ubit32_t; /* unsigned 32-bit: 0..232-1 */ - -typedef udi_ubit8_t udi_boolean_t; /* 0=False; 1..28-1=True */ -#define FALSE 0 -#define TRUE 1 - -typedef size_t udi_size_t; /* buffer size */ -typedef size_t udi_index_t; /* zero-based index type */ - -typedef void *_udi_handle_t; -#define _NULL_HANDLE NULL - -/* Channel Handle */ -typedef _udi_handle_t *udi_channel_t; -#define UDI_NULL_CHANNEL _NULL_HANDLE - -/** - * \brief Buffer Path - */ -typedef _udi_handle_t udi_buf_path_t; -#define UDI_NULL_BUF_PATH _NULL_HANDLE - -typedef _udi_handle_t udi_origin_t; -#define UDI_NULL_ORIGIN _NULL_HANDLE - -typedef Sint64 udi_timestamp_t; - -#define UDI_HANDLE_IS_NULL(handle, handle_type) (handle == NULL) -#define UDI_HANDLE_ID(handle, handle_type) ((Uint32)handle) - -/** - * \name va_arg wrapper - * \{ - */ -#define UDI_VA_ARG(pvar, type, va_code) va_arg(pvar,type) -#define UDI_VA_UBIT8_T -#define UDI_VA_SBIT8_T -#define UDI_VA_UBIT16_T -#define UDI_VA_SBIT16_T -#define UDI_VA_UBIT32_T -#define UDI_VA_SBIT32_T -#define UDI_VA_BOOLEAN_T -#define UDI_VA_INDEX_T -#define UDI_VA_SIZE_T -#define UDI_VA_STATUS_T -#define UDI_VA_CHANNEL_T -#define UDI_VA_ORIGIN_T -#define UDI_VA_POINTER -/** - * \} - */ - -/** - * \brief Status Type - */ -typedef udi_ubit32_t udi_status_t; - -#endif diff --git a/Modules/Interfaces/UDI/include/udi/attr.h b/Modules/Interfaces/UDI/include/udi/attr.h deleted file mode 100644 index a63ce8a7..00000000 --- a/Modules/Interfaces/UDI/include/udi/attr.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * \file udi_attr.h - */ -#ifndef _UDI_ATTR_H_ -#define _UDI_ATTR_H_ - -typedef struct udi_instance_attr_list_s udi_instance_attr_list_t; -typedef udi_ubit8_t udi_instance_attr_type_t; - -/* Instance attribute limits */ -#define UDI_MAX_ATTR_NAMELEN 32 -#define UDI_MAX_ATTR_SIZE 64 - -/** - * \brief Instance Attribute - */ -struct udi_instance_attr_list_s -{ - char attr_name[UDI_MAX_ATTR_NAMELEN]; - udi_ubit8_t attr_value[UDI_MAX_ATTR_SIZE]; - udi_ubit8_t attr_length; - udi_instance_attr_type_t attr_type; -}; - - -/** - * \brief Instance Attribute Types - * \see ::udi_instance_attr_type_t - */ -enum -{ - UDI_ATTR_NONE, - UDI_ATTR_STRING, - UDI_ATTR_ARRAY8, - UDI_ATTR_UBIT32, - UDI_ATTR_BOOLEAN, - UDI_ATTR_FILE -}; - - -#endif diff --git a/Modules/Interfaces/UDI/include/udi/buf.h b/Modules/Interfaces/UDI/include/udi/buf.h deleted file mode 100644 index fa2428b7..00000000 --- a/Modules/Interfaces/UDI/include/udi/buf.h +++ /dev/null @@ -1,105 +0,0 @@ -/** - * \file udi_buf.h - */ -#ifndef _UDI_BUF_H_ -#define _UDI_BUF_H_ - - -typedef struct udi_buf_s udi_buf_t; -typedef struct udi_xfer_constraints_s udi_xfer_constraints_t; -typedef void udi_buf_copy_call_t(udi_cb_t *gcb, udi_buf_t *new_dst_buf); -typedef void udi_buf_write_call_t(udi_cb_t *gcb, udi_buf_t *new_dst_buf); - -/** - * \brief Describes a buffer - * \note Semi-Opaque - */ -struct udi_buf_s -{ - udi_size_t buf_size; - Uint8 Data[]; //!< ENVIRONMENT ONLY -}; - -/** - * \brief - */ -struct udi_xfer_constraints_s -{ - udi_ubit32_t udi_xfer_max; - udi_ubit32_t udi_xfer_typical; - udi_ubit32_t udi_xfer_granularity; - udi_boolean_t udi_xfer_one_piece; - udi_boolean_t udi_xfer_exact_size; - udi_boolean_t udi_xfer_no_reorder; -}; - -// --- MACROS --- -/** - * \brief Allocates a buffer - */ -#define UDI_BUF_ALLOC(callback, gcb, init_data, size, path_handle) \ - udi_buf_write(callback, gcb, init_data, size, NULL, 0, 0, path_handle) - -/** - * \brief Inserts data into a buffer - */ -#define UDI_BUF_INSERT(callback, gcb, new_data, size, dst_buf, dst_off) \ - udi_buf_write(callback, gcb, new_data, size, dst_buf, dst_off, 0, UDI_NULL_BUF_PATH) - -/** - * \brief Removes data from a buffer (data afterwards will be moved forewards) - */ -#define UDI_BUF_DELETE(callback, gcb, size, dst_buf, dst_off) \ - udi_buf_write(callback, gcb, NULL, 0, dst_buf, dst_off, size, UDI_NULL_BUF_PATH) - -/** - * \brief Duplicates \a src_buf - */ -#define UDI_BUF_DUP(callback, gcb, src_buf, path_handle) \ - udi_buf_copy(callback, gcb, src_buf, 0, (src_buf)->buf_size, NULL, 0, 0, path_handle) - - -/** - * \brief Copies data from one buffer to another - */ -extern void udi_buf_copy( - udi_buf_copy_call_t *callback, - udi_cb_t *gcb, - udi_buf_t *src_buf, - udi_size_t src_off, - udi_size_t src_len, - udi_buf_t *dst_buf, - udi_size_t dst_off, - udi_size_t dst_len, - udi_buf_path_t path_handle ); - -/** - * \brief Copies data from driver space to a buffer - */ -extern void udi_buf_write( - udi_buf_write_call_t *callback, - udi_cb_t *gcb, - const void *src_mem, - udi_size_t src_len, - udi_buf_t *dst_buf, - udi_size_t dst_off, - udi_size_t dst_len, - udi_buf_path_t path_handle - ); - -/** - * \brief Reads data from a buffer into driver space - */ -extern void udi_buf_read( - udi_buf_t *src_buf, - udi_size_t src_off, - udi_size_t src_len, - void *dst_mem ); - -/** - * \brief Frees a buffer - */ -extern void udi_buf_free(udi_buf_t *buf); - - -#endif diff --git a/Modules/Interfaces/UDI/include/udi/cb.h b/Modules/Interfaces/UDI/include/udi/cb.h deleted file mode 100644 index 76db5c6e..00000000 --- a/Modules/Interfaces/UDI/include/udi/cb.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * \file udi_cb.h - */ -#ifndef _UDI_CB_H_ -#define _UDI_CB_H_ - -typedef struct udi_cb_s udi_cb_t; -typedef void udi_cb_alloc_call_t(udi_cb_t *gcb, udi_cb_t *new_cb); -typedef void udi_cb_alloc_batch_call_t(udi_cb_t *gcb, udi_cb_t *first_new_cb); -typedef void udi_cancel_call_t(udi_cb_t *gcb); - -#define UDI_GCB(mcb) (&(mcb)->gcb) -#define UDI_MCB(gcb, cb_type) ((cb_type *)(gcb)) - -/** - * \brief Describes a generic control block - * \note Semi-opaque - */ -struct udi_cb_s -{ - /** - * \brief Channel associated with the control block - */ - udi_channel_t channel; - /** - * \brief Current state - * \note Driver changable - */ - void *context; - /** - * \brief CB's scratch area - */ - void *scratch; - /** - * \brief ??? - */ - void *initiator_context; - /** - * \brief Request Handle? - */ - udi_origin_t origin; -}; - -extern void udi_cb_alloc ( - udi_cb_alloc_call_t *callback, - udi_cb_t *gcb, - udi_index_t cb_idx, - udi_channel_t default_channel - ); - -extern void udi_cb_alloc_dynamic( - udi_cb_alloc_call_t *callback, - udi_cb_t *gcb, - udi_index_t cb_idx, - udi_channel_t default_channel, - udi_size_t inline_size, - udi_layout_t *inline_layout - ); - -extern void udi_cb_alloc_batch( - udi_cb_alloc_batch_call_t *callback, - udi_cb_t *gcb, - udi_index_t cb_idx, - udi_index_t count, - udi_boolean_t with_buf, - udi_size_t buf_size, - udi_buf_path_t path_handle - ); - -extern void udi_cb_free(udi_cb_t *cb); - -extern void udi_cancel(udi_cancel_call_t *callback, udi_cb_t *gcb); - - - - -#endif diff --git a/Modules/Interfaces/UDI/include/udi/imc.h b/Modules/Interfaces/UDI/include/udi/imc.h deleted file mode 100644 index e5b3f3bb..00000000 --- a/Modules/Interfaces/UDI/include/udi/imc.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * \file udi_imc.h - * \brief Inter-Module Communication - */ -#ifndef _UDI_IMC_H_ -#define _UDI_IMC_H_ - -typedef void udi_channel_anchor_call_t(udi_cb_t *gcb, udi_channel_t anchored_channel); -typedef void udi_channel_spawn_call_t(udi_cb_t *gcb, udi_channel_t new_channel); - -typedef struct udi_channel_event_cb_s udi_channel_event_cb_t; - -typedef void udi_channel_event_ind_op_t(udi_channel_event_cb_t *cb); - -/** - * \brief Anchors a channel end to the current region - */ -extern void udi_channel_anchor( - udi_channel_anchor_call_t *callback, udi_cb_t *gcb, - udi_channel_t channel, udi_index_t ops_idx, void *channel_context - ); - -/** - * \brief Created a new channel between two regions - */ -extern void udi_channel_spawn( - udi_channel_spawn_call_t *callback, - udi_cb_t *gcb, - udi_channel_t channel, - udi_index_t spawn_idx, - udi_index_t ops_idx, - void *channel_context - ); - -/** - * \brief Attaches a new context pointer to the current channel - */ -extern void udi_channel_set_context( - udi_channel_t target_channel, - void *channel_context - ); -/** - * \brief - */ -extern void udi_channel_op_abort( - udi_channel_t target_channel, - udi_cb_t *orig_cb - ); - -/** - * \brief Closes an open channel - */ -extern void udi_channel_close(udi_channel_t channel); - -/** - * \brief Describes a channel event - */ -struct udi_channel_event_cb_s -{ - udi_cb_t gcb; - udi_ubit8_t event; - union { - struct { - udi_cb_t *bind_cb; - } internal_bound; - struct { - udi_cb_t *bind_cb; - udi_ubit8_t parent_ID; - udi_buf_path_t *path_handles; - } parent_bound; - udi_cb_t *orig_cb; - } params; -}; -/* Channel event types */ -#define UDI_CHANNEL_CLOSED 0 -#define UDI_CHANNEL_BOUND 1 -#define UDI_CHANNEL_OP_ABORTED 2 - -/** - * \brief Proxy function - */ -extern void udi_channel_event_ind(udi_channel_event_cb_t *cb); - -/** - * \brief Called when channel event is completed - */ -extern void udi_channel_event_complete( - udi_channel_event_cb_t *cb, udi_status_t status - ); - - -#endif diff --git a/Modules/Interfaces/UDI/include/udi/init.h b/Modules/Interfaces/UDI/include/udi/init.h deleted file mode 100644 index a6d5bb48..00000000 --- a/Modules/Interfaces/UDI/include/udi/init.h +++ /dev/null @@ -1,315 +0,0 @@ -/** - * \file udi_init.h - */ -#ifndef _UDI_INIT_H_ -#define _UDI_INIT_H_ - -typedef struct udi_init_s udi_init_t; -typedef struct udi_primary_init_s udi_primary_init_t; -typedef struct udi_secondary_init_s udi_secondary_init_t; -typedef struct udi_ops_init_s udi_ops_init_t; -typedef struct udi_cb_init_s udi_cb_init_t; -typedef struct udi_cb_select_s udi_cb_select_t; -typedef struct udi_gcb_init_s udi_gcb_init_t; - -typedef struct udi_init_context_s udi_init_context_t; -typedef struct udi_limits_s udi_limits_t; -typedef struct udi_chan_context_s udi_chan_context_t; -typedef struct udi_child_chan_context_s udi_child_chan_context_t; - -typedef void udi_op_t(void); -typedef udi_op_t * const udi_ops_vector_t; - -/** - * \brief UDI Initialisation Structure - * - * Defines how to initialise and use a UDI driver - */ -struct udi_init_s -{ - /** - * \brief Defines the primary region - * \note For secondary modules this must be NULL - */ - udi_primary_init_t *primary_init_info; - - /** - * \brief Defines all secondary regions - * Pointer to a list (so, essentially an array) of ::udi_secondary_init_t - * It is terminated by an entry with ::udi_secondary_init_t.region_idx - * set to zero. - * \note If NULL, it is to be treated as an empty list - */ - udi_secondary_init_t *secondary_init_list; - - /** - * \brief Channel operations - * Pointer to a ::udi_ops_init_t.ops_idx == 0 terminated list that - * defines the channel opterations usage for each ops vector implemented - * in this module. - * \note Must contain at least one entry for each metalanguage used - */ - udi_ops_init_t *ops_init_list; - - /** - * \brief Control Blocks - */ - udi_cb_init_t *cb_init_list; - - /** - * \brief Generic Control Blocks - */ - udi_gcb_init_t *gcb_init_list; - - /** - * \brief Overrides for control blocks - * Allows a control block to override the ammount of scratch space it - * gets for a specific ops vector. - */ - udi_cb_select_t *cb_select_list; -}; - - -/** - * \name Flags for ::udi_primary_init_t.mgmt_op_flags - * \{ - */ - -/** - * \brief Tells the environment that this operation may take some time - * Used as a hint in scheduling tasks - */ -#define UDI_OP_LONG_EXEC 0x01 - -/** - * \} - */ - -/** - * \brief Describes the Primary Region - * Tells the environment how to set up the driver's primary region. - */ -struct udi_primary_init_s -{ - /** - * \brief Management Ops Vector - * Pointer to a list of functions for the Management Metalanguage - */ - udi_mgmt_ops_t *mgmt_ops; - - /** - * \brief Flags for \a mgmt_ops - * Each entry in \a mgmt_ops is acommanied by an entry in this array. - * Each entry contains the flags that apply to the specified ops vector. - * \see UDI_OP_LONG_EXEC - */ - const udi_ubit8_t *mgmt_op_flags; - - /** - * \brief Scratch space size - * Specifies the number of bytes to allocate for each control block - * passed by the environment. - * \note must not exceed ::UDI_MAX_SCRATCH - */ - udi_size_t mgmt_scratch_requirement; - - /** - * \todo What is this? - */ - udi_ubit8_t enumeration_attr_list_length; - - /** - * \brief Size in bytes to allocate to each instance of the primary - * region - * Essentially the size of the driver's instance state - * \note Must be at least sizeof(udi_init_context_t) and not more - * than UDI_MIN_ALLOC_LIMIT - */ - udi_size_t rdata_size; - - /** - * \brief Size in bytes to allocate for each call to ::udi_enumerate_req - * \note Must not exceed UDI_MIN_ALLOC_LIMIT - */ - udi_size_t child_data_size; - - /** - * \brief Number of path handles for each parent bound to this driver - * \todo What the hell are path handles? - */ - udi_ubit8_t per_parent_paths; -}; - -/** - * \brief Tells the environment how to create a secondary region - */ -struct udi_secondary_init_s -{ - /** - * \brief Region Index - * Non-zero driver-dependent index value that identifies the region - * \note This corresponds to a "region" declaration in the udiprops.txt - * file. - */ - udi_index_t region_idx; - /** - * \brief Number of bytes to allocate - * - * \note Again, must be between sizeof(udi_init_context_t) and - * UDI_MIN_ALLOC_LIMIT - */ - udi_size_t rdata_size; -}; - -/** - * \brief Defines channel endpoints (ways of communicating with the driver) - * - */ -struct udi_ops_init_s -{ - /** - * \brief ops index number - * Used to uniquely this entry - * \note If this is zero, it marks the end of the list - */ - udi_index_t ops_idx; - /** - * \brief Metalanguage Index - * Defines what metalanguage is used - */ - udi_index_t meta_idx; - /** - * \brief Metalanguage Operation - * Defines what metalanguage operation is used - */ - udi_index_t meta_ops_num; - /** - * \brief Size of the context area - * \note If non-zero, must be at least - */ - udi_size_t chan_context_size; - /** - * \brief Pointer to the operations - * Pointer to a <>_<>_ops_t structure - */ - udi_ops_vector_t *ops_vector; - /** - * \brief Flags for each entry in \a ops_vector - */ - //const udi_ubit8_t *op_flags; -}; - -/** - * \brief Defines control blocks - * Much the same as ::udi_ops_init_t - */ -struct udi_cb_init_s -{ - udi_index_t cb_idx; - udi_index_t meta_idx; - udi_index_t meta_cb_num; - udi_size_t scratch_requirement; - /** - * \brief Size of inline memory - */ - udi_size_t inline_size; - /** - * \brief Layout of inline memory - */ - udi_layout_t *inline_layout; -}; - -/** - * \brief Overrides the scratch size for an operation - */ -struct udi_cb_select_s -{ - udi_index_t ops_idx; - udi_index_t cb_idx; -}; - -/** - * \brief General Control Blocks - * These control blocks can only be used as general data storage, not - * for any channel operations. - */ -struct udi_gcb_init_s -{ - udi_index_t cb_idx; - udi_size_t scratch_requirement; -}; - - -// === -// === -/** - * \brief Environement Imposed Limits - */ -struct udi_limits_s -{ - /** - * \brief Maximum legal ammount of memory that can be allocated - */ - udi_size_t max_legal_alloc; - - /** - * \brief Maximum ammount of guaranteed memory - */ - udi_size_t max_safe_alloc; - /** - * \brief Maximum size of the final string from ::udi_trace_write - * or ::udi_log_write - */ - udi_size_t max_trace_log_formatted_len; - /** - * \brief Maximum legal size of an instanct attribute value - */ - udi_size_t max_instance_attr_len; - /** - * \brief Minumum time difference (in nanoseconds between unique values - * returned by ::udi_time_current - */ - udi_ubit32_t min_curtime_res; - /** - * \brief Minimum resolution of timers - * \see ::udi_timer_start_repeating, ::udi_timer_start - */ - udi_ubit32_t min_timer_res; -} PACKED; - -/** - * \brief Primary Region Context data - */ -struct udi_init_context_s -{ - udi_index_t region_idx; - udi_limits_t limits; -}; - -/** - * \brief Channel context data - */ -struct udi_chan_context_s -{ - /** - * \brief Pointer to the driver instance's initial region data - */ - void *rdata; -} PACKED; - -/** - * \brief Child Channel context - */ -struct udi_child_chan_context_s -{ - /** - * \brief Pointer to the driver instance's initial region data - */ - void *rdata; - /** - * \brief Some sort of unique ID number - */ - udi_ubit32_t child_ID; -}; - -#endif diff --git a/Modules/Interfaces/UDI/include/udi/log.h b/Modules/Interfaces/UDI/include/udi/log.h deleted file mode 100644 index dccb1246..00000000 --- a/Modules/Interfaces/UDI/include/udi/log.h +++ /dev/null @@ -1,31 +0,0 @@ -/** - * \file udi_log.h - */ -#ifndef _UDI_LOG_H_ -#define _UDI_LOG_H_ - -/** - * \brief Trace Event - */ -typedef udi_ubit32_t udi_trevent_t; - -/** - * \brief Log Callback - */ -typedef void udi_log_write_call_t(udi_cb_t *gcb, udi_status_t correlated_status); - -/** - * \name Log Severities - * \brief Values for severity - * \{ - */ -#define UDI_LOG_DISASTER 1 -#define UDI_LOG_ERROR 2 -#define UDI_LOG_WARNING 3 -#define UDI_LOG_INFORMATION 4 -/** - * \} - */ - - -#endif diff --git a/Modules/Interfaces/UDI/include/udi/mem.h b/Modules/Interfaces/UDI/include/udi/mem.h deleted file mode 100644 index d29f25ed..00000000 --- a/Modules/Interfaces/UDI/include/udi/mem.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * \file udi_mem.h - */ -#ifndef _UDI_MEM_H_ -#define _UDI_MEM_H_ - -/** - * \brief Callback type for ::udi_mem_alloc - */ -typedef void udi_mem_alloc_call_t(udi_cb_t *gcb, void *new_mem); - -/** - * \brief Allocate memory - */ -extern void udi_mem_alloc( - udi_mem_alloc_call_t *callback, - udi_cb_t *gcb, - udi_size_t size, - udi_ubit8_t flags - ); - -/** - * \brief Values for ::udi_mem_alloc \a flags - * \{ - */ -#define UDI_MEM_NOZERO (1U<<0) //!< No need to zero the memory -#define UDI_MEM_MOVABLE (1U<<1) //!< Globally accessable memory? -/** - * \} - */ - -/** - * \brief Free allocated memory - */ -extern void udi_mem_free(void *target_mem); - - -#endif diff --git a/Modules/Interfaces/UDI/include/udi/meta_gio.h b/Modules/Interfaces/UDI/include/udi/meta_gio.h deleted file mode 100644 index d1cf86fd..00000000 --- a/Modules/Interfaces/UDI/include/udi/meta_gio.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * \file udi_meta_gio.h - */ -#ifndef _UDI_META_GIO_H_ -#define _UDI_META_GIO_H_ - -typedef const struct udi_gio_provider_ops_s udi_gio_provider_ops_t; -typedef const struct udi_gio_client_ops_s udi_gio_client_ops_t; -typedef struct udi_gio_bind_cb_s udi_gio_bind_cb_t; -typedef struct udi_gio_xfer_cb_s udi_gio_xfer_cb_t; -typedef struct udi_gio_rw_params_s udi_gio_rw_params_t; -typedef struct udi_gio_event_cb_s udi_gio_event_cb_t; - -typedef void udi_gio_bind_req_op_t(udi_gio_bind_cb_t *cb); -typedef void udi_gio_unbind_req_op_t(udi_gio_bind_cb_t *cb); -typedef void udi_gio_xfer_req_op_t(udi_gio_bind_cb_t *cb); -typedef void udi_gio_event_res_op_t(udi_gio_bind_cb_t *cb); - -typedef void udi_gio_bind_ack_op_t( - udi_gio_bind_cb_t *cb, - udi_ubit32_t device_size_lo, - udi_ubit32_t device_size_hi, - udi_status_t status - ); -typedef void udi_gio_unbind_ack_op_t(udi_gio_bind_cb_t *cb); -typedef void udi_gio_xfer_ack_op_t(udi_gio_bind_cb_t *cb); -typedef void udi_gio_xfer_nak_op_t(udi_gio_bind_cb_t *cb, udi_status_t status); -typedef void udi_gio_event_ind_op_t(udi_gio_bind_cb_t *cb); - -typedef udi_ubit8_t udi_gio_op_t; -/* Limit values for udi_gio_op_t */ -#define UDI_GIO_OP_CUSTOM 16 -#define UDI_GIO_OP_MAX 64 -/* Direction flag values for op */ -#define UDI_GIO_DIR_READ (1U<<6) -#define UDI_GIO_DIR_WRITE (1U<<7) -/* Standard Operation Codes */ -#define UDI_GIO_OP_READ UDI_GIO_DIR_READ -#define UDI_GIO_OP_WRITE UDI_GIO_DIR_WRITE - - - -struct udi_gio_provider_ops_s -{ - udi_channel_event_ind_op_t *channel_event_ind_op; - udi_gio_bind_req_op_t *gio_bind_req_op; - udi_gio_unbind_req_op_t *gio_unbind_req_op; - udi_gio_xfer_req_op_t *gio_xfer_req_op; - udi_gio_event_res_op_t *gio_event_res_op; -}; -/* Ops Vector Number */ -#define UDI_GIO_PROVIDER_OPS_NUM 1 - -struct udi_gio_client_ops_s -{ - udi_channel_event_ind_op_t *channel_event_ind_op; - udi_gio_bind_ack_op_t *gio_bind_ack_op; - udi_gio_unbind_ack_op_t *gio_unbind_ack_op; - udi_gio_xfer_ack_op_t *gio_xfer_ack_op; - udi_gio_xfer_nak_op_t *gio_xfer_nak_op; - udi_gio_event_ind_op_t *gio_event_ind_op; -}; -/* Ops Vector Number */ -#define UDI_GIO_CLIENT_OPS_NUM 2 - -struct udi_gio_bind_cb_s -{ - udi_cb_t gcb; - udi_xfer_constraints_t xfer_constraints; -}; -/* Control Block Group Number */ -#define UDI_GIO_BIND_CB_NUM 1 - - -struct udi_gio_xfer_cb_s -{ - udi_cb_t gcb; - udi_gio_op_t op; - void *tr_params; - udi_buf_t *data_buf; -}; -/* Control Block Group Number */ -#define UDI_GIO_XFER_CB_NUM 2 - -struct udi_gio_rw_params_s -{ - udi_ubit32_t offset_lo; - udi_ubit32_t offset_hi; -}; - -struct udi_gio_event_cb_s -{ - udi_cb_t gcb; - udi_ubit8_t event_code; - void *event_params; -}; -/* Control Block Group Number */ -#define UDI_GIO_EVENT_CB_NUM 3 - - -extern void udi_gio_bind_req(udi_gio_bind_cb_t *cb); -extern void udi_gio_bind_ack( - udi_gio_bind_cb_t *cb, - udi_ubit32_t device_size_lo, - udi_ubit32_t device_size_hi, - udi_status_t status - ); - -extern void udi_gio_unbind_req(udi_gio_bind_cb_t *cb); -extern void udi_gio_unbind_ack(udi_gio_bind_cb_t *cb); - -extern void udi_gio_xfer_req(udi_gio_xfer_cb_t *cb); -extern void udi_gio_xfer_ack(udi_gio_xfer_cb_t *cb); -extern void udi_gio_xfer_nak(udi_gio_xfer_cb_t *cb, udi_status_t status); - -extern void udi_gio_event_res(udi_gio_event_cb_t *cb); -extern void udi_gio_event_ind(udi_gio_event_cb_t *cb); -extern void udi_gio_event_res_unused(udi_gio_event_cb_t *cb); - -#endif diff --git a/Modules/Interfaces/UDI/include/udi/meta_mgmt.h b/Modules/Interfaces/UDI/include/udi/meta_mgmt.h deleted file mode 100644 index 97eccf2d..00000000 --- a/Modules/Interfaces/UDI/include/udi/meta_mgmt.h +++ /dev/null @@ -1,156 +0,0 @@ -/** - * \file udi_meta_mgmt.h - */ -#ifndef _UDI_META_MGMT_H_ -#define _UDI_META_MGMT_H_ - -typedef struct udi_mgmt_ops_s udi_mgmt_ops_t; -typedef struct udi_mgmt_cb_s udi_mgmt_cb_t; -typedef struct udi_usage_cb_s udi_usage_cb_t; -typedef struct udi_filter_element_s udi_filter_element_t; -typedef struct udi_enumerate_cb_s udi_enumerate_cb_t; - -/** - * \name Specify Usage - * \{ - */ -typedef void udi_usage_ind_op_t(udi_usage_cb_t *cb, udi_ubit8_t resource_level); -/* Values for resource_level */ -#define UDI_RESOURCES_CRITICAL 1 -#define UDI_RESOURCES_LOW 2 -#define UDI_RESOURCES_NORMAL 3 -#define UDI_RESOURCES_PLENTIFUL 4 -/* Proxy */ -extern void udi_static_usage(udi_usage_cb_t *cb, udi_ubit8_t resource_level); -/** - * \} - */ - -typedef void udi_usage_res_op_t(udi_usage_cb_t *cb); - -/** - * \name Enumerate this driver - * \{ - */ -typedef void udi_enumerate_req_op_t(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level); -/* Values for enumeration_level */ -#define UDI_ENUMERATE_START 1 -#define UDI_ENUMERATE_START_RESCAN 2 -#define UDI_ENUMERATE_NEXT 3 -#define UDI_ENUMERATE_NEW 4 -#define UDI_ENUMERATE_DIRECTED 5 -#define UDI_ENUMERATE_RELEASE 6 -/* Proxy */ -extern void udi_enumerate_no_children(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level); -/** - * \} - */ - -/** - * \name Enumeration Acknowlagement - * \{ - */ -typedef void udi_enumerate_ack_op_t(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_result, udi_index_t ops_idx); -/* Values for enumeration_result */ -#define UDI_ENUMERATE_OK 0 -#define UDI_ENUMERATE_LEAF 1 -#define UDI_ENUMERATE_DONE 2 -#define UDI_ENUMERATE_RESCAN 3 -#define UDI_ENUMERATE_REMOVED 4 -#define UDI_ENUMERATE_REMOVED_SELF 5 -#define UDI_ENUMERATE_RELEASED 6 -#define UDI_ENUMERATE_FAILED 255 -/** - * \} - */ - -/** - * \name - * \{ - */ -typedef void udi_devmgmt_req_op_t(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID); - -typedef void udi_devmgmt_ack_op_t(udi_mgmt_cb_t *cb, udi_ubit8_t flags, udi_status_t status); -/** - * \} - */ -typedef void udi_final_cleanup_req_op_t(udi_mgmt_cb_t *cb); -typedef void udi_final_cleanup_ack_op_t(udi_mgmt_cb_t *cb); - - - - - -struct udi_mgmt_ops_s -{ - udi_usage_ind_op_t *usage_ind_op; - udi_enumerate_req_op_t *enumerate_req_op; - udi_devmgmt_req_op_t *devmgmt_req_op; - udi_final_cleanup_req_op_t *final_cleanup_req_op; -}; - -struct udi_mgmt_cb_s -{ - udi_cb_t gcb; -}; - -struct udi_usage_cb_s -{ - udi_cb_t gcb; - udi_trevent_t trace_mask; - udi_index_t meta_idx; -}; - - -struct udi_filter_element_s -{ - char attr_name[UDI_MAX_ATTR_NAMELEN]; - udi_ubit8_t attr_min[UDI_MAX_ATTR_SIZE]; - udi_ubit8_t attr_min_len; - udi_ubit8_t attr_max[UDI_MAX_ATTR_SIZE]; - udi_ubit8_t attr_max_len; - udi_instance_attr_type_t attr_type; - udi_ubit32_t attr_stride; -}; -struct udi_enumerate_cb_s -{ - udi_cb_t gcb; - udi_ubit32_t child_ID; - void *child_data; - udi_instance_attr_list_t *attr_list; - udi_ubit8_t attr_valid_length; - const udi_filter_element_t *filter_list; - udi_ubit8_t filter_list_length; - udi_ubit8_t parent_ID; -}; -/* Special parent_ID filter values */ -#define UDI_ANY_PARENT_ID 0 - -/** - * \brief - */ -extern void udi_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID ); -/** - * \brief Values for ::udi_devmgmt_req \a mgmt_op - */ -enum eDMGMT -{ - UDI_DMGMT_PREPARE_TO_SUSPEND = 1, - UDI_DMGMT_SUSPEND, - UDI_DMGMT_SHUTDOWN, - UDI_DMGMT_PARENT_SUSPENDED, - UDI_DMGMT_RESUME, - UDI_DMGMT_UNBIND -}; - -extern void udi_devmgmt_ack(udi_mgmt_cb_t *cb, udi_ubit8_t flags, udi_status_t status); -//!\brief Values for flags -#define UDI_DMGMT_NONTRANSPARENT (1U<<0) -//!\brief Meta-Specific Status Codes -#define UDI_DMGMT_STAT_ROUTING_CHANGE (UDI_STAT_META_SPECIFIC|1) - -extern void udi_final_cleanup_req(udi_mgmt_cb_t *cb); -extern void udi_final_cleanup_ack(udi_mgmt_cb_t *cb); - - -#endif diff --git a/Modules/Interfaces/UDI/include/udi/strmem.h b/Modules/Interfaces/UDI/include/udi/strmem.h deleted file mode 100644 index f540a3ea..00000000 --- a/Modules/Interfaces/UDI/include/udi/strmem.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * \file udi_strmem.h - */ -#ifndef _UDI_STRMEM_H_ -#define _UDI_STRMEM_H_ - -/** - * \brief Gets the length of a C style string - */ -extern udi_size_t udi_strlen(const char *s); - -/** - * \brief Appends to a string - */ -extern char *udi_strcat(char *s1, const char *s2); -extern char *udi_strncat(char *s1, const char *s2, udi_size_t n); - -/** - * \brief Compares Strings/Memory - */ -extern udi_sbit8_t udi_strcmp(const char *s1, const char *s2); -extern udi_sbit8_t udi_strncmp(const char *s1, const char *s2, udi_size_t n); -extern udi_sbit8_t udi_memcmp(const void *s1, const void *s2, udi_size_t n); - -extern char *udi_strcpy(char *s1, const char *s2); -extern char *udi_strncpy(char *s1, const char *s2, udi_size_t n); -extern void *udi_memcpy(void *s1, const void *s2, udi_size_t n); -extern void *udi_memmove(void *s1, const void *s2, udi_size_t n); - -extern char *udi_strncpy_rtrim(char *s1, const char *s2, udi_size_t n); - -extern char *udi_strchr(const char *s, char c); -extern char *udi_strrchr(const char *s, char c); -extern void *udi_memchr (const void *s, udi_ubit8_t c, udi_size_t n); - -extern void *udi_memset(void *s, udi_ubit8_t c, udi_size_t n); -extern udi_ubit32_t udi_strtou32(const char *s, char **endptr, int base); - - -extern udi_size_t udi_snprintf(char *s, udi_size_t max_bytes, const char *format, ...); - - - -#endif diff --git a/Modules/Interfaces/UDI/include/udi_physio.h b/Modules/Interfaces/UDI/include/udi_physio.h deleted file mode 100644 index 1397b1c0..00000000 --- a/Modules/Interfaces/UDI/include/udi_physio.h +++ /dev/null @@ -1,144 +0,0 @@ -/** - * \file udi_physio.h - */ -#ifndef _UDI_PHYSIO_H_ -#define _UDI_PHYSIO_H_ - -#include - -// === TYPEDEFS === -// DMA Core -typedef _udi_handle_t udi_dma_handle_t; -#define UDI_NULL_DMA_HANDLE _NULL_HANDLE -typedef Uint64 udi_busaddr64_t; //!< \note Opaque -typedef struct udi_scgth_element_32_s udi_scgth_element_32_t; -typedef struct udi_scgth_element_64_s udi_scgth_element_64_t; -typedef struct udi_scgth_s udi_scgth_t; -typedef _udi_handle_t udi_dma_constraints_t; -#define UDI_NULL_DMA_CONSTRAINTS _NULL_HANDLE -/** - * \name DMA constraints attributes - * \{ - */ -typedef udi_ubit8_t udi_dma_constraints_attr_t; -/* DMA Convenience Attribute Codes */ -#define UDI_DMA_ADDRESSABLE_BITS 100 -#define UDI_DMA_ALIGNMENT_BITS 101 -/* DMA Constraints on the Entire Transfer */ -#define UDI_DMA_DATA_ADDRESSABLE_BITS 110 -#define UDI_DMA_NO_PARTIAL 111 -/* DMA Constraints on the Scatter/Gather List */ -#define UDI_DMA_SCGTH_MAX_ELEMENTS 120 -#define UDI_DMA_SCGTH_FORMAT 121 -#define UDI_DMA_SCGTH_ENDIANNESS 122 -#define UDI_DMA_SCGTH_ADDRESSABLE_BITS 123 -#define UDI_DMA_SCGTH_MAX_SEGMENTS 124 -/* DMA Constraints on Scatter/Gather Segments */ -#define UDI_DMA_SCGTH_ALIGNMENT_BITS 130 -#define UDI_DMA_SCGTH_MAX_EL_PER_SEG 131 -#define UDI_DMA_SCGTH_PREFIX_BYTES 132 -/* DMA Constraints on Scatter/Gather Elements */ -#define UDI_DMA_ELEMENT_ALIGNMENT_BITS 140 -#define UDI_DMA_ELEMENT_LENGTH_BITS 141 -#define UDI_DMA_ELEMENT_GRANULARITY_BITS 142 -/* DMA Constraints for Special Addressing */ -#define UDI_DMA_ADDR_FIXED_BITS 150 -#define UDI_DMA_ADDR_FIXED_TYPE 151 -#define UDI_DMA_ADDR_FIXED_VALUE_LO 152 -#define UDI_DMA_ADDR_FIXED_VALUE_HI 153 -/* DMA Constraints on DMA Access Behavior */ -#define UDI_DMA_SEQUENTIAL 160 -#define UDI_DMA_SLOP_IN_BITS 161 -#define UDI_DMA_SLOP_OUT_BITS 162 -#define UDI_DMA_SLOP_OUT_EXTRA 163 -#define UDI_DMA_SLOP_BARRIER_BITS 164 -/* Values for UDI_DMA_SCGTH_ENDIANNESS */ -#define UDI_DMA_LITTLE_ENDIAN (1U<<6) -#define UDI_DMA_BIG_ENDIAN (1U<<5) -/* Values for UDI_DMA_ADDR_FIXED_TYPE */ -#define UDI_DMA_FIXED_ELEMENT 1 -/** - * \} - */ -// DMA Constraints Management -typedef struct udi_dma_constraints_attr_spec_s udi_dma_constraints_attr_spec_t; -typedef void udi_dma_constraints_attr_set_call_t( - udi_cb_t *gcb, udi_dma_constraints_t new_constraints, udi_status_t status - ); -typedef struct udi_dma_limits_s udi_dma_limits_t; - - -// === STRUCTURES === -// --- DMA Constraints Management --- -struct udi_dma_constraints_attr_spec_s -{ - udi_dma_constraints_attr_t attr_type; - udi_ubit32_t attr_value; -}; -// --- DMA Core --- -struct udi_dma_limits_s -{ - udi_size_t max_legal_contig_alloc; - udi_size_t max_safe_contig_alloc; - udi_size_t cache_line_size; -}; -struct udi_scgth_element_32_s -{ - udi_ubit32_t block_busaddr; - udi_ubit32_t block_length; -}; -struct udi_scgth_element_64_s -{ - udi_busaddr64_t block_busaddr; - udi_ubit32_t block_length; - udi_ubit32_t el_reserved; -}; -/* Extension Flag */ -#define UDI_SCGTH_EXT 0x80000000 -struct udi_scgth_s -{ - udi_ubit16_t scgth_num_elements; - udi_ubit8_t scgth_format; - udi_boolean_t scgth_must_swap; - union { - udi_scgth_element_32_t *el32p; - udi_scgth_element_64_t *el64p; - } scgth_elements; - union { - udi_scgth_element_32_t el32; - udi_scgth_element_64_t el64; - } scgth_first_segment; -}; -/* Values for scgth_format */ -#define UDI_SCGTH_32 (1U<<0) -#define UDI_SCGTH_64 (1U<<1) -#define UDI_SCGTH_DMA_MAPPED (1U<<6) -#define UDI_SCGTH_DRIVER_MAPPED (1U<<7) - - - -// === FUNCTIONS === -// --- DMA Constraints Management --- -extern void udi_dma_constraints_attr_set( - udi_dma_constraints_attr_set_call_t *callback, - udi_cb_t *gcb, - udi_dma_constraints_t src_constraints, - const udi_dma_constraints_attr_spec_t *attr_list, - udi_ubit16_t list_length, - udi_ubit8_t flags - ); -/* Constraints Flags */ -#define UDI_DMA_CONSTRAINTS_COPY (1U<<0) - -extern void udi_dma_constraints_attr_reset( - udi_dma_constraints_t constraints, - udi_dma_constraints_attr_t attr_type - ); - -extern void udi_dma_constraints_free(udi_dma_constraints_t constraints); - -#include -#include - - -#endif diff --git a/Modules/Interfaces/UDI/logging.c b/Modules/Interfaces/UDI/logging.c deleted file mode 100644 index 2e58e373..00000000 --- a/Modules/Interfaces/UDI/logging.c +++ /dev/null @@ -1,18 +0,0 @@ -/** - * \file logging.c - * \author John Hodge (thePowersGang) - */ -#include -#include - -// === PROTOTYPES === - -// === CODE === -void udi_log_write( udi_log_write_call_t *callback, udi_cb_t *gcb, - udi_trevent_t trace_event, udi_ubit8_t severity, udi_index_t meta_idx, - udi_status_t original_status, udi_ubit32_t msgnum, ... ) -{ - Log("UDI Log"); -} - -EXPORT(udi_log_write); diff --git a/Modules/Interfaces/UDI/main.c b/Modules/Interfaces/UDI/main.c deleted file mode 100644 index a4a1504a..00000000 --- a/Modules/Interfaces/UDI/main.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Acess2 UDI Layer - */ -#define DEBUG 0 -#define VERSION ((0<<8)|1) -#include -#include -#include - -// === PROTOTYPES === - int UDI_Install(char **Arguments); - int UDI_DetectDriver(void *Base); - int UDI_LoadDriver(void *Base); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, UDI, UDI_Install, NULL, NULL); -tModuleLoader gUDI_Loader = { - NULL, "UDI", UDI_DetectDriver, UDI_LoadDriver, NULL -}; - -// === CODE === -/** - * \fn int UDI_Install(char **Arguments) - * \brief Stub intialisation routine - */ -int UDI_Install(char **Arguments) -{ - Module_RegisterLoader( &gUDI_Loader ); - return MODULE_ERR_OK; -} - -/** - * \brief Detects if a driver should be loaded by the UDI subsystem - */ -int UDI_DetectDriver(void *Base) -{ - if( Binary_FindSymbol(Base, "udi_init_info", NULL) == 0) { - return 0; - } - - return 1; -} - -/** - * \fn int UDI_LoadDriver(void *Base) - */ -int UDI_LoadDriver(void *Base) -{ - udi_init_t *info; - char *udiprops = NULL; - int udiprops_size = 0; - int i; - // int j; - - Log_Debug("UDI", "UDI_LoadDriver: (Base=%p)", Base); - - if( Binary_FindSymbol(Base, "udi_init_info", (Uint*)&info) == 0) { - Binary_Unload(Base); - return 0; - } - - if( Binary_FindSymbol(Base, "_udiprops", (Uint*)&udiprops) == 0 ) { - Log_Warning("UDI", "_udiprops is not defined, this is usually bad"); - } - else { - Uint udiprops_end = 0; - int i, j, nLines; - char **udipropsptrs; - - if( Binary_FindSymbol(Base, "_udiprops_end", (Uint*)&udiprops_end) == 0) - Log_Warning("UDI", "_udiprops_end is not defined"); - Log_Debug("UDI", "udiprops_end = %p", udiprops_end); - udiprops_size = udiprops_end - (Uint)udiprops; - Log_Debug("UDI", "udiprops = %p, udiprops_size = 0x%x", udiprops, udiprops_size); - - Debug_HexDump("UDI_LoadDriver", udiprops, udiprops_size); - - nLines = 1; - for( i = 0; i < udiprops_size; i++ ) - { - if( udiprops[i] == '\0' ) - nLines ++; - } - - Log_Debug("UDI", "nLines = %i", nLines); - - udipropsptrs = malloc( sizeof(char*)*nLines ); - udipropsptrs[0] = udiprops; - j = 0; - for( i = 0; i < udiprops_size; i++ ) - { - if( udiprops[i] == '\0' ) { - //Log_Debug("UDI", "udipropsptrs[%i] = '%s'", j, udipropsptrs[j]); - udipropsptrs[j++] = &udiprops[i+1]; - } - } - Log_Debug("UDI", "udipropsptrs[%i] = '%s'", j, udipropsptrs[j]); - Log_Debug("UDI", "udiprops = \"%s\"", udiprops); - } - - - Log("primary_init_info = %p = {", info->primary_init_info); - { - Log(" .mgmt_ops = %p = {", info->primary_init_info->mgmt_ops); - Log(" .usage_ind_op: %p() - 0x%02x", - info->primary_init_info->mgmt_ops->usage_ind_op, - info->primary_init_info->mgmt_op_flags[0] - ); - Log(" .enumerate_req_op: %p() - 0x%02x", - info->primary_init_info->mgmt_ops->enumerate_req_op, - info->primary_init_info->mgmt_op_flags[1] - ); - Log(" .devmgmt_req_op: %p() - 0x%02x", - info->primary_init_info->mgmt_ops->devmgmt_req_op, - info->primary_init_info->mgmt_op_flags[2] - ); - Log(" .final_cleanup_req_op: %p() - 0x%02x", - info->primary_init_info->mgmt_ops->final_cleanup_req_op, - info->primary_init_info->mgmt_op_flags[3] - ); - Log(" }"); - Log(" .mgmt_scratch_requirement = 0x%x", info->primary_init_info->mgmt_scratch_requirement); - Log(" .enumeration_attr_list_length = 0x%x", info->primary_init_info->enumeration_attr_list_length); - Log(" .rdata_size = 0x%x", info->primary_init_info->rdata_size); - Log(" .child_data_size = 0x%x", info->primary_init_info->child_data_size); - Log(" .per_parent_paths = 0x%x", info->primary_init_info->per_parent_paths); - } - Log("}"); - Log("secondary_init_list = %p", info->secondary_init_list); - Log("ops_init_list = %p", info->ops_init_list); - - for( i = 0; info->ops_init_list[i].ops_idx; i++ ) - { - Log("info->ops_init_list[%i] = {", i); - Log(" .ops_idx = 0x%x", info->ops_init_list[i].ops_idx); - Log(" .meta_idx = 0x%x", info->ops_init_list[i].meta_idx); - Log(" .meta_ops_num = 0x%x", info->ops_init_list[i].meta_ops_num); - Log(" .chan_context_size = 0x%x", info->ops_init_list[i].chan_context_size); - Log(" .ops_vector = %p", info->ops_init_list[i].ops_vector); -// Log(" .op_flags = %p", info->ops_init_list[i].op_flags); - Log("}"); - } - - return 0; -} diff --git a/Modules/Interfaces/UDI/mem.c b/Modules/Interfaces/UDI/mem.c deleted file mode 100644 index a09d54c5..00000000 --- a/Modules/Interfaces/UDI/mem.c +++ /dev/null @@ -1,32 +0,0 @@ -/** - * \file mem.c - * \author John Hodge (thePowersGang) - */ -#include -#include - -// === EXPORTS === -EXPORT(udi_mem_alloc); -EXPORT(udi_mem_free); - -// === CODE === -void udi_mem_alloc( - udi_mem_alloc_call_t *callback, - udi_cb_t *gcb, - udi_size_t size, - udi_ubit8_t flags - ) -{ - void *buf = malloc(size); - if(buf) - { - if( !(flags & UDI_MEM_NOZERO) ) - memset(buf, 0, size); - } - callback(gcb, buf); -} - -void udi_mem_free(void *target_mem) -{ - free(target_mem); -} diff --git a/Modules/Interfaces/UDI/meta_gio.c b/Modules/Interfaces/UDI/meta_gio.c deleted file mode 100644 index 1618c27f..00000000 --- a/Modules/Interfaces/UDI/meta_gio.c +++ /dev/null @@ -1,68 +0,0 @@ -/** - * \file meta_gio.c - * \author John Hodge (thePowersGang) - */ -#include -#include - -// === EXPORTS === -EXPORT(udi_gio_bind_req); -EXPORT(udi_gio_bind_ack); -EXPORT(udi_gio_unbind_req); -EXPORT(udi_gio_unbind_ack); -EXPORT(udi_gio_xfer_req); -EXPORT(udi_gio_xfer_ack); -EXPORT(udi_gio_xfer_nak); -EXPORT(udi_gio_event_res); -EXPORT(udi_gio_event_ind); -EXPORT(udi_gio_event_res_unused); - -// === CODE === -void udi_gio_bind_req(udi_gio_bind_cb_t *cb) -{ - UNIMPLEMENTED(); -} -void udi_gio_bind_ack( - udi_gio_bind_cb_t *cb, - udi_ubit32_t device_size_lo, - udi_ubit32_t device_size_hi, - udi_status_t status - ) -{ - UNIMPLEMENTED(); -} - -void udi_gio_unbind_req(udi_gio_bind_cb_t *cb) -{ - UNIMPLEMENTED(); -} -void udi_gio_unbind_ack(udi_gio_bind_cb_t *cb) -{ - UNIMPLEMENTED(); -} - -void udi_gio_xfer_req(udi_gio_xfer_cb_t *cb) -{ - UNIMPLEMENTED(); -} -void udi_gio_xfer_ack(udi_gio_xfer_cb_t *cb) -{ - UNIMPLEMENTED(); -} -void udi_gio_xfer_nak(udi_gio_xfer_cb_t *cb, udi_status_t status) -{ - UNIMPLEMENTED(); -} - -void udi_gio_event_res(udi_gio_event_cb_t *cb) -{ - UNIMPLEMENTED(); -} -void udi_gio_event_ind(udi_gio_event_cb_t *cb) -{ - UNIMPLEMENTED(); -} -void udi_gio_event_res_unused(udi_gio_event_cb_t *cb) -{ - UNIMPLEMENTED(); -} diff --git a/Modules/Interfaces/UDI/meta_mgmt.c b/Modules/Interfaces/UDI/meta_mgmt.c deleted file mode 100644 index 45a02c46..00000000 --- a/Modules/Interfaces/UDI/meta_mgmt.c +++ /dev/null @@ -1,49 +0,0 @@ -/** - * \file meta_mgmt.c - * \author John Hodge (thePowersGang) - */ -#include -#include - -// === EXPORTS === -EXPORT(udi_devmgmt_req); -EXPORT(udi_devmgmt_ack); -EXPORT(udi_final_cleanup_req); -EXPORT(udi_final_cleanup_ack); -EXPORT(udi_static_usage); -EXPORT(udi_enumerate_no_children); - -// === CODE === -void udi_devmgmt_req(udi_mgmt_cb_t *cb, udi_ubit8_t mgmt_op, udi_ubit8_t parent_ID ) -{ - ENTER("pcb imgmt_op iparent_ID", cb, mgmt_op, parent_ID); - LEAVE('-'); -} - -void udi_devmgmt_ack(udi_mgmt_cb_t *cb, udi_ubit8_t flags, udi_status_t status) -{ - ENTER("pcb xflags istatus", cb, flags, status); - LEAVE('-'); -} - -void udi_final_cleanup_req(udi_mgmt_cb_t *cb) -{ - ENTER("pcb", cb); - LEAVE('-'); -} - -void udi_final_cleanup_ack(udi_mgmt_cb_t *cb) -{ - ENTER("pcb", cb); - LEAVE('-'); -} - -void udi_static_usage(udi_usage_cb_t *cb, udi_ubit8_t resource_level) -{ - UNIMPLEMENTED(); -} - -void udi_enumerate_no_children(udi_enumerate_cb_t *cb, udi_ubit8_t enumeration_level) -{ - UNIMPLEMENTED(); -} diff --git a/Modules/Interfaces/UDI/physio.c b/Modules/Interfaces/UDI/physio.c deleted file mode 100644 index 881c9f40..00000000 --- a/Modules/Interfaces/UDI/physio.c +++ /dev/null @@ -1,25 +0,0 @@ -/** - * \file physio.c - * \author John Hodge (thePowersGang) - */ -#include -#include -#include - -// === EXPORTS === -EXPORT(udi_dma_constraints_attr_reset); -EXPORT(udi_dma_constraints_free); - -// === CODE === -void udi_dma_constraints_attr_reset( - udi_dma_constraints_t constraints, - udi_dma_constraints_attr_t attr_type - ) -{ - UNIMPLEMENTED(); -} - -void udi_dma_constraints_free(udi_dma_constraints_t constraints) -{ - UNIMPLEMENTED(); -} diff --git a/Modules/Interfaces/UDI/physio/meta_bus.c b/Modules/Interfaces/UDI/physio/meta_bus.c deleted file mode 100644 index e1e6a47d..00000000 --- a/Modules/Interfaces/UDI/physio/meta_bus.c +++ /dev/null @@ -1,38 +0,0 @@ -/** - * \file physio/meta_bus.c - * \author John Hodge (thePowersGang) - */ -#include -#include -#include - -// === EXPORTS === -EXPORT(udi_bus_unbind_req); -EXPORT(udi_bus_unbind_ack); -EXPORT(udi_bus_bind_req); -EXPORT(udi_bus_bind_ack); - -// === CODE === -void udi_bus_unbind_req(udi_bus_bind_cb_t *cb) -{ - UNIMPLEMENTED(); -} -void udi_bus_unbind_ack(udi_bus_bind_cb_t *cb) -{ - UNIMPLEMENTED(); -} - -void udi_bus_bind_req(udi_bus_bind_cb_t *cb) -{ - UNIMPLEMENTED(); -} - -void udi_bus_bind_ack( - udi_bus_bind_cb_t *cb, - udi_dma_constraints_t dma_constraints, - udi_ubit8_t preferred_endianness, - udi_status_t status - ) -{ - UNIMPLEMENTED(); -} diff --git a/Modules/Interfaces/UDI/physio/meta_intr.c b/Modules/Interfaces/UDI/physio/meta_intr.c deleted file mode 100644 index f4f50961..00000000 --- a/Modules/Interfaces/UDI/physio/meta_intr.c +++ /dev/null @@ -1,48 +0,0 @@ -/** - * \file physio/meta_intr.c - * \author John Hodge (thePowersGang) - */ -#include -#include -#include - -// === EXPORTS === -EXPORT(udi_intr_attach_req); -EXPORT(udi_intr_attach_ack); -EXPORT(udi_intr_attach_ack_unused); -EXPORT(udi_intr_detach_req); -EXPORT(udi_intr_detach_ack); -EXPORT(udi_intr_detach_ack_unused); -EXPORT(udi_intr_event_ind); - -// === CODE === -void udi_intr_attach_req(udi_intr_attach_cb_t *intr_attach_cb) -{ - UNIMPLEMENTED(); -} -void udi_intr_attach_ack(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status) -{ - UNIMPLEMENTED(); -} -void udi_intr_attach_ack_unused(udi_intr_attach_cb_t *intr_attach_cb, udi_status_t status) -{ - UNIMPLEMENTED(); -} - -void udi_intr_detach_req(udi_intr_detach_cb_t *intr_detach_cb) -{ - UNIMPLEMENTED(); -} -void udi_intr_detach_ack(udi_intr_detach_cb_t *intr_detach_cb) -{ - UNIMPLEMENTED(); -} -void udi_intr_detach_ack_unused(udi_intr_detach_cb_t *intr_detach_cb) -{ - UNIMPLEMENTED(); -} - -void udi_intr_event_ind(udi_intr_event_cb_t *intr_event_cb, udi_ubit8_t flags) -{ - UNIMPLEMENTED(); -} diff --git a/Modules/Interfaces/UDI/physio_main.c b/Modules/Interfaces/UDI/physio_main.c deleted file mode 100644 index f5e7aa04..00000000 --- a/Modules/Interfaces/UDI/physio_main.c +++ /dev/null @@ -1,16 +0,0 @@ -/** - * \file logging.c - * \author John Hodge (thePowersGang) - */ -#include -#include -#include - -// === CODE === -void udi_bus_bind_req(udi_bus_bind_cb_t *cb) -{ -} - -void udi_bus_unbind_req(udi_bus_bind_cb_t *cb) -{ -} diff --git a/Modules/Interfaces/UDI/strmem.c b/Modules/Interfaces/UDI/strmem.c deleted file mode 100644 index 53cc933f..00000000 --- a/Modules/Interfaces/UDI/strmem.c +++ /dev/null @@ -1,22 +0,0 @@ -/** - * \file strmem.c - * \author John Hodge (thePowersGang) - */ -#include -#include - -// === EXPORTS === -EXPORT(udi_snprintf); - -// === CODE === -udi_size_t udi_snprintf(char *s, udi_size_t max_bytes, const char *format, ...) -{ - udi_size_t ret; - va_list args; - va_start(args, format); - - ret = vsnprintf(s, max_bytes, format, args); - - va_end(args); - return ret; -} diff --git a/Modules/Libraries/SunRPC/proto.h b/Modules/Libraries/SunRPC/proto.h deleted file mode 100644 index 150fc426..00000000 --- a/Modules/Libraries/SunRPC/proto.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Acess2 Kernel - * - Sun RPC (RFC 1057) implementation - * proto.h - * - Protocol definition - */ -#ifndef _SUNRPC_PROTO_H_ -#define _SUNRPC_PROTO_H_ - -struct sRPC_CallBody -{ - Uint32 RPCVers; //!< Version (2) - Uint32 Program; //!< Program Identifier - Uint32 Version; //!< Program Version - -}; - -struct sRPC_Message -{ - tNet32 XID; //!< Transaction Identifier - union - { - struct sRPC_CallBody Call; - }; -}; - -#endif - diff --git a/Modules/Makefile.tpl b/Modules/Makefile.tpl deleted file mode 100644 index f1c31a57..00000000 --- a/Modules/Makefile.tpl +++ /dev/null @@ -1,75 +0,0 @@ - -# Acess2 Module/Driver Templater Makefile -# Makefile.tpl - -_CPPFLAGS := $(CPPFLAGS) - --include $(dir $(lastword $(MAKEFILE_LIST)))../Makefile.cfg - -LIBINCLUDES := $(addprefix -I$(ACESSDIR)/Modules/,$(DEPS)) -LIBINCLUDES := $(addsuffix /include,$(LIBINCLUDES)) - -CPPFLAGS := -I$(ACESSDIR)/Kernel/include -I$(ACESSDIR)/Kernel/arch/$(ARCHDIR)/include -CPPFLAGS += -DARCH=$(ARCH) -DARCH_is_$(ARCH) -DARCHDIR_is_$(ARCHDIR) -CPPFLAGS += $(_CPPFLAGS) -CPPFLAGS += $(LIBINCLUDES) -CFLAGS := -std=gnu99 -Wall -fno-stack-protector -g -O3 - -ifneq ($(CATEGORY),) - FULLNAME := $(CATEGORY)_$(NAME) -else - FULLNAME := $(NAME) -endif - -CPPFLAGS += -D_MODULE_NAME_=\"$(FULLNAME)\" - -ifneq ($(BUILDTYPE),static) - _SUFFIX := dyn_$(ARCH) - BIN := ../$(FULLNAME).kmd.$(ARCH) - CFLAGS += $(DYNMOD_CFLAGS) -fPIC -else - _SUFFIX := st_$(ARCH) - CFLAGS += $(KERNEL_CFLAGS) - BIN := ../$(NAME).xo.$(ARCH) -endif - -OBJ := $(addprefix obj-$(_SUFFIX)/,$(OBJ)) -#OBJ := $(addsuffix .$(_SUFFIX),$(OBJ)) - -DEPFILES := $(filter %.o,$(OBJ)) -DEPFILES := $(DEPFILES:%.o=%.d) - -.PHONY: all clean - -all: $(BIN) - -clean: - $(RM) $(BIN) $(BIN).dsm $(KOBJ) $(OBJ) $(DEPFILES) $(EXTRA) - $(RM) -r obj-$(_SUFFIX) - -install: $(BIN) -ifneq ($(BUILDTYPE),static) - @$(xMKDIR) $(DISTROOT)/Modules/$(ARCH); true - $(xCP) $(BIN) $(DISTROOT)/Modules/$(ARCH)/$(NAME).kmd -else -endif - - -ifneq ($(BUILDTYPE),static) -$(BIN): %.kmd.$(ARCH): $(OBJ) - @echo --- $(LD) -o $@ - @$(LD) --allow-shlib-undefined -shared -nostdlib -o $@ $(OBJ) -defsym=DriverInfo=_DriverInfo_$(FULLNAME) $(LDFLAGS) - @$(DISASM) $(BIN) > $(BIN).dsm -else -$(BIN): %.xo.$(ARCH): $(OBJ) - @echo --- $(LD) -o $@ - @$(LD) -r -o $@ $(OBJ) $(LDFLAGS) -endif - -obj-$(_SUFFIX)/%.o: %.c Makefile $(CFGFILES) - @echo --- $(CC) -o $@ - @mkdir -p $(dir $@) - @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< - @$(CC) -M $(CPPFLAGS) -MT $@ -o obj-$(_SUFFIX)/$*.d $< - --include $(DEPFILES) diff --git a/Modules/Network/Makefile.tpl b/Modules/Network/Makefile.tpl deleted file mode 100644 index d5c0b3cc..00000000 --- a/Modules/Network/Makefile.tpl +++ /dev/null @@ -1,3 +0,0 @@ -CATEGORY = Network - --include ../../Makefile.tpl diff --git a/Modules/Network/NE2000/Makefile b/Modules/Network/NE2000/Makefile deleted file mode 100644 index 7e740227..00000000 --- a/Modules/Network/NE2000/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = ne2000.o -NAME = NE2000 - --include ../Makefile.tpl diff --git a/Modules/Network/NE2000/ne2000.c b/Modules/Network/NE2000/ne2000.c deleted file mode 100644 index d05b2a33..00000000 --- a/Modules/Network/NE2000/ne2000.c +++ /dev/null @@ -1,543 +0,0 @@ -/* Acess2 - * NE2000 Driver - * - * See: ~/Sources/bochs/bochs.../iodev/ne2k.cc - */ -#define DEBUG 1 -#define VERSION ((0<<8)|50) -#include -#include -#include -#include -#include -#include - -// === CONSTANTS === -#define MEM_START 0x40 -#define MEM_END 0xC0 -#define RX_FIRST (MEM_START) -#define RX_LAST (MEM_START+RX_BUF_SIZE-1) -#define RX_BUF_SIZE 0x40 -#define TX_FIRST (MEM_START+RX_BUF_SIZE) -#define TX_LAST (MEM_END) -#define TX_BUF_SIZE 0x40 -#define MAX_PACKET_QUEUE 10 - -static const struct { - Uint16 Vendor; - Uint16 Device; -} csaCOMPAT_DEVICES[] = { - {0x10EC, 0x8029}, // Realtek 8029 - {0x10EC, 0x8129} // Realtek 8129 -}; -#define NUM_COMPAT_DEVICES ((int)(sizeof(csaCOMPAT_DEVICES)/sizeof(csaCOMPAT_DEVICES[0]))) - -enum eNe2k_Page0Read { - CMD = 0, //!< the master command register - CLDA0, //!< Current Local DMA Address 0 - CLDA1, //!< Current Local DMA Address 1 - BNRY, //!< Boundary Pointer (for ringbuffer) - TSR, //!< Transmit Status Register - NCR, //!< collisions counter - FIFO, //!< (for what purpose ??) - ISR, //!< Interrupt Status Register - CRDA0, //!< Current Remote DMA Address 0 - CRDA1, //!< Current Remote DMA Address 1 - RSR = 0xC //!< Receive Status Register -}; - -enum eNe2k_Page0Write { - PSTART = 1, //!< page start (init only) - PSTOP, //!< page stop (init only) - TPSR = 4, //!< transmit page start address - TBCR0, //!< transmit byte count (low) - TBCR1, //!< transmit byte count (high) - RSAR0 = 8, //!< remote start address (lo) - RSAR1, //!< remote start address (hi) - RBCR0, //!< remote byte count (lo) - RBCR1, //!< remote byte count (hi) - RCR, //!< receive config register - TCR, //!< transmit config register - DCR, //!< data config register (init) - IMR //!< interrupt mask register (init) -}; - -enum eNe2k_Page1Read { - CURR = 7 //!< current page -}; - -// === TYPES === -typedef struct sNe2k_Card { - Uint16 IOBase; //!< IO Port Address from PCI - Uint8 IRQ; //!< IRQ Assigned from PCI - - tSemaphore Semaphore; //!< Semaphore for incoming packets - int NextRXPage; //!< Next expected RX page - - int NextMemPage; //!< Next Card Memory page to use - - char Name[2]; // "0" - tVFS_Node Node; //!< VFS Node - Uint8 MacAddr[6]; //!< Cached MAC address -} tCard; - -// === PROTOTYPES === - int Ne2k_Install(char **Arguments); -char *Ne2k_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *Ne2k_FindDir(tVFS_Node *Node, const char *Name); - int Ne2k_IOCtl(tVFS_Node *Node, int ID, void *Data); -Uint64 Ne2k_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); -Uint64 Ne2k_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); - - int Ne2k_int_ReadDMA(tCard *Card, int FirstPage, int NumPages, void *Buffer); -Uint8 Ne2k_int_GetWritePage(tCard *Card, Uint16 Length); -void Ne2k_IRQHandler(int IntNum, void *Ptr); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, Ne2k, Ne2k_Install, NULL, NULL); -tVFS_NodeType gNe2K_RootNodeType = { - .ReadDir = Ne2k_ReadDir, - .FindDir = Ne2k_FindDir, - .IOCtl = Ne2k_IOCtl - }; -tVFS_NodeType gNe2K_DevNodeType = { - .Write = Ne2k_Write, - .Read = Ne2k_Read, - .IOCtl = Ne2k_IOCtl - }; -tDevFS_Driver gNe2k_DriverInfo = { - NULL, "ne2k", - { - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Flags = VFS_FFLAG_DIRECTORY, - .Type = &gNe2K_RootNodeType - } -}; -Uint16 gNe2k_BaseAddress; - int giNe2k_CardCount = 0; -tCard *gpNe2k_Cards = NULL; - -// === CODE === -/** - * \fn int Ne2k_Install(char **Options) - * \brief Installs the NE2000 Driver - */ -int Ne2k_Install(char **Options) -{ - int i, j, k; - int count, base; - tPCIDev id; - - // --- Scan PCI Bus --- - // Count Cards - giNe2k_CardCount = 0; - for( i = 0; i < NUM_COMPAT_DEVICES; i ++ ) - { - giNe2k_CardCount += PCI_CountDevices( csaCOMPAT_DEVICES[i].Vendor, csaCOMPAT_DEVICES[i].Device ); - } - - if( giNe2k_CardCount == 0 ) return MODULE_ERR_NOTNEEDED; - - // Enumerate Cards - k = 0; - gpNe2k_Cards = calloc( giNe2k_CardCount, sizeof(tCard) ); - - for( i = 0; i < NUM_COMPAT_DEVICES; i ++ ) - { - count = PCI_CountDevices( csaCOMPAT_DEVICES[i].Vendor, csaCOMPAT_DEVICES[i].Device ); - for( j = 0; j < count; j ++,k ++ ) - { - id = PCI_GetDevice( csaCOMPAT_DEVICES[i].Vendor, csaCOMPAT_DEVICES[i].Device, j ); - // Create Structure - base = PCI_GetBAR( id, 0 ); - gpNe2k_Cards[ k ].IOBase = base; - gpNe2k_Cards[ k ].IRQ = PCI_GetIRQ( id ); - gpNe2k_Cards[ k ].NextMemPage = 64; - gpNe2k_Cards[ k ].NextRXPage = RX_FIRST; - - // Install IRQ Handler - IRQ_AddHandler(gpNe2k_Cards[ k ].IRQ, Ne2k_IRQHandler, &gpNe2k_Cards[k]); - - // Reset Card - outb( base + 0x1F, inb(base + 0x1F) ); - while( (inb( base+ISR ) & 0x80) == 0 ); - outb( base + ISR, 0x80 ); - - // Initialise Card - outb( base + CMD, 0x40|0x21 ); // Page 1, No DMA, Stop - outb( base + CURR, RX_FIRST ); // Current RX page - outb( base + CMD, 0x21 ); // No DMA and Stop - outb( base + DCR, 0x49 ); // Set WORD mode - outb( base + IMR, 0x00 ); // Interrupt Mask Register - outb( base + ISR, 0xFF ); - outb( base + RCR, 0x20 ); // Reciever to Monitor - outb( base + TCR, 0x02 ); // Transmitter OFF (TCR.LB = 1, Internal Loopback) - - // Read MAC Address - outb( base + RBCR0, 6*4 ); // Remote Byte Count - outb( base + RBCR1, 0 ); - outb( base + RSAR0, 0 ); // Clear Source Address - outb( base + RSAR1, 0 ); - outb( base + CMD, 0x0A ); // Remote Read, Start - gpNe2k_Cards[ k ].MacAddr[0] = inb(base+0x10);// inb(base+0x10); - gpNe2k_Cards[ k ].MacAddr[1] = inb(base+0x10);// inb(base+0x10); - gpNe2k_Cards[ k ].MacAddr[2] = inb(base+0x10);// inb(base+0x10); - gpNe2k_Cards[ k ].MacAddr[3] = inb(base+0x10);// inb(base+0x10); - gpNe2k_Cards[ k ].MacAddr[4] = inb(base+0x10);// inb(base+0x10); - gpNe2k_Cards[ k ].MacAddr[5] = inb(base+0x10);// inb(base+0x10); - - outb( base+PSTART, RX_FIRST); // Set Receive Start - outb( base+BNRY, RX_LAST-1); // Set Boundary Page - outb( base+PSTOP, RX_LAST); // Set Stop Page - outb( base+ISR, 0xFF ); // Clear all ints - outb( base+CMD, 0x22 ); // No DMA, Start - outb( base+IMR, 0x3F ); // Set Interupt Mask - outb( base+RCR, 0x0F ); // Set WRAP and allow all packet matches - outb( base+TCR, 0x00 ); // Set Normal Transmitter mode - outb( base+TPSR, 0x40); // Set Transmit Start - - Log_Log("Ne2k", "Card %i 0x%04x IRQ%i %02x:%02x:%02x:%02x:%02x:%02x", - k, base, gpNe2k_Cards[ k ].IRQ, - gpNe2k_Cards[k].MacAddr[0], gpNe2k_Cards[k].MacAddr[1], - gpNe2k_Cards[k].MacAddr[2], gpNe2k_Cards[k].MacAddr[3], - gpNe2k_Cards[k].MacAddr[4], gpNe2k_Cards[k].MacAddr[5] - ); - - // Set VFS Node - gpNe2k_Cards[ k ].Name[0] = '0'+k; - gpNe2k_Cards[ k ].Name[1] = '\0'; - gpNe2k_Cards[ k ].Node.ImplPtr = &gpNe2k_Cards[ k ]; - gpNe2k_Cards[ k ].Node.NumACLs = 0; // Root Only - gpNe2k_Cards[ k ].Node.CTime = now(); - gpNe2k_Cards[ k ].Node.Type = &gNe2K_DevNodeType; - - // Initialise packet semaphore - // - Start at zero, no max - Semaphore_Init( &gpNe2k_Cards[k].Semaphore, 0, 0, "NE2000", gpNe2k_Cards[ k ].Name ); - } - } - - gNe2k_DriverInfo.RootNode.Size = giNe2k_CardCount; - DevFS_AddDevice( &gNe2k_DriverInfo ); - return MODULE_ERR_OK; -} - -/** - * \fn char *Ne2k_ReadDir(tVFS_Node *Node, int Pos) - */ -char *Ne2k_ReadDir(tVFS_Node *Node, int Pos) -{ - char ret[2]; - if(Pos < 0 || Pos >= giNe2k_CardCount) return NULL; - ret[0] = '0'+Pos; - ret[1] = '\0'; - return strdup(ret); -} - -/** - * \fn tVFS_Node *Ne2k_FindDir(tVFS_Node *Node, const char *Name) - */ -tVFS_Node *Ne2k_FindDir(tVFS_Node *Node, const char *Name) -{ - if(Name[0] == '\0' || Name[1] != '\0') return NULL; - - return &gpNe2k_Cards[ Name[0]-'0' ].Node; -} - -static const char *casIOCtls[] = { DRV_IOCTLNAMES, DRV_NETWORK_IOCTLNAMES, NULL }; -/** - * \fn int Ne2k_IOCtl(tVFS_Node *Node, int ID, void *Data) - * \brief IOCtl calls for a network device - */ -int Ne2k_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - ENTER("pNode iID pData", Node, ID, Data); - switch( ID ) - { - BASE_IOCTLS(DRV_TYPE_NETWORK, "NE2000", VERSION, casIOCtls); - } - - // If this is the root, return - if( Node == &gNe2k_DriverInfo.RootNode ) { - LEAVE('i', 0); - return 0; - } - - // Device specific settings - switch( ID ) - { - case NET_IOCTL_GETMAC: - if( !CheckMem(Data, 6) ) { - LEAVE('i', -1); - return -1; - } - memcpy( Data, ((tCard*)Node->ImplPtr)->MacAddr, 6 ); - LEAVE('i', 1); - return 1; - } - LEAVE('i', 0); - return 0; -} - -/** - * \fn Uint64 Ne2k_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) - * \brief Send a packet from the network card - */ -Uint64 Ne2k_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - tCard *Card = (tCard*)Node->ImplPtr; - const Uint16 *buf = Buffer; - int rem = Length; - int page; - - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - - // TODO: Lock - - // Sanity Check Length - if(Length > TX_BUF_SIZE*256) { - Log_Warning( - "Ne2k", - "Ne2k_Write - Attempting to send over TX_BUF_SIZE*256 (%i) bytes (%i)", - TX_BUF_SIZE*256, Length - ); - LEAVE('i', 0); - return 0; - } - - // Make sure that the card is in page 0 - outb(Card->IOBase + CMD, 0|0x22); // Page 0, Start, NoDMA - - // Clear Remote DMA Flag - outb(Card->IOBase + ISR, 0x40); // Bit 6 - - // Send Size - Transfer Byte Count Register - outb(Card->IOBase + TBCR0, Length & 0xFF); - outb(Card->IOBase + TBCR1, Length >> 8); - - // Send Size - Remote Byte Count Register - outb(Card->IOBase + RBCR0, Length & 0xFF); - outb(Card->IOBase + RBCR1, Length >> 8); - - // Set up transfer - outb(Card->IOBase + RSAR0, 0x00); // Page Offset - page = Ne2k_int_GetWritePage(Card, Length); - outb(Card->IOBase + RSAR1, page); // Page Offset - // Start - outb(Card->IOBase + CMD, 0|0x10|0x2); // Page 0, Remote Write, Start - - // Send Data - for(rem = Length; rem > 0; rem -= 2) { - outw(Card->IOBase + 0x10, *buf++); - } - - while( !(inb(Card->IOBase + ISR) & 0x40) ) // Wait for Remote DMA Complete - ; //Proc_Yield(); - - outb( Card->IOBase + ISR, 0x40 ); // ACK Interrupt - - // Send Packet - outb(Card->IOBase + TPSR, page); - outb(Card->IOBase + CMD, 0|0x10|0x4|0x2); - - // Complete DMA - //outb(Card->IOBase + CMD, 0|0x20); - - LEAVE('i', Length); - return Length; -} - -/** - * \fn Uint64 Ne2k_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Wait for and read a packet from the network card - */ -Uint64 Ne2k_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - tCard *Card = (tCard*)Node->ImplPtr; - Uint8 page; - Uint8 data[256]; - struct { - Uint8 Status; - Uint8 NextPacketPage; - Uint16 Length; // Little Endian - } *pktHdr; - - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - - // Wait for packets - if( Semaphore_Wait( &Card->Semaphore, 1 ) != 1 ) - { - // Error or interrupted - LEAVE_RET('i', 0); - } - - outb(Card->IOBase, 0x22 | (1 << 6)); // Page 6 - LOG("CURR : 0x%02x", inb(Card->IOBase + CURR)); - - // Get current read page - page = Card->NextRXPage; - LOG("page = %i", page); - - Ne2k_int_ReadDMA(Card, page, 1, data); - - pktHdr = (void*)data; - - LOG("pktHdr->Status = 0x%x", pktHdr->Status); - LOG("pktHdr->NextPacketPage = %i", pktHdr->NextPacketPage); - LOG("pktHdr->Length = 0x%03x", pktHdr->Length); - - // Have we read all the required bytes yet? - if(pktHdr->Length < 256 - 4) - { - if(Length > pktHdr->Length) - Length = pktHdr->Length; - memcpy(Buffer, &data[4], Length); - } - // No? oh damn, now we need to allocate a buffer - else - { - int pages = pktHdr->NextPacketPage - page; - char *buf = malloc( pages*256 ); - - LOG("pktHdr->Length (%i) > 256 - 4, allocated buffer %p", pktHdr->Length, buf); - - if(!buf) LEAVE_RET('i', -1); - - // Copy the already read data - memcpy(buf, data, 256); - - // Read all the needed pages - page ++; - if(page == RX_LAST+1) page = RX_FIRST; - - if( page + pages > RX_LAST ) - { - int first_count = RX_LAST+1 - page; - int tmp = 0; - tmp += Ne2k_int_ReadDMA(Card, page, first_count, buf+256); - tmp += Ne2k_int_ReadDMA(Card, RX_FIRST, pages-1-first_count, buf+(first_count+1)*256); - LOG("composite return count = %i", tmp); - } - else - Ne2k_int_ReadDMA(Card, page, pages-1, buf+256); - - // Wrap length to the packet length - if(Length > pktHdr->Length) - Length = pktHdr->Length; - else if( Length < pktHdr->Length ) { - Log_Warning("NE2000", "Packet truncated! (%i bytes truncated to %i)", - pktHdr->Length, Length); - } - memcpy(Buffer, &buf[4], Length); - - free(buf); - } - - // Write BNRY (maximum page for incoming packets) - if(pktHdr->NextPacketPage == RX_FIRST) - outb( Card->IOBase + BNRY, RX_LAST-1 ); - else - outb( Card->IOBase + BNRY, pktHdr->NextPacketPage-1 ); - // Set next RX Page and decrement the waiting list - Card->NextRXPage = pktHdr->NextPacketPage; - - LEAVE('i', Length); - return Length; -} - -int Ne2k_int_ReadDMA(tCard *Card, int FirstPage, int NumPages, void *Buffer) -{ - int i; - - // Sanity check - if( !(0 <= FirstPage && FirstPage < 256) ) { - Log_Warning("NE2000", "Ne2k_int_ReadDMA: BUG - FirstPage(%i) not 8-bit", FirstPage); - return -1; - } - if( !(0 <= NumPages && NumPages < 256) ) { - Log_Warning("NE2000", "Ne2k_int_ReadDMA: BUG - NumPages(%i) not 8-bit", NumPages); - return -1; - } - - ENTER("pCard iFirstPage iNumPages pBuffer", Card, FirstPage, NumPages, Buffer); - - // Make sure that the card is in bank 0 - outb(Card->IOBase + CMD, 0|0x22); // Bank 0, Start, NoDMA - outb(Card->IOBase + ISR, 0x40); // Clear Remote DMA Flag - - // Set up transfer - outb(Card->IOBase + RBCR0, 0); - outb(Card->IOBase + RBCR1, NumPages); // page count - outb(Card->IOBase + RSAR0, 0x00); // Page Offset - outb(Card->IOBase + RSAR1, FirstPage); // Page Number - outb(Card->IOBase + CMD, 0|0x08|0x2); // Bank 0, Remote Read, Start - - // TODO: Less expensive - //while( !(inb(Card->IOBase + ISR) & 0x40) ) { - // HALT(); - // LOG("inb(ISR) = 0x%02x", inb(Card->IOBase + ISR)); - //} - HALT(); // Small delay? - - // Read data - for(i = 0; i < 128*NumPages; i ++) - ((Uint16*)Buffer)[i] = inw(Card->IOBase + 0x10); - - - outb(Card->IOBase + ISR, 0x40); // Clear Remote DMA Flag - - LEAVE('i', NumPages); - return NumPages; -} - -/** - * \fn Uint8 Ne2k_int_GetWritePage(tCard *Card, Uint16 Length) - */ -Uint8 Ne2k_int_GetWritePage(tCard *Card, Uint16 Length) -{ - Uint8 ret = Card->NextMemPage; - - Card->NextMemPage += (Length + 0xFF) >> 8; - if(Card->NextMemPage >= TX_LAST) { - Card->NextMemPage -= TX_BUF_SIZE; - } - - return ret; -} - -/** - * \fn void Ne2k_IRQHandler(int IntNum) - */ -void Ne2k_IRQHandler(int IntNum, void *Ptr) -{ - Uint8 byte; - tCard *card = Ptr; - - if(card->IRQ != IntNum) return; - - byte = inb( card->IOBase + ISR ); - - LOG("byte = 0x%02x", byte); - - - // Reset All (save for RDMA), that's polled - outb( card->IOBase + ISR, 0xFF&(~0x40) ); - - // 0: Packet recieved (no error) - if( byte & 1 ) - { - //if( card->NumWaitingPackets > MAX_PACKET_QUEUE ) - // card->NumWaitingPackets = MAX_PACKET_QUEUE; - if( Semaphore_Signal( &card->Semaphore, 1 ) != 1 ) { - // Oops? - } - } - // 1: Packet sent (no error) - // 2: Recieved with error - // 3: Transmission Halted (Excessive Collisions) - // 4: Recieve Buffer Exhausted - // 5: - // 6: Remote DMA Complete - // 7: Reset -} diff --git a/Modules/Network/PCnet-FASTIII/Makefile b/Modules/Network/PCnet-FASTIII/Makefile deleted file mode 100644 index 1c7f3e94..00000000 --- a/Modules/Network/PCnet-FASTIII/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = rtl8139.o -NAME = RTL8139 - --include ../Makefile.tpl diff --git a/Modules/Network/PCnet-FASTIII/pcnet-fast3.c b/Modules/Network/PCnet-FASTIII/pcnet-fast3.c deleted file mode 100644 index bb4f83a8..00000000 --- a/Modules/Network/PCnet-FASTIII/pcnet-fast3.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Acess2 PCnet-FAST III Driver - * - By John Hodge (thePowersGang) - */ -#define DEBUG 0 -#define VERSION ((0<<8)|10) -#include -#include -#include -#include -#include -#include - -// === CONSTANTS === -#define VENDOR_ID 0x1022 -#define DEVICE_ID 0x2000 - -enum eRegs -{ - REG_APROM0 = 0x00, - REG_APROM4 = 0x04, - REG_APROM8 = 0x08, - REG_APROMC = 0x0C, - REG_RDP = 0x10, // 16 bit - REG_RAP = 0x14, // 8-bit - REG_RESET = 0x18, // 16-bit - REG_BDP = 0x1C, // 16-bit -}; - -enum eCSR_Regs -{ - CSR_STATUS, // CSR0 - Am79C973/Am79C975 Controller Status - CSR_IBA0, // CSR1 - Initialization Block Address[15:0] - CSR_IBA1, // CSR2 - Initialization Block Address[31:16] - CSR_INTMASK, // CSR3 - Interrupt Masks and Deferral Control - - CSR_MAC0 = 12, // CSR12 - Physical Address[15:0] - CSR_MAC1 = 13, // CSR13 - Physical Address[31:16] - CSR_MAC2 = 14, // CSR14 - Physical Address[47:32] - CSR_MODE = 15, // CSR15 - Mode - - CSR_RXBASE0 = 24, // CSR24 - Base Address of Receive Ring Lower - CSR_RXBASE1 = 25, // CSR25 - Base Address of Receive Ring Upper - CSR_TXBASE0 = 30, // CSR26 - Base Address of Transmit Ring Lower - CSR_TXBASE1 = 31, // CSR27 - Base Address of Transmit Ring Upper - - CSR_RXLENGTH = 76, // CSR76 - Receive Ring Length - CSR_TXLENGTH = 78, // CSR78 - Transmit Ring Length -}; - -enum eBCR_Regs -{ - BCR_PHYCS = 32, // BCR32 - Internal PHY Control and Status - BCR_PHYADDR, // BCR33 - Internal PHY Address - BCR_PHYMGMT, // BCR34 - Internal PHY Management Data -}; - -// === TYPES === -typedef struct sCard -{ - Uint16 IOBase; - Uint8 IRQ; - - int NumWaitingPackets; - - char Name[2]; - tVFS_Node Node; - Uint8 MacAddr[6]; -} tCard; - -// === PROTOTYPES === - int PCnet3_Install(char **Options); -char *PCnet3_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *PCnet3_FindDir(tVFS_Node *Node, const char *Filename); - int PCnet3_RootIOCtl(tVFS_Node *Node, int ID, void *Arg); -Uint64 PCnet3_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -Uint64 PCnet3_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); - int PCnet3_IOCtl(tVFS_Node *Node, int ID, void *Arg); -void PCnet3_IRQHandler(int Num); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, PCnet3, PCnet3_Install, NULL, NULL); -tDevFS_Driver gPCnet3_DriverInfo = { - NULL, "PCnet3", - { - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Flags = VFS_FFLAG_DIRECTORY, - .ReadDir = PCnet3_ReadDir, - .FindDir = PCnet3_FindDir, - .IOCtl = PCnet3_RootIOCtl - } -}; - int giPCnet3_CardCount; -tCard *gaPCnet3_Cards; - -// === CODE === -/** - * \brief Installs the PCnet3 Driver - */ -int PCnet3_Install(char **Options) -{ - int id = -1; - int i = 0; - Uint16 base; - tCard *card; - - giPCnet3_CardCount = PCI_CountDevices(VENDOR_ID, DEVICE_ID); - Log_Debug("PCnet3", "%i cards", giPCnet3_CardCount); - - if( giPCnet3_CardCount == 0 ) return MODULE_ERR_NOTNEEDED; - - gaPCnet3_Cards = calloc( giPCnet3_CardCount, sizeof(tCard) ); - - while( (id = PCI_GetDevice(VENDOR_ID, DEVICE_ID, i)) != -1 ) - { - card = &gaPCnet3_Cards[i]; - base = PCI_GetBAR( id, 0 ); - if( !(base & 1) ) { - Log_Warning("PCnet3", "Driver does not support MMIO, skipping card"); - card->IOBase = 0; - card->IRQ = 0; - continue ; - } - base &= ~1; - card->IOBase = base; - card->IRQ = PCI_GetIRQ( id ); - - // Install IRQ Handler - IRQ_AddHandler(card->IRQ, PCnet3_IRQHandler); - - - - Log_Log("PCnet3", "Card %i 0x%04x, IRQ %i %02x:%02x:%02x:%02x:%02x:%02x", - i, card->IOBase, card->IRQ, - card->MacAddr[0], card->MacAddr[1], card->MacAddr[2], - card->MacAddr[3], card->MacAddr[4], card->MacAddr[5] - ); - - i ++; - } - - gPCnet3_DriverInfo.RootNode.Size = giPCnet3_CardCount; - DevFS_AddDevice( &gPCnet3_DriverInfo ); - - return MODULE_ERR_OK; -} - -// --- Root Functions --- -char *PCnet3_ReadDir(tVFS_Node *Node, int Pos) -{ - if( Pos < 0 || Pos >= giPCnet3_CardCount ) return NULL; - - return strdup( gaPCnet3_Cards[Pos].Name ); -} - -tVFS_Node *PCnet3_FindDir(tVFS_Node *Node, const char *Filename) -{ - //TODO: It might be an idea to supprt >10 cards - if(Filename[0] == '\0' || Filename[1] != '\0') return NULL; - if(Filename[0] < '0' || Filename[0] > '9') return NULL; - return &gaPCnet3_Cards[ Filename[0]-'0' ].Node; -} - -const char *csaPCnet3_RootIOCtls[] = {DRV_IOCTLNAMES, NULL}; -int PCnet3_RootIOCtl(tVFS_Node *Node, int ID, void *Data) -{ - ENTER("pNode iID pData", Node, ID, Data); - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_NETWORK, "PCnet3", VERSION, csaPCnet3_RootIOCtls); - } - LEAVE('i', 0); - return 0; -} - -// --- File Functions --- -Uint64 PCnet3_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - tCard *card = Node->ImplPtr; - Uint16 read_ofs, pkt_length; - int new_read_ofs; - - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - -retry: - if( Semaphore_Wait( &card->ReadSemaphore, 1 ) != 1 ) - { - LEAVE_RET('i', 0); - } - - Mutex_Acquire( &card->ReadMutex ); - - read_ofs = inw( card->IOBase + CAPR ); - LOG("raw read_ofs = %i", read_ofs); - read_ofs = (read_ofs + 0x10) & 0xFFFF; - LOG("read_ofs = %i", read_ofs); - - pkt_length = *(Uint16*)&card->ReceiveBuffer[read_ofs+2]; - - // Calculate new read offset - new_read_ofs = read_ofs + pkt_length + 4; - new_read_ofs = (new_read_ofs + 3) & ~3; // Align - if(new_read_ofs > card->ReceiveBufferLength) { - LOG("wrapping read_ofs"); - new_read_ofs -= card->ReceiveBufferLength; - } - new_read_ofs -= 0x10; // I dunno - LOG("new_read_ofs = %i", new_read_ofs); - - // Check for errors - if( *(Uint16*)&card->ReceiveBuffer[read_ofs] & 0x1E ) { - // Update CAPR - outw(card->IOBase + CAPR, new_read_ofs); - Mutex_Release( &card->ReadMutex ); - goto retry; // I feel evil - } - - // Get packet - if( Length > pkt_length ) Length = pkt_length; - memcpy(Buffer, &card->ReceiveBuffer[read_ofs+4], Length); - - // Update CAPR - outw(card->IOBase + CAPR, new_read_ofs); - - Mutex_Release( &card->ReadMutex ); - - LEAVE('i', Length); - - return Length; -} - -Uint64 PCnet3_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - int td; - Uint32 status; - tCard *card = Node->ImplPtr; - - if( Length > 1500 ) return 0; // MTU exceeded - - ENTER("pNode XLength pBuffer", Node, Length, Buffer); - - // TODO: Implement a semaphore for avaliable transmit buffers - - // Find an avaliable descriptor - Mutex_Acquire(&card->CurTXProtector); - td = card->CurTXDescriptor; - card->CurTXDescriptor ++; - card->CurTXDescriptor %= 4; - Mutex_Release(&card->CurTXProtector); - // - Lock it - Mutex_Acquire( &card->TransmitInUse[td] ); - - LOG("td = %i", td); - - // Transmit using descriptor `td` - LOG("card->PhysTransmitBuffers[td] = %P", card->PhysTransmitBuffers[td]); - outd(card->IOBase + TSAD0 + td*4, card->PhysTransmitBuffers[td]); - LOG("card->TransmitBuffers[td] = %p", card->TransmitBuffers[td]); - // Copy to buffer - memcpy(card->TransmitBuffers[td], Buffer, Length); - // Start - status = 0; - status |= Length & 0x1FFF; // 0-12: Length - status |= 0 << 13; // 13: OWN bit - status |= (0 & 0x3F) << 16; // 16-21: Early TX threshold (zero atm, TODO: check) - LOG("status = 0x%08x", status); - outd(card->IOBase + TSD0 + td*4, status); - - LEAVE('i', (int)Length); - - return Length; -} - -const char *csaPCnet3_NodeIOCtls[] = {DRV_IOCTLNAMES, NULL}; -int PCnet3_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - tCard *card = Node->ImplPtr; - ENTER("pNode iID pData", Node, ID, Data); - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_NETWORK, "PCnet3", VERSION, csaPCnet3_NodeIOCtls); - case NET_IOCTL_GETMAC: - if( !CheckMem(Data, 6) ) { - LEAVE('i', -1); - return -1; - } - memcpy( Data, card->MacAddr, 6 ); - LEAVE('i', 1); - return 1; - } - LEAVE('i', 0); - return 0; -} - -void PCnet3_IRQHandler(int Num) -{ - int i, j; - tCard *card; - Uint16 status; - - LOG("Num = %i", Num); - - for( i = 0; i < giPCnet3_CardCount; i ++ ) - { - card = &gaPCnet3_Cards[i]; - if( Num != card->IRQ ) break; - - status = inw(card->IOBase + ISR); - LOG("status = 0x%02x", status); - - // Transmit OK, a transmit descriptor is now free - if( status & FLAG_ISR_TOK ) - { - for( j = 0; j < 4; j ++ ) - { - if( ind(card->IOBase + TSD0 + j*4) & 0x8000 ) { // TSD TOK - Mutex_Release( &card->TransmitInUse[j] ); - // TODO: Update semaphore once implemented - } - } - outw(card->IOBase + ISR, FLAG_ISR_TOK); - } - - // Recieve OK, inform read - if( status & FLAG_ISR_ROK ) - { - int read_ofs, end_ofs; - int packet_count = 0; - int len; - - // Scan recieve buffer for packets - end_ofs = inw(card->IOBase + CBA); - read_ofs = card->SeenOfs; - LOG("read_ofs = %i, end_ofs = %i", read_ofs, end_ofs); - if( read_ofs > end_ofs ) - { - while( read_ofs < card->ReceiveBufferLength ) - { - packet_count ++; - len = *(Uint16*)&card->ReceiveBuffer[read_ofs+2]; - LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x", - packet_count, read_ofs, - *(Uint16*)&card->ReceiveBuffer[read_ofs], - len - ); - if(len > 2000) { - Log_Warning("PCnet3", "IRQ: Packet in buffer exceeds sanity (%i>2000)", len); - } - read_ofs += len + 4; - read_ofs = (read_ofs + 3) & ~3; // Align - } - read_ofs -= card->ReceiveBufferLength; - LOG("wrapped read_ofs"); - } - while( read_ofs < end_ofs ) - { - packet_count ++; - LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x", - packet_count, read_ofs, - *(Uint16*)&card->ReceiveBuffer[read_ofs], - *(Uint16*)&card->ReceiveBuffer[read_ofs+2] - ); - read_ofs += *(Uint16*)&card->ReceiveBuffer[read_ofs+2] + 4; - read_ofs = (read_ofs + 3) & ~3; // Align - } - if( read_ofs != end_ofs ) { - Log_Warning("PCnet3", "IRQ: read_ofs (%i) != end_ofs(%i)", read_ofs, end_ofs); - read_ofs = end_ofs; - } - card->SeenOfs = read_ofs; - - LOG("packet_count = %i, read_ofs = 0x%x", packet_count, read_ofs); - - if( packet_count ) - { - if( Semaphore_Signal( &card->ReadSemaphore, packet_count ) != packet_count ) { - // Oops? - } - VFS_MarkAvaliable( &card->Node, 1 ); - } - - outw(card->IOBase + ISR, FLAG_ISR_ROK); - } - } -} diff --git a/Modules/Network/RTL8139/Makefile b/Modules/Network/RTL8139/Makefile deleted file mode 100644 index 1c7f3e94..00000000 --- a/Modules/Network/RTL8139/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = rtl8139.o -NAME = RTL8139 - --include ../Makefile.tpl diff --git a/Modules/Network/RTL8139/rtl8139.c b/Modules/Network/RTL8139/rtl8139.c deleted file mode 100644 index 1e9ac899..00000000 --- a/Modules/Network/RTL8139/rtl8139.c +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Acess2 RTL8139 Driver - * - By John Hodge (thePowersGang) - */ -#define DEBUG 0 -#define VERSION ((0<<8)|20) -#include -#include -#include -#include -#include -#include - -// === CONSTANTS === -#define VENDOR_ID 0x10EC -#define DEVICE_ID 0x8139 - -enum eRTL8139_Regs -{ - // MAC Address - MAC0, MAC1, MAC2, - MAC3, MAC4, MAC5, - - // Multicast Registers - MAR0 = 0x08, MAR1, MAR2, MAR3, - MAR4, MAR5, MAR6, MAR7, - - // Transmit status of descriptors 0 - 3 - TSD0 = 0x10, TSD1 = 0x14, - TSD2 = 0x18, TSD3 = 0x1C, - // Transmit start addresses - TSAD0 = 0x20, TSAD1 = 0x24, - TSAD2 = 0x28, TSAD3 = 0x2C, - - RBSTART = 0x30, //!< Recieve Buffer Start (DWord) - // Early Recieve Byte Count - ERBCR = 0x34, // 16-bits - // Early RX Status Register - ERSR = 0x36, - - // ??, ??, ??, RST, RE, TE, ??, ?? - CMD = 0x37, - - CAPR = 0x38, // Current address of packet read - CBA = 0x3A, // Current Buffer Address - Total byte count in RX buffer - - IMR = 0x3C, // Interrupt mask register - ISR = 0x3E, // Interrupt status register - - TCR = 0x40, // Transmit Configuration Register - RCR = 0x44, // Recieve Configuration Register - TCTR = 0x48, // 32-bit timer (count) - MPC = 0x4C, // Missed packet count (due to RX overflow) - - CR_9346 = 0x50, - CONFIG0 = 0x51, - CONFIG1 = 0x52, - // 0x53 resvd - TIMERINT = 0x54, // Fires a timeout when TCTR equals this value - -}; - -#define FLAG_ISR_TOK 0x04 -#define FLAG_ISR_ROK 0x01 - -// === TYPES === -typedef struct sCard -{ - Uint16 IOBase; - Uint8 IRQ; - - int NumWaitingPackets; - - char *ReceiveBuffer; - tPAddr PhysReceiveBuffer; - int ReceiveBufferLength; - int SeenOfs; //!< End of the most recently seen packet (by IRQ) - tMutex ReadMutex; - tSemaphore ReadSemaphore; - - char *TransmitBuffers[4]; - tPAddr PhysTransmitBuffers[4]; - tMutex TransmitInUse[4]; - tMutex CurTXProtector; //!< Protects \a .CurTXDescriptor - int CurTXDescriptor; - - char Name[2]; - tVFS_Node Node; - Uint8 MacAddr[6]; -} tCard; - -// === PROTOTYPES === - int RTL8139_Install(char **Options); -char *RTL8139_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *RTL8139_FindDir(tVFS_Node *Node, const char *Filename); - int RTL8139_RootIOCtl(tVFS_Node *Node, int ID, void *Arg); -Uint64 RTL8139_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -Uint64 RTL8139_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); - int RTL8139_IOCtl(tVFS_Node *Node, int ID, void *Arg); -void RTL8139_IRQHandler(int Num, void *Ptr); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, RTL8139, RTL8139_Install, NULL, NULL); -tVFS_NodeType gRTL8139_RootNodeType = { - .ReadDir = RTL8139_ReadDir, - .FindDir = RTL8139_FindDir, - .IOCtl = RTL8139_IOCtl - }; -tVFS_NodeType gRTL8139_DevNodeType = { - .Write = RTL8139_Write, - .Read = RTL8139_Read, - .IOCtl = RTL8139_IOCtl - }; -tDevFS_Driver gRTL8139_DriverInfo = { - NULL, "RTL8139", - { - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Flags = VFS_FFLAG_DIRECTORY, - .Type = &gRTL8139_RootNodeType - } -}; - int giRTL8139_CardCount; -tCard *gaRTL8139_Cards; - -// === CODE === -/** - * \brief Installs the RTL8139 Driver - */ -int RTL8139_Install(char **Options) -{ - int id = -1; - int i = 0; - Uint16 base; - tCard *card; - - giRTL8139_CardCount = PCI_CountDevices(VENDOR_ID, DEVICE_ID); - - if( giRTL8139_CardCount == 0 ) return MODULE_ERR_NOTNEEDED; - - Log_Debug("RTL8139", "%i cards", giRTL8139_CardCount); - gaRTL8139_Cards = calloc( giRTL8139_CardCount, sizeof(tCard) ); - - for( i = 0 ; (id = PCI_GetDevice(VENDOR_ID, DEVICE_ID, i)) != -1; i ++ ) - { - card = &gaRTL8139_Cards[i]; - base = PCI_GetBAR( id, 0 ); - if( !(base & 1) ) { - Log_Warning("RTL8139", "Driver does not support MMIO, skipping card (addr %x)", - base); - card->IOBase = 0; - card->IRQ = 0; - continue ; - } - base &= ~1; - card->IOBase = base; - card->IRQ = PCI_GetIRQ( id ); - - // Install IRQ Handler - IRQ_AddHandler(card->IRQ, RTL8139_IRQHandler, card); - - // Power on - outb( base + CONFIG1, 0x00 ); - - // Reset (0x10 to CMD) - outb( base + CMD, 0x10 ); - while( inb(base + CMD) & 0x10 ) ; - - // Set up recieve buffer - // - Allocate 3 pages below 4GiB for the recieve buffer (Allows 8k+16+1500) - card->ReceiveBuffer = (void*)MM_AllocDMA( 3, 32, &card->PhysReceiveBuffer ); - card->ReceiveBufferLength = 8*1024; - outd(base + RBSTART, (Uint32)card->PhysReceiveBuffer); - outd(base + CBA, 0); - outd(base + CAPR, 0); - // Set IMR to Transmit OK and Receive OK - outw(base + IMR, 0x5); - - // Set up transmit buffers - // - 2 non-contiguous pages (each page can fit 2 1500 byte packets) - card->TransmitBuffers[0] = (void*)MM_AllocDMA( 1, 32, &card->PhysTransmitBuffers[0] ); - card->TransmitBuffers[1] = card->TransmitBuffers[0] + 0x800; - card->PhysTransmitBuffers[1] = card->PhysTransmitBuffers[0] + 0x800; - - card->TransmitBuffers[2] = (void*)MM_AllocDMA( 1, 32, &card->PhysTransmitBuffers[2] ); - card->TransmitBuffers[3] = card->TransmitBuffers[2] + 0x800; - card->PhysTransmitBuffers[3] = card->PhysTransmitBuffers[2] + 0x800; - - outd(base + TSAD0, card->PhysTransmitBuffers[0]); - outd(base + TSAD1, card->PhysTransmitBuffers[1]); - outd(base + TSAD2, card->PhysTransmitBuffers[2]); - outd(base + TSAD3, card->PhysTransmitBuffers[3]); - - // Set recieve buffer size and recieve mask - // - Bit 7 being set tells the card to overflow the recieve buffer if needed - // (i.e. when the packet starts at the end of the bufffer, it overflows up - // to 1500 bytes) - outd(base + RCR, 0x8F); - - // Recive Enable and Transmit Enable - outb(base + CMD, 0x0C); - - // Get the card's MAC address - card->MacAddr[0] = inb(base+MAC0); - card->MacAddr[1] = inb(base+MAC1); - card->MacAddr[2] = inb(base+MAC2); - card->MacAddr[3] = inb(base+MAC3); - card->MacAddr[4] = inb(base+MAC4); - card->MacAddr[5] = inb(base+MAC5); - - // Set VFS Node - card->Name[0] = '0'+i; - card->Name[1] = '\0'; - card->Node.ImplPtr = card; - card->Node.NumACLs = 0; - card->Node.CTime = now(); - card->Node.Type = &gRTL8139_DevNodeType; - - Log_Log("RTL8139", "Card %i 0x%04x, IRQ %i %02x:%02x:%02x:%02x:%02x:%02x", - i, card->IOBase, card->IRQ, - card->MacAddr[0], card->MacAddr[1], card->MacAddr[2], - card->MacAddr[3], card->MacAddr[4], card->MacAddr[5] - ); - } - - gRTL8139_DriverInfo.RootNode.Size = giRTL8139_CardCount; - DevFS_AddDevice( &gRTL8139_DriverInfo ); - - return MODULE_ERR_OK; -} - -// --- Root Functions --- -char *RTL8139_ReadDir(tVFS_Node *Node, int Pos) -{ - if( Pos < 0 || Pos >= giRTL8139_CardCount ) return NULL; - - return strdup( gaRTL8139_Cards[Pos].Name ); -} - -tVFS_Node *RTL8139_FindDir(tVFS_Node *Node, const char *Filename) -{ - //TODO: It might be an idea to supprt >10 cards - if(Filename[0] == '\0' || Filename[1] != '\0') return NULL; - if(Filename[0] < '0' || Filename[0] > '9') return NULL; - return &gaRTL8139_Cards[ Filename[0]-'0' ].Node; -} - -const char *csaRTL8139_RootIOCtls[] = {DRV_IOCTLNAMES, NULL}; -int RTL8139_RootIOCtl(tVFS_Node *Node, int ID, void *Data) -{ - ENTER("pNode iID pData", Node, ID, Data); - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_NETWORK, "RTL8139", VERSION, csaRTL8139_RootIOCtls); - } - LEAVE('i', 0); - return 0; -} - -// --- File Functions --- -Uint64 RTL8139_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - tCard *card = Node->ImplPtr; - Uint16 read_ofs, pkt_length; - int new_read_ofs; - - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - -retry: - if( Semaphore_Wait( &card->ReadSemaphore, 1 ) != 1 ) - { - LEAVE_RET('i', 0); - } - - Mutex_Acquire( &card->ReadMutex ); - - read_ofs = inw( card->IOBase + CAPR ); - LOG("raw read_ofs = %i", read_ofs); - read_ofs = (read_ofs + 0x10) & 0xFFFF; - LOG("read_ofs = %i", read_ofs); - - pkt_length = *(Uint16*)&card->ReceiveBuffer[read_ofs+2]; - - // Calculate new read offset - new_read_ofs = read_ofs + pkt_length + 4; - new_read_ofs = (new_read_ofs + 3) & ~3; // Align - if(new_read_ofs > card->ReceiveBufferLength) { - LOG("wrapping read_ofs"); - new_read_ofs -= card->ReceiveBufferLength; - } - new_read_ofs -= 0x10; // I dunno - LOG("new_read_ofs = %i", new_read_ofs); - - // Check for errors - if( *(Uint16*)&card->ReceiveBuffer[read_ofs] & 0x1E ) { - // Update CAPR - outw(card->IOBase + CAPR, new_read_ofs); - Mutex_Release( &card->ReadMutex ); - goto retry; // I feel evil - } - - // Get packet - if( Length > pkt_length ) Length = pkt_length; - memcpy(Buffer, &card->ReceiveBuffer[read_ofs+4], Length); - - // Update CAPR - outw(card->IOBase + CAPR, new_read_ofs); - - Mutex_Release( &card->ReadMutex ); - - LEAVE('i', Length); - - return Length; -} - -Uint64 RTL8139_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - int td; - Uint32 status; - tCard *card = Node->ImplPtr; - - if( Length > 1500 ) return 0; // MTU exceeded - - ENTER("pNode XLength pBuffer", Node, Length, Buffer); - - // TODO: Implement a semaphore for avaliable transmit buffers - - // Find an avaliable descriptor - Mutex_Acquire(&card->CurTXProtector); - td = card->CurTXDescriptor; - card->CurTXDescriptor ++; - card->CurTXDescriptor %= 4; - Mutex_Release(&card->CurTXProtector); - // - Lock it - Mutex_Acquire( &card->TransmitInUse[td] ); - - LOG("td = %i", td); - - // Transmit using descriptor `td` - LOG("card->PhysTransmitBuffers[td] = %P", card->PhysTransmitBuffers[td]); - outd(card->IOBase + TSAD0 + td*4, card->PhysTransmitBuffers[td]); - LOG("card->TransmitBuffers[td] = %p", card->TransmitBuffers[td]); - // Copy to buffer - memcpy(card->TransmitBuffers[td], Buffer, Length); - // Start - status = 0; - status |= Length & 0x1FFF; // 0-12: Length - status |= 0 << 13; // 13: OWN bit - status |= (0 & 0x3F) << 16; // 16-21: Early TX threshold (zero atm, TODO: check) - LOG("status = 0x%08x", status); - outd(card->IOBase + TSD0 + td*4, status); - - LEAVE('i', (int)Length); - - return Length; -} - -const char *csaRTL8139_NodeIOCtls[] = {DRV_IOCTLNAMES, NULL}; -int RTL8139_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - tCard *card = Node->ImplPtr; - ENTER("pNode iID pData", Node, ID, Data); - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_NETWORK, "RTL8139", VERSION, csaRTL8139_NodeIOCtls); - case NET_IOCTL_GETMAC: - if( !CheckMem(Data, 6) ) { - LEAVE('i', -1); - return -1; - } - memcpy( Data, card->MacAddr, 6 ); - LEAVE('i', 1); - return 1; - } - LEAVE('i', 0); - return 0; -} - -void RTL8139_IRQHandler(int Num, void *Ptr) -{ - int j; - tCard *card = Ptr; - Uint16 status; - - LOG("Num = %i", Num); - - if( Num != card->IRQ ) return; - - status = inw(card->IOBase + ISR); - LOG("status = 0x%02x", status); - - // Transmit OK, a transmit descriptor is now free - if( status & FLAG_ISR_TOK ) - { - for( j = 0; j < 4; j ++ ) - { - if( ind(card->IOBase + TSD0 + j*4) & 0x8000 ) { // TSD TOK - Mutex_Release( &card->TransmitInUse[j] ); - // TODO: Update semaphore once implemented - } - } - outw(card->IOBase + ISR, FLAG_ISR_TOK); - } - - // Recieve OK, inform read - if( status & FLAG_ISR_ROK ) - { - int read_ofs, end_ofs; - int packet_count = 0; - int len; - - // Scan recieve buffer for packets - end_ofs = inw(card->IOBase + CBA); - read_ofs = card->SeenOfs; - LOG("read_ofs = %i, end_ofs = %i", read_ofs, end_ofs); - if( read_ofs > end_ofs ) - { - while( read_ofs < card->ReceiveBufferLength ) - { - packet_count ++; - len = *(Uint16*)&card->ReceiveBuffer[read_ofs+2]; - LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x", - packet_count, read_ofs, - *(Uint16*)&card->ReceiveBuffer[read_ofs], - len - ); - if(len > 2000) { - Log_Warning("RTL8139", "IRQ: Packet in buffer exceeds sanity (%i>2000)", len); - } - read_ofs += len + 4; - read_ofs = (read_ofs + 3) & ~3; // Align - } - read_ofs -= card->ReceiveBufferLength; - LOG("wrapped read_ofs"); - } - while( read_ofs < end_ofs ) - { - packet_count ++; - LOG("%i 0x%x Pkt Hdr: 0x%04x, len: 0x%04x", - packet_count, read_ofs, - *(Uint16*)&card->ReceiveBuffer[read_ofs], - *(Uint16*)&card->ReceiveBuffer[read_ofs+2] - ); - read_ofs += *(Uint16*)&card->ReceiveBuffer[read_ofs+2] + 4; - read_ofs = (read_ofs + 3) & ~3; // Align - } - if( read_ofs != end_ofs ) { - Log_Warning("RTL8139", "IRQ: read_ofs (%i) != end_ofs(%i)", read_ofs, end_ofs); - read_ofs = end_ofs; - } - card->SeenOfs = read_ofs; - - LOG("packet_count = %i, read_ofs = 0x%x", packet_count, read_ofs); - - if( packet_count ) - { - if( Semaphore_Signal( &card->ReadSemaphore, packet_count ) != packet_count ) { - // Oops? - } - VFS_MarkAvaliable( &card->Node, 1 ); - } - - outw(card->IOBase + ISR, FLAG_ISR_ROK); - } -} diff --git a/Modules/Sound/SoundBlaster16/Makefile b/Modules/Sound/SoundBlaster16/Makefile deleted file mode 100644 index 9384859c..00000000 --- a/Modules/Sound/SoundBlaster16/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = main.o -NAME = SoundBlaster16 - --include ../Makefile.tpl diff --git a/Modules/Sound/SoundBlaster16/main.c b/Modules/Sound/SoundBlaster16/main.c deleted file mode 100644 index f2d056c6..00000000 --- a/Modules/Sound/SoundBlaster16/main.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Acess2 SoundBlaster16 Driver - */ -#define DEBUG 0 -#include -#include -#include -#include -#include -#include -#include - -#define INT - -// === TYPES === -typedef struct sSB16 -{ - Uint16 Base; -} tSB16; - -// === CONSTANTS === -enum { - SB16_PORT_RESET = 0x6, - SB16_PORT_READ = 0xA, - SB16_PORT_WRITE = 0xC, - SB16_PORT_AVAIL = 0xE -}; -#define SB16_BASE_PORT 0x200 -enum { - SB16_CMD_RAW8 = 0x10, // 8-bit DAC value follows - SB16_CMD_DMAFREQ = 0x40, // followed by TIME_CONSTANT = 256 - 1000000 / frequency - SB16_CMD_DMASTOP = 0xD0, - SB16_CMD_SPKRON = 0xD1, - SB16_CMD_SPKROFF = 0xD3, - SB16_CMD_DMACONT = 0xD4, - - // DMA Types (uses channel 1) - // - Followed by 16-bit length (well, length - 1) - SB16_CMD_DMA_8BIT = 0x14, -}; - - -// === PROTOTYPES === -// Driver - int SB16_Install(char **Arguments); -void SB16_Uninstall(); -// Filesystem -Uint64 SB16_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); - int SB16_IOCtl(tVFS_Node *Node, int ID, void *Data); - -// === GLOBALS === -MODULE_DEFINE(0, 0x0032, SoundBlaster16, SB16_Install, SB16_Uninstall, "PCI", NULL); -tDevFS_Driver gBGA_DriverStruct = { - NULL, "SoundBlaster16", - { - .Write = SB16_Write, - .IOCtl = SB16_IOCtl - } -}; - -// === CODE === -int SB16_Install(char **Arguments) -{ - int jumper_port_setting = 1; // 1-6 incl - - // Reset - outb(card->Base+SB16_PORT_RESET, 1); - // - Wait 3us - outb(card->Base+SB16_PORT_RESET, 0); - - SB16_ReadDSP(card); - - return MODULE_ERR_OK; -} - -void SB16_Uninstall() -{ -} - -/** - * \fn Uint64 SB16_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Write to the framebuffer - */ -Uint64 SB16_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - return 0; -} - -/** - * \fn int SB16_IOCtl(tVFS_Node *Node, int ID, void *Data) - * \brief Handle messages to the device - */ -int SB16_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - return -1; -} - -// -int SB16_WriteDSP(tSB16 *Card, Uint8 Value) -{ - // Wait for the card to be ready - while( inb(Card->Base+SB16_PORT_WRITE) & 0x80 ) - ; - - outb(Card->Base+SB16_PORT_WRITE, Value); - - return 0; -} - -Uint8 SB16_ReadDSP(tSB16 *Card) -{ - // Wait for bit 7 of AVAIL - while( !(inb(card->Base+SB16_PORT_AVAIL) & 0x80) ) - ; - return inb(card->Base+SB16_PORT_READ); -} diff --git a/Modules/Sound/SoundBlaster16/sbdsp.txt b/Modules/Sound/SoundBlaster16/sbdsp.txt deleted file mode 100644 index 94364cd3..00000000 --- a/Modules/Sound/SoundBlaster16/sbdsp.txt +++ /dev/null @@ -1,442 +0,0 @@ - - ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ - ³ Programming the SoundBlaster DSP ³ - ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - - Written for the PC-GPE by Mark Feldman - e-mail address : u914097@student.canberra.edu.au - myndale@cairo.anu.edu.au - - ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ - ³ THIS FILE MAY NOT BE DISTRIBUTED ³ - ³ SEPARATE TO THE ENTIRE PC-GPE COLLECTION. ³ - ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ Disclaimer ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ - -I assume no responsibility whatsoever for any effect that this file, the -information contained therein or the use thereof has on you, your sanity, -computer, spouse, children, pets or anything else related to you or your -existance. No warranty is provided nor implied with this information. - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ Introduction ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -The SoundBlaster is capable of both FM and digitised sounds. The FM wave -is fully Adlib compatible, so check the ADLIB.TXT file for info -on how to program it. This file will concentrate on recording and playback -of digital samples through the SoundBlaster CT-DSP 1321 chip. - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ The SoundBlaster DSP I/O Ports ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -The DSP (Digital Sound Processor) chip is programmed through 4 ports which -are determined by the SoundBlaster base address jumper setting: - - RESET 2x6h - - READ DATA 2xAh - -WRITE COMMAND/DATA output -WRITE BUFFER STATUS input 2xCh - - - DATA AVAILABLE 2xEh - -where x = 1 for base address jumper setting 210h - x = 2 for base address jumper setting 220h - . - . - x = 6 for base address jumper setting 260h - - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ Resetting the DSP ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -You have to reset the DSP before you program it. This is done with the -following procedure : - -1) Write a 1 to the SoundBlaster RESET port (2x6h) -2) Wait for 3 micro-seconds -3) Write a 0 to the SoundBlaster RESET port (2x6h) -4) Read the byte from the DATA AVAILABLE (2xEh) port until bit 7 = 1 -5) Poll for a ready byte (AAh) from the READ DATA port (2xAh). Before - reading the READ DATA port it is avdvisable. - -The DSP usually takes somewhere around 100 micro-seconds to reset itself. -If it fails to do within a reasonable time (say 200 micro-seconds) then -an error has occurred, possibly an incorrect I/O address is being used. - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ Writing to the DSP ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -A value can be written to the DSP with the following procedure : - -1) Read the DSP's WRITE BUFFER STATUS port (2xCh) until bit 7 = 0 -2) Write the value to the WRITE COMMAND/DATA port (2xCh) - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ Reading the DSP ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -A value can be read from the DSP with the following procedure : - -1) Read the DSP's DATA AVAILABLE port (2xEh) until bit 7 = 1 -2) Read the data from the READ DATA port (2xAh) - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ Turning the speaker on and controlling DMA ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -Speaker and DMA control are handled by writing one of the following bytes -to the DSP: - - ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ - ³ Value Description ³ - ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ - ³ D0h DMA Stop ³ - ³ D1h Turn speaker on ³ - ³ D3h Turn speaker off ³ - ³ D4h DMA Continue ³ - ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -DMA is discussed below. The DMA commands shown here can be used to pause -the sample during DMA playback playback. - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ Writing to the DAC ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -The DAC (Digital to Analog Converter) is the part of the card which converts -a sample number (ie 0 -> 255) to a sound level. To generate a square sound -wave at maximum volume (for example) you could alternate writing 0's and -255's to the DAC. - -Programming the DAC in direct mode involves the main program setting the -DAC to a desired value. Only 8 bit DAC is available in direct mode. To set -the DAC level you write the value 10h to the DSP followed by the sample -number (0 -> 255). Note that no sound will be heard unless the speaker has -been turned on. In direct mode the main program is responsible for the -timing between samples, the DAC can output sound samples as fast as the -calling program can change it. Typically the timer interrupt is reprogrammed -and used to generate the timing required for a sample playback. Info on -programming the PIT chip can be found in the PIT.TXT file. - -The DAC can also be programmed to accept values sent to it via the DMA -chip. Draeden has written an excellent article on programming the DMA chip -(see DMA_VLA.TXT) so only a brief example of it's use will be given here. -The important thing to remember is that the DMA chip cannot transfer data -which crosses between page breaks. If the data does cross page breaks then -it will have to be split up into several transfers, with one page per -transfer. - -Setting the playback frequency for the DMA transfer is done by writing -the value 40h to the DSP followed by TIME_CONSTANT, where -TIME_CONSTANT = 256 - 1000000 / frequency - -There are several types of DMA transfers available. The following table -lists them: - - ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ - ³DMA_TYPE_VALUE Description Frequency Range ³ - ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ - ³ 14h 8 bit 4KHz -> 23 KHz ³ - ³ 74h 4 bit ADPCM 4KHz -> 12 KHz ³ - ³ 75h 4 bit ADPCM with 4KHz -> 12 KHz ³ - ³ reference byte ³ - ³ 76h 2.6 bit ADPCM 4KHz -> 13 KHz ³ - ³ 77h 2.6 bit ADPCM with 4KHz -> 13 KHz ³ - ³ reference byte ³ - ³ 16h 2 bit ADPCM 4KHz -> 11 KHz ³ - ³ 17h 2 bit ADPCM with 4KHz -> 11 KHz ³ - ³ reference byte ³ - ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -ADPCM stands for Adaptive Pulse Code Modulation, a sound compression -technique where the difference between successive samples is stored rather -than their actual values. In the modes with reference bytes, the first -byte is the actual starting value. Having modes with and without reference -bytes means you can output successive blocks without the need for a -reference byte at the start of each one. - -The procedure for doing a DMA transfer is as follows: - -1) Load the sound data into memory -2) Set up the DMA chip for the tranfer -3) Set the DSP TIME_CONSTANT to the sampling rate -4) Write DMA_TYPE_VALUE value to the DSP -5) Write DATA_LENGTH to the DSP (2 bytes, LSB first) where - DATA_LENGTH = number of bytes to send - 1 - -Note that the DMA chip must be programmed before the BSP. - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ Reading from the ADC ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -Reading samples from the ADC (Analog to Digital Converter) can also be -done in either direct or DMA mode. - -To read a sample in direct mode write the value 20h to the DSP and then -read the value from the DSP. Simple as that! - -To set up the DSP for a DMA transfer, follow this procedure : - -1) Get a memory buffer ready to hold the sample -2) Set up the DMA chip for the transfer -3) Set the DSP TIME_CONSTANT to the sampling rate -4) Write the value 24h to the DSP -5) Write DATA_LENGTH to the DSP (2 bytes, LSB first) where - DATA_LENGTH = number of bytes to read - 1 - -Note that the DMA chip must be programmed before the BSP. - -DMA reads only support 8 bit mode, compressed modes are done by software and -stored in the voc file. I haven't tried to figure out how the compression is -done. If someone does figure it out I'd like to know about it! - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ Programming the DMA Chip ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -As mentioned before, Draeden has written a very good article on the dma -chip, but here is a brief run down on what you would need to do to program -the DMA channel 1 for the DSP in real mode: - -1) Calculate the 20 bit address of the memory buffer you are using - where Base Address = Segment * 16 + Offset - eg 1234h:5678h = 179B8h -2) Send the value 05h to port 0Ah (mask off channel 1) -3) Send the value 00h to port 0Ch (clear the internal DMA flip/flop) -4) Send the value 49h to port 0Bh (for playback) or - 45h to port 0Bh (for recording) -5) Write the LSB (bits 0 -> 7) of the 20 bit memory address to port 02h -6) Write the MSB (bits 8 -> 15) of the 20 bit memory address to ort 02h -7) Write the Page (bits 16 -> 19) of the 20 bit memory address to port 83h -8) Send the LSB of DATA_LENGTH to port 03h -9) Send the MSB of DATA_LENGTH to port 03h -10) Send the value 01h to port 0Ah (enable channel 1) - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ End of DMA Interrupt ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -When a DMA transfer is complete an interrupt is generated. The actual -interrupt number depends on the SoundBlaster card's IRQ jumper setting: - - ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ - ³ IRQ Jumper ³ - ³ Setting Interrupt ³ - ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ - ³ 2 0Ah ³ - ³ 3 0Bh ³ - ³ 5 0Dh ³ - ³ 7 0Fh ³ - ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -To service one of these interrupts you must perform these 3 tasks: - -1) Acknowledge the DSP interrupt by reading the DATA AVAILABLE port (2xEh) - once. -2) If there are more blocks to transfer then set them up -3) Output value 20h (EOI) to the interrupt controller port 20h - -Of course, as with any hardware interrupt you must also leave the -state of the system (registers etc..) the way it was when the interrupt -was called. - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ A Simple DSP Pascal Unit ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -{ - - DSP.PAS - A demo SoundBlaster DSP unit for real mode - - By Mark Feldman -} - -Unit DSP; - -Interface - -{ ResetDSP returns true if reset was successful - base should be 1 for base address 210h, 2 for 220h etc... } -function ResetDSP(base : word) : boolean; - -{ Write DAC sets the speaker output level } -procedure WriteDAC(level : byte); - -{ ReadDAC reads the microphone input level } -function ReadDAC : byte; - -{ SpeakerOn connects the DAC to the speaker } -function SpeakerOn: byte; - -{ SpeakerOff disconnects the DAC from the speaker, - but does not affect the DAC operation } -function SpeakerOff: byte; - -{ Functions to pause DMA playback } -procedure DMAStop; -procedure DMAContinue; - -{ Playback plays a sample of a given size back at a given frequency using - DMA channel 1. The sample must not cross a page boundry } -procedure Playback(sound : Pointer; size : word; frequency : word); - -Implementation - -Uses Crt; - -var DSP_RESET : word; - DSP_READ_DATA : word; - DSP_WRITE_DATA : word; - DSP_WRITE_STATUS : word; - DSP_DATA_AVAIL : word; - -function ResetDSP(base : word) : boolean; -begin - - base := base * $10; - - { Calculate the port addresses } - DSP_RESET := base + $206; - DSP_READ_DATA := base + $20A; - DSP_WRITE_DATA := base + $20C; - DSP_WRITE_STATUS := base + $20C; - DSP_DATA_AVAIL := base + $20E; - - { Reset the DSP, and give some nice long delays just to be safe } - Port[DSP_RESET] := 1; - Delay(10); - Port[DSP_RESET] := 0; - Delay(10); - if (Port[DSP_DATA_AVAIL] And $80 = $80) And - (Port[DSP_READ_DATA] = $AA) then - ResetDSP := true - else - ResetDSP := false; -end; - -procedure WriteDSP(value : byte); -begin - while Port[DSP_WRITE_STATUS] And $80 <> 0 do; - Port[DSP_WRITE_DATA] := value; -end; - -function ReadDSP : byte; -begin - while Port[DSP_DATA_AVAIL] and $80 = 0 do; - ReadDSP := Port[DSP_READ_DATA]; -end; - -procedure WriteDAC(level : byte); -begin - WriteDSP($10); - WriteDSP(level); -end; - -function ReadDAC : byte; -begin - WriteDSP($20); - ReadDAC := ReadDSP; -end; - -function SpeakerOn: byte; -begin - WriteDSP($D1); -end; - -function SpeakerOff: byte; -begin - WriteDSP($D3); -end; - -procedure DMAContinue; -begin - WriteDSP($D4); -end; - -procedure DMAStop; -begin - WriteDSP($D0); -end; - -procedure Playback(sound : Pointer; size : word; frequency : word); -var time_constant : word; - page, offset : word; -begin - - SpeakerOn; - - size := size - 1; - - { Set up the DMA chip } - offset := Seg(sound^) Shl 4 + Ofs(sound^); - page := (Seg(sound^) + Ofs(sound^) shr 4) shr 12; - Port[$0A] := 5; - Port[$0C] := 0; - Port[$0B] := $49; - Port[$02] := Lo(offset); - Port[$02] := Hi(offset); - Port[$83] := page; - Port[$03] := Lo(size); - Port[$03] := Hi(size); - Port[$0A] := 1; - - { Set the playback frequency } - time_constant := 256 - 1000000 div frequency; - WriteDSP($40); - WriteDSP(time_constant); - - { Set the playback type (8-bit) } - WriteDSP($14); - WriteDSP(Lo(size)); - WriteDSP(Hi(size)); -end; - -end. - - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ References ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ - -Title : The SoundBlaster Developpers Kit -Publishers : Creative Labs Inc - Creative Technology PTE LTD - -Title : Sound Blaster - The Official Book -Authors : Richard Heimlich, David M. Golden, Ivan Luk, Peter M. Ridge -Publishers : Osborne/McGraw Hill -ISBN : 0-07-881907-5 - -Some of the information in this file was either obtained from or verified -by the source code in a public domain library called SOUNDX by Peter -Sprenger. I haven't tried using his library yet (I don't have a C compiler -at the moment) but it looks very well done and contains numerous sound card -detection routines. Says Peter : "It would be nice, that when you make -something commercial with my routines, that you send me a copy of your -project or send me some bucks, just enough for pizza and coke to support my -night programming sessions. If you send me nothing, ok. But USE the stuff, -if you can need it!". Heh...a REAL programmer! - -ftpsite: ftp.uwp.edu -directory: /pub/msdos/demos/programming/game-dev/source -filename: soundx.zip - -ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ -³ Sound Familiar? ³ -ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ - -What the...why is there a faint glimmer of sunlight outside? HOLY $#!^!! It's -5:30am! I'm goin' to bed! - diff --git a/Modules/Storage/ATA/Makefile b/Modules/Storage/ATA/Makefile deleted file mode 100644 index 22923422..00000000 --- a/Modules/Storage/ATA/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = main.o io.o mbr.o -NAME = ATA - --include ../Makefile.tpl diff --git a/Modules/Storage/ATA/common.h b/Modules/Storage/ATA/common.h deleted file mode 100644 index 891d2bbe..00000000 --- a/Modules/Storage/ATA/common.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Acess2 IDE Harddisk Driver - * - main.c - */ -#ifndef _COMMON_H_ -#define _COMMON_H_ - -#include -#include - -// === CONSTANTS === -#define MAX_ATA_DISKS 4 -#define SECTOR_SIZE 512 -#define ATA_TIMEOUT 2000 // 2s timeout -// Needed out of io.c because it's the max for Read/WriteDMA -#define MAX_DMA_SECTORS (0x1000 / SECTOR_SIZE) - -// === STRUCTURES === -typedef struct -{ - Uint8 BootCode[0x1BE]; - struct { - Uint8 Boot; - Uint8 Unused1; // Also CHS Start - Uint16 StartHi; // Also CHS Start - Uint8 SystemID; - Uint8 Unused2; // Also CHS Length - Uint16 LengthHi; // Also CHS Length - Uint32 LBAStart; - Uint32 LBALength; - } __attribute__ ((packed)) Parts[4]; - Uint16 BootFlag; // = 0xAA 55 -} __attribute__ ((packed)) tMBR; - -typedef struct -{ - Uint64 Start; - Uint64 Length; - char Name[4]; - tVFS_Node Node; -} tATA_Partition; - -typedef struct -{ - Uint64 Sectors; - char Name[2]; - tVFS_Node Node; - int NumPartitions; - tATA_Partition *Partitions; -} tATA_Disk; - -// === GLOBALS === -extern tATA_Disk gATA_Disks[]; - -// === FUNCTIONS === -// --- Common --- -extern void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length); - -// --- MBR Parsing --- -extern void ATA_ParseMBR(int Disk, tMBR *MBR); - -// --- IO Functions --- -extern int ATA_SetupIO(void); -extern Uint64 ATA_GetDiskSize(int Disk); -extern int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer); -extern int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer); - -#endif diff --git a/Modules/Storage/ATA/io.c b/Modules/Storage/ATA/io.c deleted file mode 100644 index b8a15d01..00000000 --- a/Modules/Storage/ATA/io.c +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Acess2 IDE Harddisk Driver - * - io.c - * - * Disk Input/Output control - */ -#define DEBUG 0 -#include -#include // Needed for error codes -#include -#include "common.h" - -// === MACROS === -#define IO_DELAY() do{inb(0x80); inb(0x80); inb(0x80); inb(0x80);}while(0) - -// === Constants === -#define IDE_PRI_BASE 0x1F0 -#define IDE_PRI_CTRL 0x3F6 -#define IDE_SEC_BASE 0x170 -#define IDE_SEC_CTRL 0x376 - -#define IDE_PRDT_LAST 0x8000 -/** - \enum HddControls - \brief Commands to be sent to HDD_CMD -*/ -enum HddControls { - HDD_PIO_R28 = 0x20, - HDD_PIO_R48 = 0x24, - HDD_DMA_R48 = 0x25, - HDD_PIO_W28 = 0x30, - HDD_PIO_W48 = 0x34, - HDD_DMA_W48 = 0x35, - HDD_DMA_R28 = 0xC8, - HDD_DMA_W28 = 0xCA, - HDD_IDENTIFY = 0xEC -}; - -// === TYPES === -/** - * \brief PRDT Entry - */ -typedef struct -{ - Uint32 PBufAddr; // Physical Buffer Address - Uint16 Bytes; // Size of transfer entry - Uint16 Flags; // Flags -} __attribute__ ((packed)) tPRDT_Ent; - -/** - * \brief Structure returned by the ATA IDENTIFY command - */ -typedef struct -{ - Uint16 Flags; // 1 - Uint16 Usused1[9]; // 10 - char SerialNum[20]; // 20 - Uint16 Usused2[3]; // 23 - char FirmwareVer[8]; // 27 - char ModelNumber[40]; // 47 - Uint16 SectPerInt; // 48 - Low byte only - Uint16 Unused3; // 49 - Uint16 Capabilities[2]; // 51 - Uint16 Unused4[2]; // 53 - Uint16 ValidExtData; // 54 - Uint16 Unused5[5]; // 59 - Uint16 SizeOfRWMultiple; // 60 - Uint32 Sectors28; // LBA 28 Sector Count - Uint16 Unused6[100-62]; - Uint64 Sectors48; // LBA 48 Sector Count - Uint16 Unused7[256-104]; -} __attribute__ ((packed)) tIdentify; - -// === PROTOTYPES === - int ATA_SetupIO(void); -Uint64 ATA_GetDiskSize(int Disk); -Uint16 ATA_GetBasePort(int Disk); -// Read/Write DMA - int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer); - int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer); -// IRQs -void ATA_IRQHandlerPri(int UNUSED(IRQ), void *UNUSED(Ptr)); -void ATA_IRQHandlerSec(int UNUSED(IRQ), void *UNUSED(Ptr)); -// Controller IO -Uint8 ATA_int_BusMasterReadByte(int Ofs); -Uint32 ATA_int_BusMasterReadDWord(int Ofs); -void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value); -void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value); - -// === GLOBALS === -// - BusMaster IO Addresses -Uint32 gATA_BusMasterBase; //!< True Address (IO/MMIO) -Uint8 *gATA_BusMasterBasePtr; //!< Paging Mapped MMIO (If needed) -// - IRQs - int gATA_IRQPri = 14; - int gATA_IRQSec = 15; -volatile int gaATA_IRQs[2] = {0}; -// - Locks to avoid tripping -tMutex glaATA_ControllerLock[2]; -// - Buffers! -Uint8 gATA_Buffers[2][(MAX_DMA_SECTORS+0xFFF)&~0xFFF] __attribute__ ((section(".padata"))); -// - PRDTs -tPRDT_Ent gATA_PRDTs[2] = { - {0, 512, IDE_PRDT_LAST}, - {0, 512, IDE_PRDT_LAST} -}; -tPAddr gaATA_PRDT_PAddrs[2]; - -// === CODE === -/** - * \brief Sets up the ATA controller's DMA mode - */ -int ATA_SetupIO(void) -{ - int ent; - - ENTER(""); - - // Get IDE Controller's PCI Entry - ent = PCI_GetDeviceByClass(0x010100, 0xFFFF00, -1); - LOG("ent = %i", ent); - gATA_BusMasterBase = PCI_GetBAR(ent, 4); - if( gATA_BusMasterBase == 0 ) { - Log_Warning("ATA", "It seems that there is no Bus Master Controller on this machine. Get one"); - // TODO: Use PIO mode instead - LEAVE('i', MODULE_ERR_NOTNEEDED); - return MODULE_ERR_NOTNEEDED; - } - - LOG("BAR5 = 0x%x", PCI_GetBAR(ent, 5)); - LOG("IRQ = %i", PCI_GetIRQ(ent)); - - // Map memory - if( !(gATA_BusMasterBase & 1) ) - { - if( gATA_BusMasterBase < 0x100000 ) - gATA_BusMasterBasePtr = (void*)(KERNEL_BASE | (tVAddr)gATA_BusMasterBase); - else - gATA_BusMasterBasePtr = (void*)( MM_MapHWPages( gATA_BusMasterBase, 1 ) + (gATA_BusMasterBase&0xFFF) ); - LOG("gATA_BusMasterBasePtr = %p", gATA_BusMasterBasePtr); - } - else { - // Bit 0 is left set as a flag to other functions - LOG("gATA_BusMasterBase = IO 0x%x", gATA_BusMasterBase & ~1); - } - - // Register IRQs and get Buffers - IRQ_AddHandler( gATA_IRQPri, ATA_IRQHandlerPri, NULL ); - IRQ_AddHandler( gATA_IRQSec, ATA_IRQHandlerSec, NULL ); - - gATA_PRDTs[0].PBufAddr = MM_GetPhysAddr( (tVAddr)&gATA_Buffers[0] ); - gATA_PRDTs[1].PBufAddr = MM_GetPhysAddr( (tVAddr)&gATA_Buffers[1] ); - - LOG("gATA_PRDTs = {PBufAddr: 0x%x, PBufAddr: 0x%x}", gATA_PRDTs[0].PBufAddr, gATA_PRDTs[1].PBufAddr); - - gaATA_PRDT_PAddrs[0] = MM_GetPhysAddr( (tVAddr)&gATA_PRDTs[0] ); - LOG("gaATA_PRDT_PAddrs[0] = 0x%x", gaATA_PRDT_PAddrs[0]); - ATA_int_BusMasterWriteDWord(4, gaATA_PRDT_PAddrs[0]); - - gaATA_PRDT_PAddrs[1] = MM_GetPhysAddr( (tVAddr)&gATA_PRDTs[1] ); - LOG("gaATA_PRDT_PAddrs[1] = 0x%x", gaATA_PRDT_PAddrs[1]); - ATA_int_BusMasterWriteDWord(12, gaATA_PRDT_PAddrs[1]); - - // Enable controllers - outb(IDE_PRI_BASE+1, 1); - outb(IDE_SEC_BASE+1, 1); - outb(IDE_PRI_CTRL, 0); - outb(IDE_SEC_CTRL, 0); - - // Make sure interrupts are ACKed - ATA_int_BusMasterWriteByte(2, 0x4); - ATA_int_BusMasterWriteByte(10, 0x4); - - // return - LEAVE('i', MODULE_ERR_OK); - return MODULE_ERR_OK; -} - -/** - * \brief Get the size (in sectors) of a disk - * \param Disk Disk to get size of - * \return Number of sectors reported - * - * Does an ATA IDENTIFY - */ -Uint64 ATA_GetDiskSize(int Disk) -{ - union { - Uint16 buf[256]; - tIdentify identify; - } data; - Uint16 base; - Uint8 val; - int i; - ENTER("iDisk", Disk); - - base = ATA_GetBasePort( Disk ); - - // Send Disk Selector - if(Disk & 1) // Slave - outb(base+6, 0xB0); - else // Master - outb(base+6, 0xA0); - IO_DELAY(); - - // Check for a floating bus - if( 0xFF == inb(base+7) ) { - LOG("Floating bus"); - LEAVE('i', 0); - return 0; - } - - // Check for the controller - // - Write to two RW ports and attempt to read back - outb(base+0x02, 0x66); - outb(base+0x03, 0xFF); - if(inb(base+0x02) != 0x66 || inb(base+0x03) != 0xFF) { - LOG("No controller"); - LEAVE('i', 0); - return 0; - } - - // Send ATA IDENTIFY - outb(base+7, HDD_IDENTIFY); - IO_DELAY(); - val = inb(base+7); // Read status - LOG("val = 0x%02x", val); - if(val == 0) { - LEAVE('i', 0); - return 0; // Disk does not exist - } - - // Poll until BSY clears or ERR is set - // TODO: Timeout? - while( (val & 0x80) && !(val & 1) ) - val = inb(base+7); - LOG("BSY unset (0x%x)", val); - // and, wait for DRQ to set - while( !(val & 0x08) && !(val & 1)) - val = inb(base+7); - LOG("DRQ set (0x%x)", val); - - // Check for an error - if(val & 1) { - LEAVE('i', 0); - return 0; // Error occured, so return false - } - - // Read Data - for( i = 0; i < 256; i++ ) - data.buf[i] = inw(base); - - // Return the disk size - if(data.identify.Sectors48 != 0) { - LEAVE('X', data.identify.Sectors48); - return data.identify.Sectors48; - } - else { - LEAVE('x', data.identify.Sectors28); - return data.identify.Sectors28; - } -} - -/** - * \fn Uint16 ATA_GetPortBase(int Disk) - * \brief Returns the base port for a given disk - */ -Uint16 ATA_GetBasePort(int Disk) -{ - switch(Disk) - { - case 0: case 1: return IDE_PRI_BASE; - case 2: case 3: return IDE_SEC_BASE; - } - return 0; -} - -/** - * \fn int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer) - * \return Boolean Failure - */ -int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer) -{ - int cont = (Disk>>1)&1; // Controller ID - int disk = Disk & 1; - Uint16 base; - Sint64 timeoutTime; - - ENTER("iDisk XAddress iCount pBuffer", Disk, Address, Count, Buffer); - - // Check if the count is small enough - if(Count > MAX_DMA_SECTORS) { - Log_Warning("ATA", "Passed too many sectors for a bulk DMA read (%i > %i)", - Count, MAX_DMA_SECTORS); - LEAVE('i'); - return 1; - } - - // Hack to make debug hexdump noticable - #if 1 - memset(Buffer, 0xFF, Count*SECTOR_SIZE); - #endif - - // Get exclusive access to the disk controller - Mutex_Acquire( &glaATA_ControllerLock[ cont ] ); - - // Set Size - gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE; - - // Get Port Base - base = ATA_GetBasePort(Disk); - - // Reset IRQ Flag - gaATA_IRQs[cont] = 0; - - #if 1 - if( cont == 0 ) { - outb(IDE_PRI_CTRL, 4); - IO_DELAY(); - outb(IDE_PRI_CTRL, 0); - } - else { - outb(IDE_SEC_CTRL, 4); - IO_DELAY(); - outb(IDE_SEC_CTRL, 0); - } - #endif - - // Set up transfer - if( Address > 0x0FFFFFFF ) // Use LBA48 - { - outb(base+0x6, 0x40 | (disk << 4)); - IO_DELAY(); - outb(base+0x2, 0 >> 8); // Upper Sector Count - outb(base+0x3, Address >> 24); // Low 2 Addr - outb(base+0x4, Address >> 28); // Mid 2 Addr - outb(base+0x5, Address >> 32); // High 2 Addr - } - else - { - // Magic, Disk, High Address nibble - outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F)); - //outb(base+0x06, 0xA0 | (disk << 4) | ((Address >> 24) & 0x0F)); - IO_DELAY(); - } - - //outb(base+0x01, 0x01); //? - outb(base+0x02, Count & 0xFF); // Sector Count - outb(base+0x03, Address & 0xFF); // Low Addr - outb(base+0x04, (Address >> 8) & 0xFF); // Middle Addr - outb(base+0x05, (Address >> 16) & 0xFF); // High Addr - - LOG("Starting Transfer"); - - // HACK: Ensure the PRDT is reset - ATA_int_BusMasterWriteDWord(cont*8+4, gaATA_PRDT_PAddrs[cont]); - ATA_int_BusMasterWriteByte(cont*8, 4); // Reset IRQ - - LOG("gATA_PRDTs[%i].Bytes = %i", cont, gATA_PRDTs[cont].Bytes); - if( Address > 0x0FFFFFFF ) - outb(base+0x07, HDD_DMA_R48); // Read Command (LBA48) - else - outb(base+0x07, HDD_DMA_R28); // Read Command (LBA28) - - // Start transfer - ATA_int_BusMasterWriteByte( cont * 8, 9 ); // Read and start - - // Wait for transfer to complete - timeoutTime = now() + ATA_TIMEOUT; - while( gaATA_IRQs[cont] == 0 && now() < timeoutTime) - { - HALT(); - } - - // Complete Transfer - ATA_int_BusMasterWriteByte( cont * 8, 8 ); // Read and stop - - #if DEBUG - { - Uint8 val = inb(base+0x7); - LOG("Status byte = 0x%02x, Controller Status = 0x%02x", - val, ATA_int_BusMasterReadByte(cont * 8 + 2)); - } - #else - inb(base+0x7); - #endif - - if( gaATA_IRQs[cont] == 0 ) - { - if( ATA_int_BusMasterReadByte(cont * 8 + 2) & 0x4 ) { - Log_Error("ATA", "BM Status reports an interrupt, but none recieved"); - ATA_int_BusMasterWriteByte(cont*8 + 2, 4); // Clear interrupt - memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE ); - Mutex_Release( &glaATA_ControllerLock[ cont ] ); - LEAVE('i', 0); - return 0; - } - - #if 1 - Debug_HexDump("ATA", Buffer, 512); - #endif - - // Release controller lock - Mutex_Release( &glaATA_ControllerLock[ cont ] ); - Log_Warning("ATA", - "Read timeout on disk %i (Reading sector 0x%llx)", - Disk, Address); - // Return error - LEAVE('i', 1); - return 1; - } - else { - LOG("Transfer Completed & Acknowledged"); - // Copy to destination buffer - memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE ); - // Release controller lock - Mutex_Release( &glaATA_ControllerLock[ cont ] ); - - LEAVE('i', 0); - return 0; - } -} - -/** - * \fn int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer) - * \brief Write up to \a MAX_DMA_SECTORS to a disk - * \param Disk Disk ID to write to - * \param Address LBA of first sector - * \param Count Number of sectors to write (must be >= \a MAX_DMA_SECTORS) - * \param Buffer Source buffer for data - * \return Boolean Failure - */ -int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, const void *Buffer) -{ - int cont = (Disk>>1)&1; // Controller ID - int disk = Disk & 1; - Uint16 base; - Sint64 timeoutTime; - - // Check if the count is small enough - if(Count > MAX_DMA_SECTORS) return 1; - - // Get exclusive access to the disk controller - Mutex_Acquire( &glaATA_ControllerLock[ cont ] ); - - // Set Size - gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE; - - // Get Port Base - base = ATA_GetBasePort(Disk); - - // Reset IRQ Flag - gaATA_IRQs[cont] = 0; - - // Set up transfer - outb(base+0x01, 0x00); - if( Address > 0x0FFFFFFF ) // Use LBA48 - { - outb(base+0x6, 0x40 | (disk << 4)); - outb(base+0x2, 0 >> 8); // Upper Sector Count - outb(base+0x3, Address >> 24); // Low 2 Addr - outb(base+0x3, Address >> 28); // Mid 2 Addr - outb(base+0x3, Address >> 32); // High 2 Addr - } - else - { - // Magic, Disk, High Address nibble - outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F)); - } - - outb(base+0x02, (Uint8) Count); // Sector Count - outb(base+0x03, (Uint8) Address); // Low Addr - outb(base+0x04, (Uint8) (Address >> 8)); // Middle Addr - outb(base+0x05, (Uint8) (Address >> 16)); // High Addr - if( Address > 0x0FFFFFFF ) - outb(base+0x07, HDD_DMA_W48); // Write Command (LBA48) - else - outb(base+0x07, HDD_DMA_W28); // Write Command (LBA28) - - // Copy to output buffer - memcpy( gATA_Buffers[cont], Buffer, Count*SECTOR_SIZE ); - - // Start transfer - ATA_int_BusMasterWriteByte( cont << 3, 1 ); // Write and start - - // Wait for transfer to complete - timeoutTime = now() + ATA_TIMEOUT; - while( gaATA_IRQs[cont] == 0 && now() < timeoutTime) - { - HALT(); - } - - // Complete Transfer - ATA_int_BusMasterWriteByte( cont << 3, 0 ); // Write and stop - - // If the IRQ is unset, return error - if( gaATA_IRQs[cont] == 0 ) { - // Release controller lock - Mutex_Release( &glaATA_ControllerLock[ cont ] ); - return 1; // Error - } - else { - Mutex_Release( &glaATA_ControllerLock[ cont ] ); - return 0; - } -} - -/** - * \brief Primary ATA Channel IRQ handler - */ -void ATA_IRQHandlerPri(int UNUSED(IRQ), void *UNUSED(Ptr)) -{ - Uint8 val; - - // IRQ bit set for Primary Controller - val = ATA_int_BusMasterReadByte( 0x2 ); - LOG("IRQ val = 0x%x", val); - if(val & 4) { - LOG("IRQ hit (val = 0x%x)", val); - ATA_int_BusMasterWriteByte( 0x2, 4 ); - gaATA_IRQs[0] = 1; - return ; - } -} - -/** - * \brief Second ATA Channel IRQ handler - */ -void ATA_IRQHandlerSec(int UNUSED(IRQ), void *UNUSED(Ptr)) -{ - Uint8 val; - // IRQ bit set for Secondary Controller - val = ATA_int_BusMasterReadByte( 0xA ); - LOG("IRQ val = 0x%x", val); - if(val & 4) { - LOG("IRQ hit (val = 0x%x)", val); - ATA_int_BusMasterWriteByte( 0xA, 4 ); - gaATA_IRQs[1] = 1; - return ; - } -} - -/** - * \brief Read an 8-bit value from a Bus Master register - * \param Ofs Register offset - */ -Uint8 ATA_int_BusMasterReadByte(int Ofs) -{ - if( gATA_BusMasterBase & 1 ) - return inb( (gATA_BusMasterBase & ~1) + Ofs ); - else - return *(Uint8*)(gATA_BusMasterBasePtr + Ofs); -} - -/** - * \brief Read an 32-bit value from a Bus Master register - * \param Ofs Register offset - */ -Uint32 ATA_int_BusMasterReadDWord(int Ofs) -{ - if( gATA_BusMasterBase & 1 ) - return ind( (gATA_BusMasterBase & ~1) + Ofs ); - else - return *(Uint32*)(gATA_BusMasterBasePtr + Ofs); -} - -/** - * \brief Writes a byte to a Bus Master Register - * \param Ofs Register Offset - * \param Value Value to write - */ -void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value) -{ - if( gATA_BusMasterBase & 1 ) - outb( (gATA_BusMasterBase & ~1) + Ofs, Value ); - else - *(Uint8*)(gATA_BusMasterBasePtr + Ofs) = Value; -} - -/** - * \brief Writes a 32-bit value to a Bus Master Register - * \param Ofs Register offset - * \param Value Value to write - */ -void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value) -{ - if( gATA_BusMasterBase & 1 ) - outd( (gATA_BusMasterBase & ~1) + Ofs, Value ); - else - *(Uint32*)(gATA_BusMasterBasePtr + Ofs) = Value; -} diff --git a/Modules/Storage/ATA/main.c b/Modules/Storage/ATA/main.c deleted file mode 100644 index 41228d88..00000000 --- a/Modules/Storage/ATA/main.c +++ /dev/null @@ -1,457 +0,0 @@ -/* - * Acess2 IDE Harddisk Driver - * - main.c - */ -#define DEBUG 0 -#define VERSION 0x0032 -#include -#include -#include -#include -#include -#include -#include "common.h" - -// === MACROS === -#define IO_DELAY() do{inb(0x80); inb(0x80); inb(0x80); inb(0x80);}while(0) - -// === PROTOTYPES === - int ATA_Install(char **Arguments); -void ATA_SetupPartitions(void); -void ATA_SetupVFS(void); - int ATA_ScanDisk(int Disk); -void ATA_ParseGPT(int Disk); -void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length); -Uint16 ATA_GetBasePort(int Disk); -// Filesystem Interface -char *ATA_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *ATA_FindDir(tVFS_Node *Node, const char *Name); -Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); -Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); - int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data); -// Read/Write Interface/Quantiser -Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk); -Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, i386ATA, ATA_Install, NULL, "PCI", NULL); -tVFS_NodeType gATA_RootNodeType = { - .TypeName = "ATA Root Node", - .ReadDir = ATA_ReadDir, - .FindDir = ATA_FindDir - }; -tVFS_NodeType gATA_DiskNodeType = { - .TypeName = "ATA Volume", - .Read = ATA_ReadFS, - .Write = ATA_WriteFS, - .IOCtl = ATA_IOCtl - }; -tDevFS_Driver gATA_DriverInfo = { - NULL, "ata", - { - .NumACLs = 1, - .Size = -1, - .Flags = VFS_FFLAG_DIRECTORY, - .ACLs = &gVFS_ACL_EveryoneRX, - .Type = &gATA_RootNodeType - } -}; -tATA_Disk gATA_Disks[MAX_ATA_DISKS]; - int giATA_NumNodes; -tVFS_Node **gATA_Nodes; - -// === CODE === -/** - * \brief Initialise the ATA driver - */ -int ATA_Install(char **Arguments) -{ - int ret; - - ret = ATA_SetupIO(); - if(ret) return ret; - - ATA_SetupPartitions(); - - ATA_SetupVFS(); - - if( DevFS_AddDevice( &gATA_DriverInfo ) == 0 ) - return MODULE_ERR_MISC; - - return MODULE_ERR_OK; -} - -/** - * \brief Scan all disks, looking for partitions - */ -void ATA_SetupPartitions(void) -{ - int i; - for( i = 0; i < MAX_ATA_DISKS; i ++ ) - { - if( !ATA_ScanDisk(i) ) { - gATA_Disks[i].Name[0] = '\0'; // Mark as unused - continue; - } - } -} - -/** - * \brief Sets up the ATA drivers VFS information and registers with DevFS - */ -void ATA_SetupVFS(void) -{ - int i, j, k; - - // Count number of nodes needed - giATA_NumNodes = 0; - for( i = 0; i < MAX_ATA_DISKS; i++ ) - { - if(gATA_Disks[i].Name[0] == '\0') continue; // Ignore - giATA_NumNodes ++; - giATA_NumNodes += gATA_Disks[i].NumPartitions; - } - - // Allocate Node space - gATA_Nodes = malloc( giATA_NumNodes * sizeof(void*) ); - - // Set nodes - k = 0; - for( i = 0; i < MAX_ATA_DISKS; i++ ) - { - if(gATA_Disks[i].Name[0] == '\0') continue; // Ignore - gATA_Nodes[ k++ ] = &gATA_Disks[i].Node; - for( j = 0; j < gATA_Disks[i].NumPartitions; j ++ ) - gATA_Nodes[ k++ ] = &gATA_Disks[i].Partitions[j].Node; - } - - gATA_DriverInfo.RootNode.Size = giATA_NumNodes; -} - -/** - * \brief Scan a disk, getting the size and any paritions - * \param Disk Disk ID to scan - */ -int ATA_ScanDisk(int Disk) -{ - tVFS_Node *node; - tMBR mbr; - - ENTER("iDisk", Disk); - - // Get the disk size - gATA_Disks[ Disk ].Sectors = ATA_GetDiskSize(Disk); - if(gATA_Disks[ Disk ].Sectors == 0) - { - LEAVE('i', 0); - return 0; - } - - LOG("gATA_Disks[ %i ].Sectors = 0x%x", Disk, gATA_Disks[ Disk ].Sectors); - - // Create Name - gATA_Disks[ Disk ].Name[0] = 'A'+Disk; - gATA_Disks[ Disk ].Name[1] = '\0'; - - #if 1 - { - Uint64 val = gATA_Disks[ Disk ].Sectors / 2; - char *units = "KiB"; - if( val > 4*1024 ) { - val /= 1024; - units = "MiB"; - } - else if( val > 4*1024 ) { - val /= 1024; - units = "GiB"; - } - else if( val > 4*1024 ) { - val /= 1024; - units = "TiB"; - } - Log_Notice("ATA", "Disk %s: 0x%llx Sectors (%lli %s)", - gATA_Disks[ Disk ].Name, gATA_Disks[ Disk ].Sectors, val, units); - } - #endif - - // Get pointer to vfs node and populate it - node = &gATA_Disks[ Disk ].Node; - node->Size = gATA_Disks[Disk].Sectors * SECTOR_SIZE; - node->NumACLs = 0; // Means Superuser only can access it - node->Inode = (Disk << 8) | 0xFF; - node->ImplPtr = gATA_Disks[ Disk ].Name; - - node->ATime = node->MTime = node->CTime = now(); - - node->Type = &gATA_DiskNodeType; - - // --- Scan Partitions --- - LOG("Reading MBR"); - // Read Boot Sector - if( ATA_ReadDMA( Disk, 0, 1, &mbr ) != 0 ) { - Log_Warning("ATA", "Error in reading MBR on %i", Disk); - LEAVE('i', 0); - return 0; - } - - // Check for a GPT table - if(mbr.Parts[0].SystemID == 0xEE) - ATA_ParseGPT(Disk); - else // No? Just parse the MBR - ATA_ParseMBR(Disk, &mbr); - - #if DEBUG >= 2 - ATA_ReadDMA( Disk, 1, 1, &mbr ); - Debug_HexDump("ATA_ScanDisk", &mbr, 512); - #endif - - LEAVE('i', 1); - return 1; -} - -/** - * \fn void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length) - * \brief Fills a parition's information structure - */ -void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length) -{ - ENTER("pPart iDisk iNum XStart XLength", Part, Disk, Num, Start, Length); - Part->Start = Start; - Part->Length = Length; - Part->Name[0] = 'A'+Disk; - if(Num >= 10) { - Part->Name[1] = '1'+Num/10; - Part->Name[2] = '1'+Num%10; - Part->Name[3] = '\0'; - } else { - Part->Name[1] = '1'+Num; - Part->Name[2] = '\0'; - } - Part->Node.NumACLs = 0; // Only root can read/write raw block devices - Part->Node.Inode = (Disk << 8) | Num; - Part->Node.ImplPtr = Part->Name; - - Part->Node.Type = &gATA_DiskNodeType; - Log_Notice("ATA", "Partition %s at 0x%llx+0x%llx", Part->Name, Part->Start, Part->Length); - LOG("Made '%s' (&Node=%p)", Part->Name, &Part->Node); - LEAVE('-'); -} - -/** - * \fn void ATA_ParseGPT(int Disk) - * \brief Parses the GUID Partition Table - */ -void ATA_ParseGPT(int Disk) -{ - ///\todo Support GPT Disks - Warning("GPT Disks are currently unsupported (Disk %i)", Disk); -} - -/** - * \fn char *ATA_ReadDir(tVFS_Node *Node, int Pos) - */ -char *ATA_ReadDir(tVFS_Node *UNUSED(Node), int Pos) -{ - if(Pos >= giATA_NumNodes || Pos < 0) return NULL; - return strdup( gATA_Nodes[Pos]->ImplPtr ); -} - -/** - * \fn tVFS_Node *ATA_FindDir(tVFS_Node *Node, const char *Name) - */ -tVFS_Node *ATA_FindDir(tVFS_Node *UNUSED(Node), const char *Name) -{ - int part; - tATA_Disk *disk; - - // Check first character - if(Name[0] < 'A' || Name[0] > 'A'+MAX_ATA_DISKS) - return NULL; - disk = &gATA_Disks[Name[0]-'A']; - // Raw Disk - if(Name[1] == '\0') { - if( disk->Sectors == 0 && disk->Name[0] == '\0') - return NULL; - return &disk->Node; - } - - // Partitions - if(Name[1] < '0' || '9' < Name[1]) return NULL; - if(Name[2] == '\0') { // <= 9 - part = Name[1] - '0'; - part --; - return &disk->Partitions[part].Node; - } - // > 9 - if('0' > Name[2] || '9' < Name[2]) return NULL; - if(Name[3] != '\0') return NULL; - - part = (Name[1] - '0') * 10; - part += Name[2] - '0'; - part --; - return &disk->Partitions[part].Node; - -} - -/** - * \fn Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - */ -Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - int disk = Node->Inode >> 8; - int part = Node->Inode & 0xFF; - - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - - // Raw Disk Access - if(part == 0xFF) - { - if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE ) { - LEAVE('i', 0); - return 0; - } - if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE ) - Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset; - } - // Partition - else - { - if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) { - LEAVE('i', 0); - return 0; - } - if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) - Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset; - Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE; - } - - { - int ret = DrvUtil_ReadBlock(Offset, Length, Buffer, ATA_ReadRaw, SECTOR_SIZE, disk); - //Log("ATA_ReadFS: disk=%i, Offset=%lli, Length=%lli", disk, Offset, Length); - //Debug_HexDump("ATA_ReadFS", Buffer, Length); - LEAVE('i', ret); - return ret; - } -} - -/** - * \fn Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) - */ -Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - int disk = Node->Inode >> 8; - int part = Node->Inode & 0xFF; - - // Raw Disk Access - if(part == 0xFF) - { - if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE ) - return 0; - if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE ) - Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset; - } - // Partition - else - { - if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) - return 0; - if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) - Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset; - Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE; - } - - Log("ATA_WriteFS: (Node=%p, Offset=0x%llx, Length=0x%llx, Buffer=%p)", Node, Offset, Length, Buffer); - Debug_HexDump("ATA_WriteFS", Buffer, Length); - return DrvUtil_WriteBlock(Offset, Length, Buffer, ATA_ReadRaw, ATA_WriteRaw, SECTOR_SIZE, disk); -} - -const char *csaATA_IOCtls[] = {DRV_IOCTLNAMES, DRV_DISK_IOCTLNAMES, NULL}; -/** - * \fn int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data) - * \brief IO Control Funtion - */ -int ATA_IOCtl(tVFS_Node *UNUSED(Node), int Id, void *Data) -{ - switch(Id) - { - BASE_IOCTLS(DRV_TYPE_DISK, "i386ATA", VERSION, csaATA_IOCtls); - - case DISK_IOCTL_GETBLOCKSIZE: - return 512; - - default: - return 0; - } - return 0; -} - -// --- Disk Access --- -/** - * \fn Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk) - */ -Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk) -{ - int ret; - Uint offset; - Uint done = 0; - - // Pass straight on to ATA_ReadDMAPage if we can - if(Count <= MAX_DMA_SECTORS) - { - ret = ATA_ReadDMA(Disk, Address, Count, Buffer); - if(ret == 0) return 0; - return Count; - } - - // Else we will have to break up the transfer - offset = 0; - while(Count > MAX_DMA_SECTORS) - { - ret = ATA_ReadDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset); - // Check for errors - if(ret != 1) return done; - // Change Position - done += MAX_DMA_SECTORS; - Count -= MAX_DMA_SECTORS; - offset += MAX_DMA_SECTORS*SECTOR_SIZE; - } - - ret = ATA_ReadDMA(Disk, Address+offset, Count, Buffer+offset); - if(ret != 1) return 0; - return done+Count; -} - -/** - * \fn Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk) - */ -Uint ATA_WriteRaw(Uint64 Address, Uint Count, const void *Buffer, Uint Disk) -{ - int ret; - Uint offset; - Uint done = 0; - - // Pass straight on to ATA_WriteDMA, if we can - if(Count <= MAX_DMA_SECTORS) - { - ret = ATA_WriteDMA(Disk, Address, Count, Buffer); - if(ret == 0) return 0; - return Count; - } - - // Else we will have to break up the transfer - offset = 0; - while(Count > MAX_DMA_SECTORS) - { - ret = ATA_WriteDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset); - // Check for errors - if(ret != 1) return done; - // Change Position - done += MAX_DMA_SECTORS; - Count -= MAX_DMA_SECTORS; - offset += MAX_DMA_SECTORS*SECTOR_SIZE; - } - - ret = ATA_WriteDMA(Disk, Address+offset, Count, Buffer+offset); - if(ret != 1) return 0; - return done+Count; -} diff --git a/Modules/Storage/ATA/mbr.c b/Modules/Storage/ATA/mbr.c deleted file mode 100644 index a294ca81..00000000 --- a/Modules/Storage/ATA/mbr.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Acess2 IDE Harddisk Driver - * - MBR Parsing Code - * mbr.c - */ -#define DEBUG 0 -#include -#include "common.h" - -// === PROTOTYPES === -void ATA_ParseMBR(int Disk, tMBR *MBR); -Uint64 ATA_MBR_int_ReadExt(int Disk, Uint64 Addr, Uint64 *Base, Uint64 *Length); - -// === GLOBALS === - -// === CODE === -/** - * \fn void ATA_ParseMBR(int Disk, tMBR *MBR) - */ -void ATA_ParseMBR(int Disk, tMBR *MBR) -{ - int i, j = 0, k = 4; - Uint64 extendedLBA; - Uint64 base, len; - - ENTER("iDisk", Disk); - - // Count Partitions - gATA_Disks[Disk].NumPartitions = 0; - extendedLBA = 0; - for( i = 0; i < 4; i ++ ) - { - if( MBR->Parts[i].SystemID == 0 ) continue; - if( MBR->Parts[i].Boot == 0x0 || MBR->Parts[i].Boot == 0x80 // LBA 28 - || MBR->Parts[i].Boot == 0x1 || MBR->Parts[i].Boot == 0x81 // LBA 48 - ) - { - if( MBR->Parts[i].SystemID == 0xF || MBR->Parts[i].SystemID == 5 ) { - LOG("Extended Partition at 0x%llx", MBR->Parts[i].LBAStart); - if(extendedLBA != 0) { - Warning("Disk %i has multiple extended partitions, ignoring rest", Disk); - continue; - } - extendedLBA = MBR->Parts[i].LBAStart; - continue; - } - LOG("Primary Partition at 0x%llx", MBR->Parts[i].LBAStart); - - gATA_Disks[Disk].NumPartitions ++; - continue; - } - // Invalid Partition, so don't count it - } - while(extendedLBA != 0) - { - extendedLBA = ATA_MBR_int_ReadExt(Disk, extendedLBA, &base, &len); - if( extendedLBA == -1 ) break; - gATA_Disks[Disk].NumPartitions ++; - } - LOG("gATA_Disks[Disk].NumPartitions = %i", gATA_Disks[Disk].NumPartitions); - - // Create patition array - gATA_Disks[Disk].Partitions = malloc( gATA_Disks[Disk].NumPartitions * sizeof(tATA_Partition) ); - - // --- Fill Partition Info --- - extendedLBA = 0; - for( j = 0, i = 0; i < 4; i ++ ) - { - LOG("MBR->Parts[%i].SystemID = 0x%02x", i, MBR->Parts[i].SystemID); - if( MBR->Parts[i].SystemID == 0 ) continue; - if( MBR->Parts[i].Boot == 0x0 || MBR->Parts[i].Boot == 0x80 ) // LBA 28 - { - base = MBR->Parts[i].LBAStart; - len = MBR->Parts[i].LBALength; - } - else if( MBR->Parts[i].Boot == 0x1 || MBR->Parts[i].Boot == 0x81 ) // LBA 58 - { - base = (MBR->Parts[i].StartHi << 16) | MBR->Parts[i].LBAStart; - len = (MBR->Parts[i].LengthHi << 16) | MBR->Parts[i].LBALength; - } - else - continue; - - if( MBR->Parts[i].SystemID == 0xF || MBR->Parts[i].SystemID == 5 ) { - if(extendedLBA != 0) { - Log_Warning("ATA", "Disk %i has multiple extended partitions, ignoring rest", Disk); - continue; - } - extendedLBA = base; - continue; - } - // Create Partition - ATA_int_MakePartition( - &gATA_Disks[Disk].Partitions[j], Disk, j, - base, len - ); - j ++; - - } - // Scan extended partitions - while(extendedLBA != 0) - { - extendedLBA = ATA_MBR_int_ReadExt(Disk, extendedLBA, &base, &len); - if(extendedLBA == -1) break; - ATA_int_MakePartition( - &gATA_Disks[Disk].Partitions[j], Disk, k, base, len - ); - } - - LEAVE('-'); -} - -/** - * \brief Reads an extended partition - * \return LBA of next Extended, -1 on error, 0 for last - */ -Uint64 ATA_MBR_int_ReadExt(int Disk, Uint64 Addr, Uint64 *Base, Uint64 *Length) -{ - Uint64 link = 0; - int bFoundPart = 0;; - int i; - tMBR mbr; - Uint64 base, len; - - if( ATA_ReadDMA( Disk, Addr, 1, &mbr ) != 0 ) - return -1; // Stop on Errors - - for( i = 0; i < 4; i ++ ) - { - if( mbr.Parts[i].SystemID == 0 ) continue; - - // LBA 24 - if( mbr.Parts[i].Boot == 0x0 || mbr.Parts[i].Boot == 0x80 ) { - base = mbr.Parts[i].LBAStart; - len = mbr.Parts[i].LBALength; - } - // LBA 48 - else if( mbr.Parts[i].Boot == 0x1 || mbr.Parts[i].Boot == 0x81 ) { - base = (mbr.Parts[i].StartHi << 16) | mbr.Parts[i].LBAStart; - len = (mbr.Parts[i].LengthHi << 16) | mbr.Parts[i].LBALength; - } - else { - Log_Warning("ATA MBR", - "Unknown partition type 0x%x, Disk %i Ext 0x%llx Part %i", - mbr.Parts[i].Boot, Disk, Addr, i - ); - return -1; - } - - switch(mbr.Parts[i].SystemID) - { - case 0xF: - case 0x5: - if(link != 0) { - Log_Warning("ATA MBR", - "Disk %i has two forward links in the extended partition", - Disk - ); - return -1; - } - link = base; - break; - default: - if(bFoundPart) { - Warning("ATA MBR", - "Disk %i has more than one partition in the extended partition at 0x%llx", - Disk, Addr - ); - return -1; - } - bFoundPart = 1; - *Base = base; - *Length = len; - break; - } - } - - if(!bFoundPart) { - Log_Warning("ATA MBR", - "No partition in extended partiton, Disk %i 0x%llx", - Disk, Addr); - return -1; - } - - return link; -} diff --git a/Modules/Storage/FDD/Makefile b/Modules/Storage/FDD/Makefile deleted file mode 100644 index 15965531..00000000 --- a/Modules/Storage/FDD/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ = fdd.o -NAME = FDD - --include ../Makefile.tpl diff --git a/Modules/Storage/FDD/fdd.c b/Modules/Storage/FDD/fdd.c deleted file mode 100644 index 9cf808d2..00000000 --- a/Modules/Storage/FDD/fdd.c +++ /dev/null @@ -1,957 +0,0 @@ -/* - * AcessOS 0.1 - * Floppy Disk Access Code - */ -#define DEBUG 0 -#include -#include -#include -#include -#include -#include - -#define WARN 0 - -// === CONSTANTS === -// --- Current Version -#define FDD_VERSION ((0<<8)|(75)) - -// --- Options -#define FDD_SEEK_TIMEOUT 10 // Timeout for a seek operation -#define MOTOR_ON_DELAY 500 // Miliseconds -#define MOTOR_OFF_DELAY 2000 // Miliseconds -#define FDD_MAX_READWRITE_ATTEMPTS 16 - -// === TYPEDEFS === -/** - * \brief Representation of a floppy drive - */ -typedef struct sFloppyDrive -{ - int type; - volatile int motorState; //2 - On, 1 - Spinup, 0 - Off - int track[2]; - int timer; - tVFS_Node Node; - #if !USE_CACHE - tIOCache *CacheHandle; - #endif -} t_floppyDevice; - -/** - * \brief Cached Sector - */ -typedef struct { - Uint64 timestamp; - Uint16 disk; - Uint16 sector; // Allows 32Mb of addressable space (Plenty for FDD) - Uint8 data[512]; -} t_floppySector; - -// === CONSTANTS === -static const char *cFDD_TYPES[] = {"None", "360kB 5.25\"", "1.2MB 5.25\"", "720kB 3.5\"", "1.44MB 3.5\"", "2.88MB 3.5\"" }; -static const int cFDD_SIZES[] = { 0, 360*1024, 1200*1024, 720*1024, 1440*1024, 2880*1024 }; -static const short cPORTBASE[] = { 0x3F0, 0x370 }; -#if DEBUG -static const char *cFDD_STATUSES[] = {NULL, "Error", "Invalid command", "Drive not ready"}; -#endif - -enum FloppyPorts { - PORT_STATUSA = 0x0, - PORT_STATUSB = 0x1, - PORT_DIGOUTPUT = 0x2, - PORT_MAINSTATUS = 0x4, - PORT_DATARATE = 0x4, - PORT_DATA = 0x5, - PORT_DIGINPUT = 0x7, - PORT_CONFIGCTRL = 0x7 -}; - -#define CMD_FLAG_MULTI_TRACK 0x80 //!< Multitrack -#define CMD_FLAG_MFM_ENCODING 0x40 //!< MFM Encoding Mode (Always set for read/write/format/verify) -#define CMD_FLAG_SKIP_MODE 0x20 //!< Skip Mode (don't use) -enum FloppyCommands { - CMD_READ_TRACK = 0x02, - CMD_SPECIFY = 0x03, - CMD_SENSE_STATUS = 0x04, - CMD_WRITE_DATA = 0x05, - CMD_READ_DATA = 0x06, - CMD_RECALIBRATE = 0x07, - CMD_SENSE_INTERRUPT = 0x08, - CMD_WRITE_DEL_DATA = 0x09, - CMD_READ_SECTOR_ID = 0x0A, - // 0x0B - ? - CMD_READ_DEL_DATA = 0x0C, - CMD_FORMAT_TRACK = 0x0D, - // 0x0E - ? - CMD_SEEK_TRACK = 0x0F, - CMD_VERSION = 0x10, - - CMD_LOCK = 0x14, //!< Save controller parameters - - CMD_CONFIGURE = 0x13 -}; - -// === PROTOTYPES === -// --- Filesystem - int FDD_Install(char **Arguments); -void FDD_UnloadModule(); -// --- VFS Methods -char *FDD_ReadDir(tVFS_Node *Node, int pos); -tVFS_Node *FDD_FindDir(tVFS_Node *dirNode, const char *Name); - int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data); -Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); -// --- Functions for IOCache/DrvUtil -Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk); -// --- Raw Disk Access - int FDD_ReadSector(Uint32 disk, Uint64 lba, void *Buffer); - int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer); -// --- Helpers - int FDD_WaitIRQ(); -void FDD_IRQHandler(int Num, void *Ptr); -void FDD_SenseInt(int base, Uint8 *sr0, Uint8 *cyl); - - int FDD_Reset(int id); -void FDD_Recalibrate(int disk); - int FDD_Reconfigure(int ID); - - int FDD_int_SeekTrack(int disk, int head, int track); -void FDD_int_TimerCallback(void *Arg); -void FDD_int_StopMotor(int Disk); -void FDD_int_StopMotorCallback(void *Arg); -void FDD_int_StartMotor(int Disk); - int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt); - - int FDD_int_SendByte(int base, Uint8 Byte); - int FDD_int_GetByte(int base, Uint8 *Byte); - -// === GLOBALS === -MODULE_DEFINE(0, FDD_VERSION, FDD, FDD_Install, NULL, "x86_ISADMA", NULL); -t_floppyDevice gFDD_Devices[2]; -tMutex glFDD; -volatile int gbFDD_IrqFired = 0; -tDevFS_Driver gFDD_DriverInfo = { - NULL, "fdd", - { - .Size = -1, - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Flags = VFS_FFLAG_DIRECTORY, - .ReadDir = FDD_ReadDir, - .FindDir = FDD_FindDir, - .IOCtl = FDD_IOCtl - } -}; - -// === CODE === -/** - * \fn int FDD_Install(char **Arguments) - * \brief Installs floppy driver - */ -int FDD_Install(char **Arguments) -{ - Uint8 data; - char **args = Arguments; - - // Determine Floppy Types (From CMOS) - outb(0x70, 0x10); - data = inb(0x71); - gFDD_Devices[0].type = data >> 4; - gFDD_Devices[1].type = data & 0xF; - gFDD_Devices[0].track[0] = -1; - gFDD_Devices[1].track[1] = -1; - - Log_Log("FDD", "Detected Disk 0: %s and Disk 1: %s", cFDD_TYPES[data>>4], cFDD_TYPES[data&0xF]); - - if( data == 0 ) { - return MODULE_ERR_NOTNEEDED; - } - - // Handle arguments - if(args) { - for(;*args;args++) - { - if(strcmp(*args, "disable")==0) - return MODULE_ERR_NOTNEEDED; - } - } - - // Install IRQ6 Handler - IRQ_AddHandler(6, FDD_IRQHandler, NULL); - - // Ensure the FDD version is 0x90 - { - Uint8 tmp = 0; - FDD_int_SendByte(cPORTBASE[0], CMD_VERSION); - FDD_int_GetByte(cPORTBASE[0], &tmp); - if( tmp != 0x90 ) { - Log_Error("FDD", "Version(0x%2x) != 0x90", tmp); - return MODULE_ERR_NOTNEEDED; - } - } - - // Configure - FDD_Reconfigure(0); - - // Reset Primary FDD Controller - if( FDD_Reset(0) != 0 ) { - return MODULE_ERR_MISC; - } - - #if 0 - { - int retries; - // Recalibrate disks - LOG("Recalibrate disks (16x seek)"); - retries = 16; - while(FDD_int_SeekTrack(0, 0, 1) == 0 && retries --) - Threads_Yield(); // set track - if(retries < 0) LEAVE_RET('i', -1); - - retries = 16; - while(FDD_int_SeekTrack(0, 1, 1) == 0 && retries --) - Threads_Yield(); // set track - if(retries < 0) LEAVE_RET('i', -1); - } - #endif - - - // Initialise Root Node - gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime - = gFDD_DriverInfo.RootNode.ATime = now(); - - // Initialise Child Nodes - gFDD_Devices[0].Node.Inode = 0; - gFDD_Devices[0].Node.Flags = 0; - gFDD_Devices[0].Node.NumACLs = 0; - gFDD_Devices[0].Node.Read = FDD_ReadFS; - gFDD_Devices[0].Node.Write = NULL;//FDD_WriteFS; - memcpy(&gFDD_Devices[1].Node, &gFDD_Devices[0].Node, sizeof(tVFS_Node)); - - gFDD_Devices[1].Node.Inode = 1; - - // Set Lengths - gFDD_Devices[0].Node.Size = cFDD_SIZES[data >> 4]; - gFDD_Devices[1].Node.Size = cFDD_SIZES[data & 0xF]; - - // Create Sector Cache - if( cFDD_SIZES[data >> 4] ) - { - gFDD_Devices[0].CacheHandle = IOCache_Create( - FDD_WriteSector, 0, 512, - gFDD_Devices[0].Node.Size / (512*4) - ); // Cache is 1/4 the size of the disk - } - if( cFDD_SIZES[data & 15] ) - { - gFDD_Devices[1].CacheHandle = IOCache_Create( - FDD_WriteSector, 0, 512, - gFDD_Devices[1].Node.Size / (512*4) - ); // Cache is 1/4 the size of the disk - } - - // Register with devfs - DevFS_AddDevice(&gFDD_DriverInfo); - - return MODULE_ERR_OK; -} - -/** - * \brief Prepare the module for removal - */ -void FDD_UnloadModule() -{ - int i; - DevFS_DelDevice( &gFDD_DriverInfo ); - Mutex_Acquire(&glFDD); - for(i=0;i<4;i++) { - Time_RemoveTimer(gFDD_Devices[i].timer); - FDD_int_StopMotor(i); - } - Mutex_Release(&glFDD); - //IRQ_Clear(6); -} - -/** - * \fn char *FDD_ReadDir(tVFS_Node *Node, int pos) - * \brief Read Directory - */ -char *FDD_ReadDir(tVFS_Node *UNUSED(Node), int Pos) -{ - char name[2] = "0\0"; - - if(Pos >= 2 || Pos < 0) return NULL; - - if(gFDD_Devices[Pos].type == 0) return VFS_SKIP; - - name[0] += Pos; - - return strdup(name); -} - -/** - * \fn tVFS_Node *FDD_FindDir(tVFS_Node *Node, const char *filename); - * \brief Find File Routine (for vfs_node) - */ -tVFS_Node *FDD_FindDir(tVFS_Node *UNUSED(Node), const char *Filename) -{ - int i; - - ENTER("sFilename", Filename); - - // Sanity check string - if(Filename == NULL) { - LEAVE('n'); - return NULL; - } - - // Check string length (should be 1) - if(Filename[0] == '\0' || Filename[1] != '\0') { - LEAVE('n'); - return NULL; - } - - // Get First character - i = Filename[0] - '0'; - - // Check for 1st disk and if it is present return - if(i == 0 && gFDD_Devices[0].type != 0) { - LEAVE('p', &gFDD_Devices[0].Node); - return &gFDD_Devices[0].Node; - } - - // Check for 2nd disk and if it is present return - if(i == 1 && gFDD_Devices[1].type != 0) { - LEAVE('p', &gFDD_Devices[1].Node); - return &gFDD_Devices[1].Node; - } - - // Else return null - LEAVE('n'); - return NULL; -} - -static const char *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL}; -/** - * \fn int FDD_IOCtl(tVFS_Node *Node, int id, void *data) - * \brief Stub ioctl function - */ -int FDD_IOCtl(tVFS_Node *UNUSED(Node), int ID, void *Data) -{ - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_DISK, "FDD", FDD_VERSION, casIOCTLS); - - case DISK_IOCTL_GETBLOCKSIZE: return 512; - - default: - return 0; - } -} - -/** - * \fn Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) - * \brief Read Data from a disk -*/ -Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - int ret; - - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - - if(Node == NULL) { - LEAVE('i', -1); - return -1; - } - - if(Node->Inode != 0 && Node->Inode != 1) { - LEAVE('i', -1); - return -1; - } - - ret = DrvUtil_ReadBlock(Offset, Length, Buffer, FDD_ReadSectors, 512, Node->Inode); - LEAVE('i', ret); - return ret; -} - -/** - * \brief Reads \a Count contiguous sectors from a disk - * \param SectorAddr Address of the first sector - * \param Count Number of sectors to read - * \param Buffer Destination Buffer - * \param Disk Disk Number - * \return Number of sectors read - * \note Used as a ::DrvUtil_ReadBlock helper - */ -Uint FDD_ReadSectors(Uint64 SectorAddr, Uint Count, void *Buffer, Uint Disk) -{ - Uint ret = 0; - while(Count --) - { - if( FDD_ReadSector(Disk, SectorAddr, Buffer) != 1 ) - return ret; - - Buffer = (void*)( (tVAddr)Buffer + 512 ); - SectorAddr ++; - ret ++; - } - return ret; -} - -int FDD_int_ReadWriteSector(Uint32 Disk, Uint64 SectorAddr, int Write, void *Buffer) -{ - int cyl, head, sec; - int spt, base; - int i; - int lba = SectorAddr; - Uint8 st0=0, st1=0, st2=0, bps=0; // Status Values - - ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer); - - base = cPORTBASE[Disk >> 1]; - - LOG("Calculating Disk Dimensions"); - // Get CHS position - if(FDD_int_GetDims(gFDD_Devices[Disk].type, lba, &cyl, &head, &sec, &spt) != 1) - { - LEAVE('i', -1); - return -1; - } - LOG("Cyl=%i, Head=%i, Sector=%i", cyl, head, sec); - - // Start the motor - Mutex_Acquire(&glFDD); // Lock to stop the motor stopping on us - Time_RemoveTimer(gFDD_Devices[Disk].timer); // Remove Old Timer - // Start motor if needed - if(gFDD_Devices[Disk].motorState != 2) FDD_int_StartMotor(Disk); - Mutex_Release(&glFDD); - - // Wait for spinup - LOG("Wait for the motor to spin up"); - while(gFDD_Devices[Disk].motorState == 1) Threads_Yield(); - - LOG("Acquire Spinlock"); - Mutex_Acquire(&glFDD); - - // Read Data from DMA - LOG("Setting DMA for read"); - DMA_SetChannel(2, 512, !Write); // Read/Write 512 Bytes from channel 2 - - LOG("Sending command"); - - #define SENDB(__data) if(FDD_int_SendByte(base, __data)) { FDD_Reset(Disk >> 1); continue; } - - for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ ) - { - FDD_int_SeekTrack(Disk, head, cyl); - if( Write ) { - SENDB(CMD_WRITE_DATA|CMD_FLAG_MFM_ENCODING); - } - else { - SENDB(CMD_READ_DATA|CMD_FLAG_MFM_ENCODING); - } - SENDB( (head << 2) | (Disk&1) ); - SENDB(cyl & 0xFF); - SENDB(head & 0xFF); - SENDB(sec & 0xFF); - SENDB(0x02); // Bytes Per Sector (Real BPS=128*2^{val}) - SENDB(spt); // SPT - SENDB(0x1B); // Gap Length (27 is default) - SENDB(0xFF); // Data Length - - // Wait for IRQ - if( Write ) { - LOG("Writing Data"); - DMA_WriteData(2, 512, Buffer); - LOG("Waiting for Data to be written"); - if( FDD_WaitIRQ() ) { FDD_Reset(Disk>>1); continue; } - } - else { - LOG("Waiting for data to be read"); - if( FDD_WaitIRQ() ) { FDD_Reset(Disk>>1); continue; } - LOG("Reading Data"); - DMA_ReadData(2, 512, Buffer); - } - - // Clear Input Buffer - LOG("Clearing Input Buffer"); - // Status Values - FDD_int_GetByte(base, &st0); - FDD_int_GetByte(base, &st1); - FDD_int_GetByte(base, &st2); - - // Cylinder, Head and Sector (mutilated in some way) - FDD_int_GetByte(base, NULL); // Cylinder - FDD_int_GetByte(base, NULL); // Head - FDD_int_GetByte(base, NULL); // Sector - // Should be the BPS set above (0x02) - FDD_int_GetByte(base, &bps); - - // Check Status - // - Error Code - if(st0 & 0xC0) { - LOG("Error (st0 & 0xC0) \"%s\"", cFDD_STATUSES[st0 >> 6]); - continue; - } - // - Status Flags - if(st0 & 0x08) { LOG("Drive not ready"); continue; } - if(st1 & 0x80) { LOG("End of Cylinder"); continue; } - if(st1 & 0x20) { LOG("CRC Error"); continue; } - if(st1 & 0x10) { LOG("Controller Timeout"); continue; } - if(st1 & 0x04) { LOG("No Data Found"); continue; } - if(st1 & 0x01 || st2 & 0x01) { - LOG("No Address mark found"); - continue; - } - if(st2 & 0x40) { LOG("Deleted address mark"); continue; } - if(st2 & 0x20) { LOG("CRC error in data"); continue; } - if(st2 & 0x10) { LOG("Wrong Cylinder"); continue; } - if(st2 & 0x04) { LOG("uPD765 sector not found"); continue; } - if(st2 & 0x02) { LOG("Bad Cylinder"); continue; } - - if(bps != 0x2) { - LOG("Returned BPS = 0x%02x, not 0x02", bps); - continue; - } - - if(st1 & 0x02) { - LOG("Floppy not writable"); - // Return error without triggering the attempt count check - i = FDD_MAX_READWRITE_ATTEMPTS+1; - break; - } - - // Success! - break; - } - #undef SENDB - - // Release Spinlock - LOG("Realeasing Spinlock and setting motor to stop"); - Mutex_Release(&glFDD); - - if(i == FDD_MAX_READWRITE_ATTEMPTS) { - Log_Warning("FDD", "Exceeded %i attempts in %s the disk", - FDD_MAX_READWRITE_ATTEMPTS, - (Write ? "writing to" : "reading from") - ); - } - - // Don't turn the motor off now, wait for a while - FDD_int_StopMotor(Disk); - - // Error check - if( i < FDD_MAX_READWRITE_ATTEMPTS ) { - LEAVE('i', 0); - return 0; - } - else { - LEAVE('i', 1); - return 1; - } -} - -/** - * \fn int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer) - * \brief Read a sector from disk - * \todo Make real-hardware safe (account for read errors) - */ -int FDD_ReadSector(Uint32 Disk, Uint64 SectorAddr, void *Buffer) -{ - int ret; - - ENTER("iDisk XSectorAddr pBuffer", Disk, SectorAddr, Buffer); - - if( IOCache_Read( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer ) == 1 ) { - LEAVE('i', 1); - return 1; - } - - // Pass to general function - ret = FDD_int_ReadWriteSector(Disk, SectorAddr, 0, Buffer); - - if( ret == 0 ) { - IOCache_Add( gFDD_Devices[Disk].CacheHandle, SectorAddr, Buffer ); - LEAVE('i', 1); - return 1; - } - else { - LOG("Reading failed"); - LEAVE('i', 0); - return 0; - } -} - -/** - * \fn int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer) - * \brief Write a sector to the floppy disk - * \note Not Implemented - */ -int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer) -{ - Log_Warning("FDD", "Read Only at the moment"); - return -1; -} - -/** - * \brief Seek disk to selected track - */ -int FDD_int_SeekTrack(int disk, int head, int track) -{ - Uint8 sr0=0, cyl=0; - int base, i, bUnclean; - - base = cPORTBASE[disk>>1]; - - // Check if seeking is needed - if(gFDD_Devices[disk].track[head] == track) - return 1; - - // - Seek Head 0 - for( i = 0; i < FDD_MAX_READWRITE_ATTEMPTS; i ++ ) - { - if(i && bUnclean) FDD_Reset(disk >> 1); - bUnclean = 1; - if(FDD_int_SendByte(base, CMD_SEEK_TRACK)) continue; - if(FDD_int_SendByte(base, (head<<2)|(disk&1))) continue; - if(FDD_int_SendByte(base, track)) continue; - FDD_WaitIRQ(); - FDD_SenseInt(base, &sr0, &cyl); // Wait for IRQ - - bUnclean = 0; - if( cyl != track ) - continue; // Try again - if( (sr0 & 0xF0) != 0x20 ) { - LOG("sr0 = 0x%x", sr0); - continue ; - } - - break; - } - - if( i == FDD_MAX_READWRITE_ATTEMPTS ) { - Log_Warning("FDD", "Unable to seek to track %i on disk %i", - track, disk); - return 0; - } - - // Set Track in structure - gFDD_Devices[disk].track[head] = track; - - LOG("Time_Delay(100)"); - // Wait for Head to settle - Time_Delay(100); - - return 1; -} - -/** - * \fn int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt) - * \brief Get Dimensions of a disk - */ -int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt) -{ - switch(type) { - case 0: - return 0; - - // 360Kb 5.25" - case 1: - *spt = 9; - *s = (lba % 9) + 1; - *c = lba / 18; - *h = (lba / 9) & 1; - break; - - // 1220Kb 5.25" - case 2: - *spt = 15; - *s = (lba % 15) + 1; - *c = lba / 30; - *h = (lba / 15) & 1; - break; - - // 720Kb 3.5" - case 3: - *spt = 9; - *s = (lba % 9) + 1; - *c = lba / 18; - *h = (lba / 9) & 1; - break; - - // 1440Kb 3.5" - case 4: - *spt = 18; - *s = (lba % 18) + 1; - *c = lba / 36; - *h = (lba / 18) & 1; - //Log("1440k - lba=%i(0x%x), *s=%i,*c=%i,*h=%i", lba, lba, *s, *c, *h); - break; - - // 2880Kb 3.5" - case 5: - *spt = 36; - *s = (lba % 36) + 1; - *c = lba / 72; - *h = (lba / 32) & 1; - break; - - default: - return -2; - } - return 1; -} - -/** - * \fn void FDD_IRQHandler(int Num) - * \brief Handles IRQ6 - */ -void FDD_IRQHandler(int Num, void *Ptr) -{ - gbFDD_IrqFired = 1; -} - -/** - * \brief Wait for the FDD IRQ to fire - * \return Boolean failure (1 for timeout) - */ -inline int FDD_WaitIRQ() -{ - tTime end = now() + 2000; - - // Wait for IRQ - while(!gbFDD_IrqFired && now() < end) - Threads_Yield(); - - if( !gbFDD_IrqFired ) { - Log_Warning("FDD", "FDD_WaitIRQ - Timeout"); - return 1; - } - - gbFDD_IrqFired = 0; - return 0; -} - -void FDD_SenseInt(int base, Uint8 *sr0, Uint8 *cyl) -{ - FDD_int_SendByte(base, CMD_SENSE_INTERRUPT); - FDD_int_GetByte(base, sr0); - FDD_int_GetByte(base, cyl); -} - -/** - * void FDD_int_SendByte(int base, char byte) - * \brief Sends a command to the controller - */ -int FDD_int_SendByte(int base, Uint8 byte) -{ - tTime end = now() + 1000; // 1s - - while( (inb(base + PORT_MAINSTATUS) & 0x80) != 0x80 && now() < end ) - Threads_Yield(); //Delay -// Time_Delay(10); //Delay - - if( inb(base + PORT_MAINSTATUS) & 0x40 ) { - Log_Warning("FDD", "FDD_int_SendByte: DIO set, is this ok?"); - return -2; - } - - if( now() > end ) - { - Log_Warning("FDD", "FDD_int_SendByte: Timeout sending byte 0x%x to base 0x%x", byte, base); - return 1; - } - outb(base + PORT_DATA, byte); -// Log_Debug("FDD", "FDD_int_SendByte: Sent 0x%02x to 0x%x", byte, base); - return 0; -} - -/** - * int FDD_int_GetByte(int base, char byte) - * \brief Receive data from fdd controller - */ -int FDD_int_GetByte(int base, Uint8 *value) -{ - tTime end = now() + 1000; // 1s - - while( (inb(base + PORT_MAINSTATUS) & 0x80) != 0x80 && now() < end ) - Time_Delay(10); - - if( !(inb(base + PORT_MAINSTATUS) & 0x40) ) { - Log_Warning("FDD", "FDD_int_GetByte: DIO unset, is this ok?"); - return -2; - } - - if( now() > end ) - { - Log_Warning("FDD", "FDD_int_GetByte: Timeout reading byte from base 0x%x", base); - return -1; - } - - if(value) - *value = inb(base + PORT_DATA); - else - inb(base + PORT_DATA); - return 0; -} - -/** - * \brief Recalibrate the specified disk - */ -void FDD_Recalibrate(int disk) -{ - ENTER("idisk", disk); - - LOG("Starting Motor"); - FDD_int_StartMotor(disk); - // Wait for Spinup - while(gFDD_Devices[disk].motorState <= 1) Threads_Yield(); - - LOG("Sending Calibrate Command"); - FDD_int_SendByte(cPORTBASE[disk>>1], CMD_RECALIBRATE); - FDD_int_SendByte(cPORTBASE[disk>>1], disk&1); - - LOG("Waiting for IRQ"); - FDD_WaitIRQ(); - FDD_SenseInt(cPORTBASE[disk>>1], NULL, NULL); - - LOG("Stopping Motor"); - FDD_int_StopMotor(disk); - LEAVE('-'); -} - -/** - * \brief Reconfigure the controller - */ -int FDD_Reconfigure(int ID) -{ - Uint16 base = cPORTBASE[ID]; - - ENTER("iID", ID); - - FDD_int_SendByte(base, CMD_CONFIGURE); - FDD_int_SendByte(base, 0); - // Implied seek enabled, FIFO Enabled, Drive Polling Disabled, data buffer threshold 8 bytes - FDD_int_SendByte(base, (1 << 6) | (0 << 5) | (0 << 4) | 7); - FDD_int_SendByte(base, 0); // Precompensation - use default - - // Commit - FDD_int_SendByte(base, CMD_LOCK|CMD_FLAG_MULTI_TRACK); - FDD_int_GetByte(base, NULL); - - LEAVE('i', 0); - return 0; -} - -/** - * \brief Reset the specified FDD controller - */ -int FDD_Reset(int id) -{ - Uint16 base = cPORTBASE[id]; - Uint8 motor_state; - - ENTER("iID", id); - - // Reset the card - motor_state = inb(base + PORT_DIGOUTPUT) & 0xF0; - outb(base + PORT_DIGOUTPUT, motor_state|0); // Disable FDC - Time_Delay(1); - outb(base + PORT_DIGOUTPUT, motor_state|8|4); // Re-enable FDC (DMA and Enable) - - // Set the data rate - outb(base + PORT_DATARATE, 0); // Set data rate to 500K/s - - // Wait for IRQ - LOG("Awaiting IRQ"); - - FDD_WaitIRQ(); - - FDD_SenseInt(base, NULL, NULL); - - // Specify - FDD_int_SendByte(base, CMD_SPECIFY); // Step and Head Load Times - FDD_int_SendByte(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each) - FDD_int_SendByte(base, 0x02); // Head Load Time >> 1 - - LOG("Recalibrating Disk"); - FDD_Recalibrate((id<<1)|0); - FDD_Recalibrate((id<<1)|1); - - LEAVE_RET('i', 0); -} - -/** - * \fn void FDD_int_TimerCallback() - * \brief Called by timer - */ -void FDD_int_TimerCallback(void *Arg) -{ - int disk = (Uint)Arg; - ENTER("iarg", disk); - if(gFDD_Devices[disk].motorState == 1) - gFDD_Devices[disk].motorState = 2; - Time_RemoveTimer(gFDD_Devices[disk].timer); - gFDD_Devices[disk].timer = -1; - LEAVE('-'); -} - -/** - * \fn void FDD_int_StartMotor(char disk) - * \brief Starts FDD Motor - */ -void FDD_int_StartMotor(int disk) -{ - Uint8 state; - if( gFDD_Devices[disk].motorState != 0 ) return ; - // Set motor ON bit - state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT ); - state |= 1 << (4+disk); - outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state ); - // Mark as spinning up - gFDD_Devices[disk].motorState = 1; - // Schedule a timer for when it's up to speed - gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_ON_DELAY, FDD_int_TimerCallback, (void*)(Uint)disk); -} - -/** - * \brief Schedule the drive motor to stop - * \param Disk Disk number to stop - */ -void FDD_int_StopMotor(int Disk) -{ - // Ignore if the motor is aready off - if( gFDD_Devices[Disk].motorState == 0 ) return ; - - // Don't double-schedule timer - if( gFDD_Devices[Disk].timer != -1 ) - { - Time_RemoveTimer( gFDD_Devices[Disk].timer ); - } - - gFDD_Devices[Disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotorCallback, (void*)(Uint)Disk); -} - -/** - * \brief Stops FDD Motor - */ -void FDD_int_StopMotorCallback(void *Arg) -{ - Uint8 state, disk = (Uint)Arg; - - // Mutex is only locked if disk is in use - if( Mutex_IsLocked(&glFDD) ) return ; - - ENTER("iDisk", disk); - - // Clear motor on bit - state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT ); - state &= ~( 1 << (4+disk) ); - outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state ); - - // Mark as off - gFDD_Devices[disk].motorState = 0; - - LEAVE('-'); -} - diff --git a/Modules/Storage/FDDv2/Makefile b/Modules/Storage/FDDv2/Makefile deleted file mode 100644 index 97aae0c4..00000000 --- a/Modules/Storage/FDDv2/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# - -DEPS = x86/ISADMA -OBJ = main.o fdc.o -NAME = FDDv2 - --include ../Makefile.tpl diff --git a/Modules/Storage/FDDv2/common.h b/Modules/Storage/FDDv2/common.h deleted file mode 100644 index 4d6a4f7e..00000000 --- a/Modules/Storage/FDDv2/common.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Acess2 82077AA FDC - * - By John Hodge (thePowersGang) - * - * common.h - * - Common definitions - */ -#ifndef _FDC_COMMON_H_ -#define _FDC_COMMON_H_ - -#include -#include - -// === CONSTANTS === -#define MAX_DISKS 8 // 4 per controller, 2 controllers -#define TRACKS_PER_DISK (1440*2/18) -#define BYTES_PER_TRACK (18*512) - -// === TYPEDEFS === -typedef struct sFDD_Drive tDrive; - -// === STRUCTURES === -struct sFDD_Drive -{ - int bValid; - int bInserted; - int MotorState; - tTimer *Timer; - - tMutex Mutex; - - void *TrackData[TRACKS_PER_DISK]; // Whole tracks are read -}; - -// === FUNCTIONS === -extern int FDD_SetupIO(void); -extern int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer); - -// === GLOBALS === -extern tDrive gaFDD_Disks[MAX_DISKS]; - -#endif - diff --git a/Modules/Storage/FDDv2/fdc.c b/Modules/Storage/FDDv2/fdc.c deleted file mode 100644 index e86b31c6..00000000 --- a/Modules/Storage/FDDv2/fdc.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Acess2 82077AA FDC - * - By John Hodge (thePowersGang) - * - * fdc.c - * - FDC IO Functions - */ -#define DEBUG 0 -#include -#include "common.h" -#include -#include - -// === CONSTANTS === -#define MOTOR_ON_DELAY 500 -#define MOTOR_OFF_DELAY 2000 - -enum eMotorState -{ - MOTOR_OFF, - MOTOR_ATSPEED, -}; - -enum eFDC_Registers -{ - FDC_DOR = 0x02, // Digital Output Register - FDC_MSR = 0x04, // Master Status Register (Read Only) - FDC_FIFO = 0x05, // FIFO port - FDC_CCR = 0x07 // Configuration Control Register (write only) -}; - -enum eFDC_Commands -{ - CMD_SPECIFY = 3, // Specify parameters - CMD_WRITE_DATA = 5, // Write Data - CMD_READ_DATA = 6, // Read Data - CMD_RECALIBRATE = 7, // Recalibrate a drive - CMD_SENSE_INTERRUPT = 8, // Sense (Ack) an interrupt - CMD_SEEK = 15, // Seek to a track -}; - -// === PROTOTYPES === - int FDD_SetupIO(void); - int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer); - int FDD_int_SeekToTrack(int Disk, int Track); - int FDD_int_Calibrate(int Disk); - int FDD_int_Reset(int Disk); -// --- FIFO - int FDD_int_WriteData(Uint16 Base, Uint8 Data); - int FDD_int_ReadData(Uint16 Base, Uint8 *Data); -void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl); -// --- Motor Control - int FDD_int_StartMotor(int Disk); - int FDD_int_StopMotor(int Disk); -void FDD_int_StopMotorCallback(void *Ptr); -// --- Helpers - int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0); -Uint16 FDD_int_GetBase(int Disk, int *Drive); -// --- Interrupt -void FDD_int_ClearIRQ(void); - int FDD_int_WaitIRQ(void); -void FDD_int_IRQHandler(int IRQ, void *Ptr); - -// === GLOBALS === -/** - * \brief Marker for IRQ6 - * \todo Convert into a semaphore? - */ - int gbFDD_IRQ6Fired; -/** - * \brief Protector for DMA and IRQ6 - */ -tMutex gFDD_IOMutex; - -// === CODE === -/** - * \brief Set up FDC IO - * \return Boolean failure - * - * Registers the IRQ handler and resets the controller - */ -int FDD_SetupIO(void) -{ - // Install IRQ6 Handler - IRQ_AddHandler(6, FDD_int_IRQHandler, NULL); - - // Reset controller - FDD_int_Reset(0); - // TODO: All controllers - - return 0; -} - -/** - * \brief Read/Write data from/to a disk - * \param Disk Global disk number - * \param Track Track number (Cyl*2+Head) - * \param bWrite Toggle write mode - * \param Buffer Destination/Source buffer - * \return Boolean failure - */ -int FDD_int_ReadWriteTrack(int Disk, int Track, int bWrite, void *Buffer) -{ - Uint8 cmd; - int i, _disk; - Uint16 base = FDD_int_GetBase(Disk, &_disk); - int cyl = Track >> 1, head = Track & 1; - - ENTER("iDisk iTrack ibWrite pBuffer", Disk, Track, bWrite, Buffer); - - Mutex_Acquire( &gFDD_IOMutex ); - - // Initialise DMA for read/write - // TODO: Support non 1.44MiB FDs - DMA_SetChannel(2, BYTES_PER_TRACK, !bWrite); - - // Select command - if( bWrite ) - cmd = CMD_WRITE_DATA | 0xC0; - else - cmd = CMD_READ_DATA | 0xC0; - - LOG("cmd = 0x%x", cmd); - - // Seek - if( FDD_int_SeekToTrack(Disk, Track) ) { - Mutex_Release( &gFDD_IOMutex ); - LEAVE('i', -1); - return -1; - } - LOG("Track seek done"); - - for( i = 0; i < 20; i ++ ) - { - LOG("Starting motor"); - FDD_int_StartMotor(Disk); - - // Write data - if( bWrite ) - DMA_WriteData(2, BYTES_PER_TRACK, Buffer); - - LOG("Sending command stream"); - FDD_int_WriteData(base, cmd); - FDD_int_WriteData(base, (head << 2) | _disk); - FDD_int_WriteData(base, cyl); - FDD_int_WriteData(base, head); - FDD_int_WriteData(base, 1); // First Sector - FDD_int_WriteData(base, 2); // Bytes per sector (128*2^n) - FDD_int_WriteData(base, 18); // 18 tracks (full disk) - TODO: Non 1.44 - FDD_int_WriteData(base, 0x1B); // Gap length - TODO: again - FDD_int_WriteData(base, 0xFF); // Data length - ? - - LOG("Waiting for IRQ"); - FDD_int_WaitIRQ(); - - // No Sense Interrupt - - LOG("Reading result"); - Uint8 st0=0, st1=0, st2=0, bps=0; - FDD_int_ReadData(base, &st0); - FDD_int_ReadData(base, &st1); // st1 - FDD_int_ReadData(base, &st2); // st2 - FDD_int_ReadData(base, NULL); // rcy - Mutilated Cyl - FDD_int_ReadData(base, NULL); // rhe - Mutilated Head - FDD_int_ReadData(base, NULL); // rse - Mutilated sector - FDD_int_ReadData(base, &bps); // bps - Should be the same as above - - if( st0 & 0xc0 ) { - FDD_int_HandleST0Error(__func__, Disk, st0); - continue ; - } - - if( st2 & 0x02 ) { - Log_Debug("FDD", "Disk %i is not writable", Disk); - Mutex_Release( &gFDD_IOMutex ); - LEAVE('i', 2); - return 2; - } - - if( st0 & 0x08 ) { - Log_Debug("FDD", "FDD_int_ReadWriteTrack: Drive not ready"); - continue ; - } - - - if( st1 & 0x80 ) { - Log_Debug("FDD", "FDD_int_ReadWriteTrack: End of cylinder"); - continue ; - } - - if( st1 & (0x20|0x10|0x04|0x01) ) { - Log_Debug("FDD", "FDD_int_ReadWriteTrack: st1 = 0x%x", st1); - continue; - } - - if( st2 & (0x40|0x20|0x10|0x04|0x01) ) { - Log_Debug("FDD", "FDD_int_ReadWriteTrack: st2 = 0x%x", st2); - continue ; - } - - if( bps != 0x2 ) { - Log_Debug("FDD", "Wanted bps = 2 (512), got %i", bps); - continue ; - } - - // Read back data - if( !bWrite ) - DMA_ReadData(2, BYTES_PER_TRACK, Buffer); - - LOG("All data done"); - FDD_int_StopMotor(Disk); - Mutex_Release( &gFDD_IOMutex ); - LEAVE('i', 0); - return 0; - } - - Log_Debug("FDD", "%i retries exhausted", i); - FDD_int_StopMotor(Disk); - Mutex_Release( &gFDD_IOMutex ); - LEAVE('i', 1); - return 1; -} - -/** - * \brief Seek to a specific track - * \param Disk Global disk number - * \param Track Track number (Cyl*2+Head) - * \return Boolean failure - */ -int FDD_int_SeekToTrack(int Disk, int Track) -{ - Uint8 st0=0, res_cyl=0; - int cyl, head; - int _disk; - Uint16 base = FDD_int_GetBase(Disk, &_disk);; - - ENTER("iDisk iTrack", Disk, Track); - - cyl = Track / 2; - head = Track % 2; - - LOG("cyl = %i, head = %i", cyl, head); - - FDD_int_StartMotor(Disk); - - for( int i = 0; i < 10; i ++ ) - { - LOG("Sending command"); - FDD_int_ClearIRQ(); - FDD_int_WriteData(base, CMD_SEEK); - FDD_int_WriteData(base, (head << 2) + _disk); - FDD_int_WriteData(base, cyl); - - LOG("Waiting for IRQ"); - FDD_int_WaitIRQ(); - FDD_int_SenseInterrupt(base, &st0, &res_cyl); - - if( st0 & 0xC0 ) - { - FDD_int_HandleST0Error(__func__, Disk, st0); - continue ; - } - - if( res_cyl == cyl ) { - FDD_int_StopMotor(Disk); - LEAVE('i', 0); - return 0; - } - } - - Log_Error("FDD", "FDD_int_SeekToTrack: 10 retries exhausted\n"); - FDD_int_StopMotor(Disk); - LEAVE('i', 1); - return 1; -} - -/** - * \brief Calibrate a drive - * \param Disk Global disk number - */ -int FDD_int_Calibrate(int Disk) -{ - int _disk; - Uint16 base = FDD_int_GetBase(Disk, &_disk); - FDD_int_StartMotor(Disk); - - for( int i = 0; i < 10; i ++ ) - { - Uint8 st0=0, cyl = -1; - - FDD_int_ClearIRQ(); - FDD_int_WriteData(base, CMD_RECALIBRATE); - FDD_int_WriteData(base, _disk); - - FDD_int_WaitIRQ(); - - FDD_int_SenseInterrupt(base, &st0, &cyl); - - if( st0 & 0xC0 ) { - FDD_int_HandleST0Error(__func__, Disk, st0); - continue ; - } - - if( cyl == 0 ) - { - FDD_int_StopMotor(Disk); - return 0; - } - } - - Log_Error("FDD", "FDD_int_Calibrate: Retries exhausted"); - - return 1; -} - -/** - * \brief Reset a controller - * \param Base Controller base address - */ -int FDD_int_Reset(int Disk) -{ - Uint8 tmp; - int _disk; - Uint16 base = FDD_int_GetBase(Disk, &_disk); - - tmp = inb(base + FDC_DOR) & 0xF0; - outb( base + FDC_DOR, 0x00 ); - Time_Delay(1); - outb( base + FDC_DOR, tmp | 0x0C ); - - FDD_int_SenseInterrupt(base, NULL, NULL); - - outb(base + FDC_CCR, 0x00); // 500KB/s - - FDD_int_WriteData(base, CMD_SPECIFY); // Step and Head Load Times - FDD_int_WriteData(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each) - FDD_int_WriteData(base, 0x02); // Head Load Time >> 1 - - // TODO: Recalibrate all present disks - FDD_int_Calibrate(Disk); - return 0; -} - -/** - * \brief Write a byte to the FIFO - */ -int FDD_int_WriteData(Uint16 Base, Uint8 Data) -{ - for( int i = 0; i < 100; i ++ ) - { - if( inb(Base + FDC_MSR) & 0x80 ) - { - outb(Base + FDC_FIFO, Data); - return 0; - } - Time_Delay(10); - } - Log_Error("FDD", "Write timeout"); - return 1; -} - -/** - * \brief Read a byte from the FIFO - */ -int FDD_int_ReadData(Uint16 Base, Uint8 *Data) -{ - for( int i = 0; i < 100; i ++ ) - { - if( inb(Base + FDC_MSR) & 0x80 ) - { - Uint8 tmp = inb(Base + FDC_FIFO); - if(Data) *Data = tmp; - return 0; - } - Time_Delay(10); - } - Log_Error("FDD", "Read timeout"); - return 1; -} - -/** - * \brief Acknowledge an interrupt - * \param Base Controller base address - * \param ST0 Location to store the ST0 value - * \param Cyl Current cylinder - */ -void FDD_int_SenseInterrupt(Uint16 Base, Uint8 *ST0, Uint8 *Cyl) -{ - FDD_int_WriteData(Base, CMD_SENSE_INTERRUPT); - FDD_int_ReadData(Base, ST0); - FDD_int_ReadData(Base, Cyl); -} - -/** - * \brief Start the motor on a disk - */ -int FDD_int_StartMotor(int Disk) -{ - int _disk; - Uint16 base = FDD_int_GetBase(Disk, &_disk); - - // Clear the motor off timer - Time_RemoveTimer(gaFDD_Disks[Disk].Timer); - gaFDD_Disks[Disk].Timer = NULL; - - // Check if the motor is already on - if( gaFDD_Disks[Disk].MotorState == MOTOR_ATSPEED ) - return 0; - - // Turn motor on - outb(base + FDC_DOR, inb(base+FDC_DOR) | (1 << (_disk + 4))); - - // Wait for it to reach speed - Time_Delay(MOTOR_ON_DELAY); - - gaFDD_Disks[Disk].MotorState = MOTOR_ATSPEED; - - return 0; -} - -/** - * \brief Schedule the motor to stop - */ -int FDD_int_StopMotor(int Disk) -{ - if( gaFDD_Disks[Disk].MotorState != MOTOR_ATSPEED ) - return 0; - if( gaFDD_Disks[Disk].Timer != NULL ) - return 0; - - gaFDD_Disks[Disk].Timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotorCallback, (void*)(tVAddr)Disk); - - return 0; -} - -/** - * \brief Actually stop the motor - * \param Ptr Actaully the global disk number - */ -void FDD_int_StopMotorCallback(void *Ptr) -{ - int Disk = (tVAddr)Ptr; - int _disk; - Uint16 base = FDD_int_GetBase(Disk, &_disk); - - gaFDD_Disks[Disk].Timer = NULL; - gaFDD_Disks[Disk].MotorState = MOTOR_OFF; - - outb(base + FDC_DOR, inb(base+FDC_DOR) & ~(1 << (_disk + 4))); - - return ; -} - -/** - * \brief Converts a global disk number into a controller and drive - * \param Disk Global disk number - * \param Drive Destination for controller disk number - * \return Controller base address - */ -Uint16 FDD_int_GetBase(int Disk, int *Drive) -{ - if(Drive) *Drive = Disk & 3; - switch(Disk >> 2) - { - case 0: return 0x3F0; - case 1: return 0x370; - default: - return 0; - } -} - -/** - * \brief Convert a ST0 error value into a message - * \param Fcn Calling function name - * \parma Disk Global disk number - * \param ST0 ST0 Value - * \return Boolean failure - */ -int FDD_int_HandleST0Error(const char *Fcn, int Disk, Uint8 ST0) -{ - static const char *status_type[] = { - 0, "Error", "Invalid", "Drive Error" - }; - - Log_Debug("FDD", "%s: Disk %i ST0 Status = %s (0x%x & 0xC0 = 0x%x)", - Fcn, Disk, status_type[ST0 >> 6], ST0, ST0 & 0xC0 - ); - return 0; -} - -/** - * \brief Clear the IRQ fired flag - */ -void FDD_int_ClearIRQ(void) -{ - gbFDD_IRQ6Fired = 0; -} - -/** - * \brief Wait for an IRQ to fire - */ -int FDD_int_WaitIRQ(void) -{ - while(gbFDD_IRQ6Fired == 0) - Threads_Yield(); - return 0; -} - -/** - * \brief IRQ Handler - * \param IRQ IRQ Number (unused) - * \param Ptr Data Pointer (unused) - */ -void FDD_int_IRQHandler(int IRQ, void *Ptr) -{ - gbFDD_IRQ6Fired = 1; -} - diff --git a/Modules/Storage/FDDv2/main.c b/Modules/Storage/FDDv2/main.c deleted file mode 100644 index bec157ce..00000000 --- a/Modules/Storage/FDDv2/main.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Acess2 82077AA FDC - * - By John Hodge (thePowersGang) - * - * fdc.c - * - Core file - */ -#define DEBUG 0 -#include -#include -#include -#include "common.h" -#include - -// === CONSTANTS === -#define FDD_VERSION VER2(1,10) - -// === STRUCTURES === - -// === PROTOTYPES === - int FDD_Install(char **Arguments); - int FDD_RegisterFS(void); -// --- VFS -char *FDD_ReadDir(tVFS_Node *Node, int pos); -tVFS_Node *FDD_FindDir(tVFS_Node *dirNode, const char *Name); - int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data); -Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); -// --- Helpers - int FDD_int_ReadWriteWithinTrack(int Disk, int Track, int bWrite, size_t Offset, size_t Length, void *Buffer); - -// === GLOBALS === -MODULE_DEFINE(0, FDD_VERSION, Storage_FDDv2, FDD_Install, NULL, "x86_ISADMA", NULL); -tDrive gaFDD_Disks[MAX_DISKS]; -tVFS_Node gaFDD_DiskNodes[MAX_DISKS]; -tVFS_NodeType gFDD_RootNodeType = { - .TypeName = "FDD Root Node", - .ReadDir = FDD_ReadDir, - .FindDir = FDD_FindDir, - .IOCtl = FDD_IOCtl - }; -tVFS_NodeType gFDD_DevNodeType = { - .TypeName = "FDD Device", - .Read = FDD_ReadFS, - .Write = NULL // FDD_WriteFS? - }; -tDevFS_Driver gFDD_DriverInfo = { - NULL, "fdd", - { - .Size = -1, - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Flags = VFS_FFLAG_DIRECTORY, - .Type = &gFDD_RootNodeType - } - }; - -// === CODE === -int FDD_Install(char **Arguments) -{ - // Query CMOS memory - { - Uint8 data; - outb(0x70, 0x10); - data = inb(0x71); - - // NOTE: CMOS only reports 2 disks - if( (data & 0xF0) == 0x40 ) - gaFDD_Disks[0].bValid = gaFDD_Disks[0].bInserted = 1; - if( (data & 0x0F) == 0x04 ) - gaFDD_Disks[1].bValid = gaFDD_Disks[1].bInserted = 1; - - if( gaFDD_Disks[0].bValid == 0 && gaFDD_Disks[1].bValid == 0 ) - return MODULE_ERR_NOTNEEDED; - } - - // Initialise controller - FDD_SetupIO(); - - FDD_RegisterFS(); - - return 0; -} - -/** - * \brief Register the FDD driver with DevFS - */ -int FDD_RegisterFS(void) -{ - gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime - = gFDD_DriverInfo.RootNode.ATime = now(); - - for( int i = 0; i < MAX_DISKS; i ++ ) - { - if( !gaFDD_Disks[i].bValid ) continue ; - - // Initialise Child Nodes - gaFDD_DiskNodes[i].Inode = i; - gaFDD_DiskNodes[i].Flags = 0; - gaFDD_DiskNodes[i].NumACLs = 0; - gaFDD_DiskNodes[i].Type = &gFDD_DevNodeType; - gaFDD_DiskNodes[i].Size = 1440*1024; // TODO: Non 1.44 disks - } - - DevFS_AddDevice( &gFDD_DriverInfo ); - return 0; -} - -/** - * \brief Get the name of the \a Pos th item in the driver root - * \param Node Root node (unused) - * \param Pos Position - * \return Heap string of node name - */ -char *FDD_ReadDir(tVFS_Node *Node, int Pos) -{ - char ret_tpl[2]; - if(Pos < 0 || Pos > MAX_DISKS ) - return NULL; - if(gaFDD_Disks[Pos].bValid) - return VFS_SKIP; - - ret_tpl[0] = '0' + Pos; - ret_tpl[1] = '\0'; - return strdup(ret_tpl); -} - -/** - * \brief Get a node by name - * \param Node Root node (unused) - * \param Name Drive name - * \return Pointer to node structure - */ -tVFS_Node *FDD_FindDir(tVFS_Node *Node, const char *Name) -{ - int pos; - if( '0' > Name[0] || Name[0] > '9' ) return NULL; - if( Name[1] != '\0' ) return NULL; - - pos = Name[0] - '0'; - - return &gaFDD_DiskNodes[pos]; -} - -static const char *casIOCTLS[] = {DRV_IOCTLNAMES,DRV_DISK_IOCTLNAMES,NULL}; -/** - * \brief Driver root IOCtl Handler - * \param Node Root node (unused) - * \param ID IOCtl ID - * \param Data IOCtl specific data pointer - */ -int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - switch(ID) - { - BASE_IOCTLS(DRV_TYPE_DISK, "FDDv2", FDD_VERSION, casIOCTLS); - - case DISK_IOCTL_GETBLOCKSIZE: return 512; - - default: - return -1; - } -} - -/** - * \brief Read from a disk - * \param Node Disk node - * \param Offset Byte offset in disk - * \param Length Number of bytes to read - * \param Buffer Destination buffer - * \return Number of bytes read - */ -Uint64 FDD_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) -{ - int disk = Node->Inode; - int track; - int rem_len; - char *dest = Buffer; - - ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - - if( Offset > Node->Size ) LEAVE_RET('i', 0); - if( Length > Node->Size ) Length = Node->Size; - if( Offset + Length > Node->Size ) - Length = Node->Size - Offset; - if( Length == 0 ) return 0; - - rem_len = Length; - - track = Offset / BYTES_PER_TRACK; - Offset %= BYTES_PER_TRACK; - - if( Offset ) - { - int len; - if(rem_len > BYTES_PER_TRACK - Offset) - len = BYTES_PER_TRACK - Offset; - else - len = rem_len; - FDD_int_ReadWriteWithinTrack(disk, track, 0, Offset, len, dest); - dest += len; - rem_len -= len; - track ++; - } - - if( rem_len > 0) - { - while( rem_len > BYTES_PER_TRACK ) - { - FDD_int_ReadWriteWithinTrack(disk, track, 0, 0, BYTES_PER_TRACK, dest); - dest += BYTES_PER_TRACK; - rem_len -= BYTES_PER_TRACK; - track ++; - } - - FDD_int_ReadWriteWithinTrack(disk, track, 0, 0, rem_len, dest); - } - - LEAVE('X', Length); - return Length; -} - -/** - * \brief Read from a track - */ -int FDD_int_ReadWriteWithinTrack(int Disk, int Track, int bWrite, size_t Offset, size_t Length, void *Buffer) -{ - if( Offset > BYTES_PER_TRACK || Length > BYTES_PER_TRACK ) - return 1; - if( Offset + Length > BYTES_PER_TRACK ) - return 1; - - if( Length == 0 ) - return 0; - - ENTER("iDisk iTrack bbWrite xOffset xLength pBuffer", - Disk, Track, bWrite, Offset, Length, Buffer); - - Mutex_Acquire( &gaFDD_Disks[Disk].Mutex ); - - // If the cache doesn't exist, create it - if( !gaFDD_Disks[Disk].TrackData[Track] ) - { - gaFDD_Disks[Disk].TrackData[Track] = malloc( BYTES_PER_TRACK ); - // Don't bother reading if this is a whole track write - if( !(bWrite && Offset == 0 && Length == BYTES_PER_TRACK) ) - { - LOG("Reading track"); - FDD_int_ReadWriteTrack(Disk, Track, 0, gaFDD_Disks[Disk].TrackData[Track]); - } - } - - // Read/Write - if( bWrite ) - { - // Write to cache then commit cache to disk - char *dest = gaFDD_Disks[Disk].TrackData[Track]; - LOG("Write to cache"); - memcpy( dest + Offset, Buffer, Length ); - FDD_int_ReadWriteTrack(Disk, Track, 1, gaFDD_Disks[Disk].TrackData[Track]); - } - else - { - // Read from cache - char *src = gaFDD_Disks[Disk].TrackData[Track]; - LOG("Read from cache"); - memcpy(Buffer, src + Offset, Length); - } - - Mutex_Release( &gaFDD_Disks[Disk].Mutex ); - - LEAVE('i', 0); - return 0; -} diff --git a/Modules/Storage/Makefile.tpl b/Modules/Storage/Makefile.tpl deleted file mode 100644 index d5291f69..00000000 --- a/Modules/Storage/Makefile.tpl +++ /dev/null @@ -1,3 +0,0 @@ -CATEGORY = Storage - --include ../../Makefile.tpl diff --git a/Modules/USB/Core/Makefile b/Modules/USB/Core/Makefile deleted file mode 100644 index 4d6e6845..00000000 --- a/Modules/USB/Core/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# -# - -OBJ = main.o -OBJ += usb.o usb_lowlevel.o usb_devinit.o usb_io.o usb_poll.o -OBJ += hub.o -CPPFLAGS = -Iinclude -NAME = Core - --include ../Makefile.tpl diff --git a/Modules/USB/Core/hub.c b/Modules/USB/Core/hub.c deleted file mode 100644 index 849e8a3f..00000000 --- a/Modules/USB/Core/hub.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Acess2 USB Stack - * - By John Hodge (thePowersGang) - * - * hub.c - * - Basic hub driver - */ -#define DEBUG 1 -#include - -#define MAX_PORTS 32 // Not actually a max, but used for DeviceRemovable - - -#define GET_STATUS 0 -#define CLEAR_FEATURE 1 -// resvd -#define SET_FEATURE 3 - -#define PORT_CONNECTION 0 -#define PORT_ENABLE 1 -#define PORT_SUSPEND 2 -#define PORT_OVER_CURRENT 3 -#define PORT_RESET 4 -#define PORT_POWER 8 -#define PORT_LOW_SPEED 9 -#define C_PORT_CONNECTION 16 -#define C_PORT_ENABLE 17 -#define C_PORT_SUSPEND 18 -#define C_PORT_OVER_CURRENT 19 -#define C_PORT_RESET 20 -#define PORT_TEST 21 -#define PORT_INDICATOR 21 - -struct sHubDescriptor -{ - Uint8 DescLength; - Uint8 DescType; // = 0x29 - Uint8 NbrPorts; - Uint16 HubCharacteristics; - Uint8 PwrOn2PwrGood; // 2 ms intervals - Uint8 HubContrCurrent; // Max internal current (mA) - Uint8 DeviceRemovable[MAX_PORTS]; -}; - -struct sHubInfo -{ - tUSBHub *HubPtr; - int PowerOnDelay; // in ms - int nPorts; - Uint8 DeviceRemovable[]; -}; - -// === PROTOTYPES === -void Hub_Connected(tUSBInterface *Dev); -void Hub_Disconnected(tUSBInterface *Dev); -void Hub_PortStatusChange(tUSBInterface *Dev, int Endpoint, int Length, void *Data); -void Hub_int_HandleChange(tUSBInterface *Dev, int Port); - -// === GLOBALS === -tUSBDriver gUSBHub_Driver = { - .Name = "Hub", - .Match = {.Class = {0x090000, 0xFF0000}}, - .Connected = Hub_Connected, - .Disconnected = Hub_Disconnected, - .MaxEndpoints = 1, - .Endpoints = { - {0x83, Hub_PortStatusChange} - } -}; - -// === CODE === -#if 0 -int Hub_DriverInitialise(char **Arguments) -{ - USB_RegisterDriver( &gUSBHub_Driver ); - return 0; -} -#endif - -void Hub_Connected(tUSBInterface *Dev) -{ - struct sHubDescriptor hub_desc; - struct sHubInfo *info; - - // Read hub descriptor (Class descriptor 0x29) - USB_ReadDescriptor(Dev, 0x129, 0, sizeof(hub_desc), &hub_desc); - - LOG("%i Ports", hub_desc.NbrPorts); - LOG("Takes %i ms for power to stabilise", hub_desc.PwrOn2PwrGood*2); - - // Allocate infomation structure - info = malloc(sizeof(*info) + (hub_desc.NbrPorts+7)/8); - if(!info) { - Log_Error("USBHub", "malloc() failed"); - return ; - } - USB_SetDeviceDataPtr(Dev, info); - - // Fill data - info->nPorts = hub_desc.NbrPorts; - info->PowerOnDelay = hub_desc.PwrOn2PwrGood * 2; - memcpy(info->DeviceRemovable, hub_desc.DeviceRemovable, (hub_desc.NbrPorts+7)/8); - // Register - info->HubPtr = USB_RegisterHub(Dev, info->nPorts); - - // Register poll on endpoint - USB_StartPollingEndpoint(Dev, 1); -} - -void Hub_Disconnected(tUSBInterface *Dev) -{ - struct sHubInfo *info = USB_GetDeviceDataPtr(Dev); - USB_RemoveHub(info->HubPtr); - free(info); -} - -void Hub_PortStatusChange(tUSBInterface *Dev, int Endpoint, int Length, void *Data) -{ - Uint8 *status = Data; - struct sHubInfo *info = USB_GetDeviceDataPtr(Dev); - int i; - for( i = 0; i < info->nPorts; i += 8, status ++ ) - { - if( i/8 >= Length ) break; - if( *status == 0 ) continue; - - for( int j = 0; j < 8; j ++ ) - if( *status & (1 << j) ) - Hub_int_HandleChange(Dev, i+j); - } -} - -void Hub_int_HandleChange(tUSBInterface *Dev, int Port) -{ - struct sHubInfo *info = USB_GetDeviceDataPtr(Dev); - Uint16 status[2]; // Status, Change - - // Get port status - USB_Request(Dev, 0, 0xA3, GET_STATUS, 0, Port, 4, status); - - LOG("Port %i: status = {0b%b, 0b%b}", Port, status[0], status[1]); - - // Handle connections / disconnections - if( status[1] & 0x0001 ) - { - if( status[0] & 0x0001 ) { - // Connected - // - Power on port - USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_POWER, Port, 0, NULL); - Time_Delay(info->PowerOnDelay); - // - Reset - USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_RESET, Port, 0, NULL); - Time_Delay(20); // Spec says 10ms after reset, but how long is reset? - // - Enable - USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_ENABLE, Port, 0, NULL); - // - Poke USB Stack - USB_DeviceConnected(info->HubPtr, Port); - } - else { - // Disconnected - USB_DeviceDisconnected(info->HubPtr, Port); - } - - USB_Request(Dev, 0, 0x23, CLEAR_FEATURE, C_PORT_CONNECTION, Port, 0, NULL); - } - - // Reset change - if( status[1] & 0x0010 ) - { - if( status[0] & 0x0010 ) { - // Reset complete - } - else { - // Useful? - } - // ACK - USB_Request(Dev, 0, 0x23, CLEAR_FEATURE, C_PORT_RESET, Port, 0, NULL); - } -} diff --git a/Modules/USB/Core/include/usb_core.h b/Modules/USB/Core/include/usb_core.h deleted file mode 100644 index 43af16f2..00000000 --- a/Modules/USB/Core/include/usb_core.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Acess2 USB Stack - * - By John Hodge (thePowersGang) - * - * usb_core.h - * - Core USB IO Header - */ -#ifndef _USB_CORE_H_ -#define _USB_CORE_H_ - -#include - -typedef struct sUSBInterface tUSBInterface; -typedef struct sUSBDriver tUSBDriver; - -typedef void (*tUSB_DataCallback)(tUSBInterface *Dev, int EndPt, int Length, void *Data); - -/** - */ -struct sUSBDriver -{ - tUSBDriver *Next; - - const char *Name; - - int MatchType; // 0: Interface, 1: Device, 2: Vendor - union { - struct { - // 23:16 - Interface Class - // 15:8 - Interface Sub Class - // 7:0 - Interface Protocol - Uint32 ClassCode; - Uint32 ClassMask; - } Class; - struct { - Uint16 VendorID; - Uint16 DeviceID; - } VendorDev; - } Match; - - void (*Connected)(tUSBInterface *Dev); - void (*Disconnected)(tUSBInterface *Dev); - - int MaxEndpoints; - struct { - // USB Attrbute byte - // NOTE: Top bit indicates the direction (1=Input) - Uint8 Attributes; - // Data availiable Callback - tUSB_DataCallback DataAvail; - } Endpoints[]; -}; - -extern void *USB_GetDeviceDataPtr(tUSBInterface *Dev); -extern void USB_SetDeviceDataPtr(tUSBInterface *Dev, void *Ptr); - -extern void USB_StartPollingEndpoint(tUSBInterface *Dev, int Endpoint); -extern void USB_ReadDescriptor(tUSBInterface *Dev, int Type, int Index, int Length, void *Data); -extern void USB_Request(tUSBInterface *Dev, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data); -// TODO: Async -extern void USB_SendData(tUSBInterface *Dev, int Endpoint, int Length, void *Data); -extern void USB_RecvData(tUSBInterface *Dev, int Endpoint, int Length, void *Data); -extern void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf, tUSB_DataCallback Callback); - -#endif - diff --git a/Modules/USB/Core/include/usb_host.h b/Modules/USB/Core/include/usb_host.h deleted file mode 100644 index c5f3e39d..00000000 --- a/Modules/USB/Core/include/usb_host.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Acess2 USB Stack - * - By John Hodge (thePowersGang) - * - * usb_host.h - * - USB Host Controller Interface - */ -#ifndef _USB_HOST_H_ -#define _USB_HOST_H_ - -#include "usb_core.h" -#include "usb_hub.h" - -typedef struct sUSBHostDef tUSBHostDef; - -typedef void (*tUSBHostCb)(void *DataPtr, void *Data, int Length); - -typedef void *(*tUSBHostOp)(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb CB, void *CbData, void *Data, size_t Length); - -/** - * \brief Defines a USB Host Controller type - */ -struct sUSBHostDef -{ - tUSBHostOp SendIN; - tUSBHostOp SendOUT; - tUSBHostOp SendSETUP; - - int (*IsOpComplete)(void *Ptr, void *OpPtr); - - void (*CheckPorts)(void *Ptr); -}; - -extern tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts); - -#endif - diff --git a/Modules/USB/Core/include/usb_hub.h b/Modules/USB/Core/include/usb_hub.h deleted file mode 100644 index 6b32d49c..00000000 --- a/Modules/USB/Core/include/usb_hub.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Acess2 USB Stack - * - By John Hodge (thePowersGang) - * - * usb_hub.h - * - Core Hub Definitions - */ -#ifndef _USB_HUB_H_ -#define _USB_HUB_H_ - -#include "usb_core.h" - -typedef struct sUSBHub tUSBHub; - -/** - * \brief Register a device as a hub - * - * Used by the hub class initialisation routine. - */ -extern tUSBHub *USB_RegisterHub(tUSBInterface *Device, int nPorts); -extern void USB_RemoveHub(tUSBHub *Hub); - -extern void USB_DeviceConnected(tUSBHub *Hub, int Port); -extern void USB_DeviceDisconnected(tUSBHub *Hub, int Port); - -#endif - diff --git a/Modules/USB/Core/main.c b/Modules/USB/Core/main.c deleted file mode 100644 index df7f174f..00000000 --- a/Modules/USB/Core/main.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Acess2 - * USB Stack - */ -#define VERSION ( (0<<8)| 5 ) -#define DEBUG 1 -#include -#include -#include -#include -#include "usb.h" - -// === IMPORTS === -extern void USB_PollThread(void *unused); -extern void USB_AsyncThread(void *Unused); - -// === PROTOTYPES === - int USB_Install(char **Arguments); -void USB_Cleanup(void); -char *USB_ReadDir(tVFS_Node *Node, int Pos); -tVFS_Node *USB_FindDir(tVFS_Node *Node, const char *Name); - int USB_IOCtl(tVFS_Node *Node, int Id, void *Data); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, USB_Core, USB_Install, NULL, NULL); -tVFS_NodeType gUSB_RootNodeType = { - .ReadDir = USB_ReadDir, - .FindDir = USB_FindDir, - .IOCtl = USB_IOCtl -}; -tDevFS_Driver gUSB_DrvInfo = { - NULL, "usb", { - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Flags = VFS_FFLAG_DIRECTORY, - .Type = &gUSB_RootNodeType - } -}; -tUSBHost *gUSB_Hosts = NULL; - -// === CODE === -/** - * \brief Called once module is loaded - */ -int USB_Install(char **Arguments) -{ - Log_Warning("USB", "Not Complete - Devel Only"); - - Proc_SpawnWorker(USB_PollThread, NULL); - Proc_SpawnWorker(USB_AsyncThread, NULL); - - return MODULE_ERR_OK; -} - -/** - * \brief Called just before module is unloaded - */ -void USB_Cleanup() -{ -} - -/** - * \fn char *USB_ReadDir(tVFS_Node *Node, int Pos) - * \brief Read from the USB root - */ -char *USB_ReadDir(tVFS_Node *Node, int Pos) -{ - return NULL; -} - -/** - * \fn tVFS_Node *USB_FindDir(tVFS_Node *Node, const char *Name) - * \brief Locate an entry in the USB root - */ -tVFS_Node *USB_FindDir(tVFS_Node *Node, const char *Name) -{ - return NULL; -} - -/** - * \brief Handles IOCtl Calls to the USB driver - */ -int USB_IOCtl(tVFS_Node *Node, int Id, void *Data) -{ - return 0; -} diff --git a/Modules/USB/Core/usb.c b/Modules/USB/Core/usb.c deleted file mode 100644 index b3195804..00000000 --- a/Modules/USB/Core/usb.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Acess2 USB Stack - * - By John Hodge (thePowersGang) - * - * usb.c - * - USB Structure - */ -#define DEBUG 1 -#include -#include -#include -#include "usb.h" - -// === IMPORTS === -extern tUSBHost *gUSB_Hosts; -extern tUSBDriver gUSBHub_Driver; - -// === STRUCTURES === - -// === PROTOTYPES === -tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts); - -// === GLOBALS === -tUSBDriver *gpUSB_InterfaceDrivers = &gUSBHub_Driver; - -// === CODE === -tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts) -{ - tUSBHost *host; - - host = malloc(sizeof(tUSBHost) + nPorts*sizeof(void*)); - if(!host) { - // Oh, bugger. - return NULL; - } - host->HostDef = HostDef; - host->Ptr = ControllerPtr; - memset(host->AddressBitmap, 0, sizeof(host->AddressBitmap)); - - host->RootHubDev.ParentHub = NULL; - host->RootHubDev.Host = host; - host->RootHubDev.Address = 0; - -// host->RootHubIf.Next = NULL; - host->RootHubIf.Dev = &host->RootHubDev; - host->RootHubIf.Driver = NULL; - host->RootHubIf.Data = NULL; - host->RootHubIf.nEndpoints = 0; - - host->RootHub.Interface = &host->RootHubIf; - host->RootHub.nPorts = nPorts; - memset(host->RootHub.Devices, 0, sizeof(void*)*nPorts); - - // TODO: Lock - host->Next = gUSB_Hosts; - gUSB_Hosts = host; - - return &host->RootHub; -} - -// --- Drivers --- -void USB_RegisterDriver(tUSBDriver *Driver) -{ - Log_Warning("USB", "TODO: Implement USB_RegisterDriver"); -} - -tUSBDriver *USB_int_FindDriverByClass(Uint32 ClassCode) -{ - ENTER("xClassCode", ClassCode); - for( tUSBDriver *ret = gpUSB_InterfaceDrivers; ret; ret = ret->Next ) - { - LOG(" 0x%x & 0x%x == 0x%x?", ClassCode, ret->Match.Class.ClassMask, ret->Match.Class.ClassCode); - if( (ClassCode & ret->Match.Class.ClassMask) == ret->Match.Class.ClassCode ) - { - LOG("Found '%s'", ret->Name); - LEAVE('p', ret); - return ret; - } - } - LEAVE('n'); - return NULL; -} - -// --- Hub Registration --- -// NOTE: Doesn't do much nowdays -tUSBHub *USB_RegisterHub(tUSBInterface *Device, int PortCount) -{ - tUSBHub *ret; - - ret = malloc(sizeof(tUSBHub) + sizeof(ret->Devices[0])*PortCount); - ret->Interface = Device; - ret->nPorts = PortCount; - memset(ret->Devices, 0, sizeof(ret->Devices[0])*PortCount); - return ret; -} - -void USB_RemoveHub(tUSBHub *Hub) -{ - for( int i = 0; i < Hub->nPorts; i ++ ) - { - if( Hub->Devices[i] ) - { - USB_DeviceDisconnected( Hub, i ); - } - } - free(Hub); -} - diff --git a/Modules/USB/Core/usb.h b/Modules/USB/Core/usb.h deleted file mode 100644 index 5a133630..00000000 --- a/Modules/USB/Core/usb.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Acess2 USB Stack - * - By John Hodge (thePowersGang) - * - * usb.h - * - USB Internal definitions - */ -#ifndef _USB_H_ -#define _USB_H_ - -#include -#include -#include - -typedef struct sUSBHost tUSBHost; -typedef struct sUSBDevice tUSBDevice; -typedef struct sUSBEndpoint tUSBEndpoint; - -// === STRUCTURES === -/** - * \brief USB Hub data - */ -struct sUSBHub -{ - tUSBInterface *Interface; - - int nPorts; - tUSBDevice *Devices[]; -}; - -struct sUSBEndpoint -{ - tUSBEndpoint *Next; // (usb_poll.c) Clock list - tUSBInterface *Interface; - int EndpointIdx; // Interface endpoint index - int EndpointNum; // Device endpoint num - - int PollingPeriod; // In 1ms intervals - int MaxPacketSize; // In bytes - Uint8 Type; // Same as sUSBDriver.Endpoints.Type - - int PollingAtoms; // (usb_poll.c) Period in clock list - void *InputData; -}; - -/** - * \brief Structure for a device's interface - */ -struct sUSBInterface -{ -// tUSBInterface *Next; - tUSBDevice *Dev; - - tUSBDriver *Driver; - void *Data; - - int nEndpoints; - tUSBEndpoint Endpoints[]; -}; - -/** - * \brief Defines a single device on the USB Bus - */ -struct sUSBDevice -{ - tUSBHub *ParentHub; - - /** - * \brief Host controller used - */ - tUSBHost *Host; - int Address; - - int nInterfaces; - tUSBInterface *Interfaces[]; -}; - -struct sUSBHost -{ - struct sUSBHost *Next; - - tUSBHostDef *HostDef; - void *Ptr; - - Uint8 AddressBitmap[128/8]; - - tUSBDevice RootHubDev; - tUSBInterface RootHubIf; - tUSBHub RootHub; -}; - -extern tUSBDriver *USB_int_FindDriverByClass(Uint32 ClassCode); - -#endif diff --git a/Modules/USB/Core/usb_devinit.c b/Modules/USB/Core/usb_devinit.c deleted file mode 100644 index d01d7d1c..00000000 --- a/Modules/USB/Core/usb_devinit.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Acess 2 USB Stack - * - By John Hodge (thePowersGang) - * - * usb_devinit.c - * - USB Device Initialisation - */ -#define DEBUG 1 - -#include -#include -#include -#include "usb.h" -#include "usb_proto.h" -#include "usb_lowlevel.h" - -// === PROTOTYPES === -void USB_DeviceConnected(tUSBHub *Hub, int Port); -void USB_DeviceDisconnected(tUSBHub *Hub, int Port); -void *USB_GetDeviceDataPtr(tUSBInterface *Dev); -void USB_SetDeviceDataPtr(tUSBInterface *Dev, void *Ptr); - int USB_int_AllocateAddress(tUSBHost *Host); - -// === CODE === -void USB_DeviceConnected(tUSBHub *Hub, int Port) -{ - tUSBDevice tmpdev; - tUSBDevice *dev = &tmpdev; - if( Port >= Hub->nPorts ) return ; - if( Hub->Devices[Port] ) return ; - - ENTER("pHub iPort", Hub, Port); - - // Device should be in 'Default' state - - // Create structure - dev->ParentHub = Hub; - dev->Host = Hub->Interface->Dev->Host; - dev->Address = 0; - - // 1. Assign an address - dev->Address = USB_int_AllocateAddress(dev->Host); - if(dev->Address == 0) { - Log_Error("USB", "No addresses avaliable on host %p", dev->Host); - free(dev); - LEAVE('-'); - return ; - } - USB_int_SendSetupSetAddress(dev->Host, dev->Address); - LOG("Assigned address %i", dev->Address); - - // 2. Get device information - { - struct sDescriptor_Device desc; - LOG("Getting device descriptor"); - // Endpoint 0, Desc Type 1, Index 0 - USB_int_ReadDescriptor(dev, 0, 1, 0, sizeof(desc), &desc); - - LOG("Device Descriptor = {"); - LOG(" .Length = %i", desc.Length); - LOG(" .Type = %i", desc.Type); - LOG(" .USBVersion = 0x%04x", desc.USBVersion); - LOG(" .DeviceClass = 0x%02x", desc.DeviceClass); - LOG(" .DeviceSubClass = 0x%02x", desc.DeviceSubClass); - LOG(" .DeviceProtocol = 0x%02x", desc.DeviceProtocol); - LOG(" .MaxPacketSize = 0x%02x", desc.MaxPacketSize); - LOG(" .VendorID = 0x%04x", desc.VendorID); - LOG(" .ProductID = 0x%04x", desc.ProductID); - LOG(" .DeviceID = 0x%04x", desc.DeviceID); - LOG(" .ManufacturerStr = Str %i", desc.ManufacturerStr); - LOG(" .ProductStr = Str %i", desc.ProductStr); - LOG(" .SerialNumberStr = Str %i", desc.SerialNumberStr); - LOG(" .NumConfigurations = %i", desc.SerialNumberStr); - LOG("}"); - - if( desc.ManufacturerStr ) - { - char *tmp = USB_int_GetDeviceString(dev, 0, desc.ManufacturerStr); - LOG("ManufacturerStr = '%s'", tmp); - free(tmp); - } - if( desc.ProductStr ) - { - char *tmp = USB_int_GetDeviceString(dev, 0, desc.ProductStr); - LOG("ProductStr = '%s'", tmp); - free(tmp); - } - if( desc.SerialNumberStr ) - { - char *tmp = USB_int_GetDeviceString(dev, 0, desc.SerialNumberStr); - LOG("SerialNumbertStr = '%s'", tmp); - free(tmp); - } - } - - // TODO: Support alternate configurations - - // 3. Get configurations - for( int i = 0; i < 1; i ++ ) - { - struct sDescriptor_Configuration desc; - void *full_buf; - char *cur_ptr; - - USB_int_ReadDescriptor(dev, 0, 2, i, sizeof(desc), &desc); - LOG("Configuration Descriptor %i = {", i); - LOG(" .Length = %i", desc.Length); - LOG(" .Type = %i", desc.Type); - LOG(" .TotalLength = 0x%x", LittleEndian16(desc.TotalLength)); - LOG(" .NumInterfaces = %i", desc.NumInterfaces); - LOG(" .ConfigurationValue = %i", desc.ConfigurationValue); - LOG(" .ConfigurationStr = %i", desc.ConfigurationStr); - LOG(" .AttributesBmp = 0b%b", desc.AttributesBmp); - LOG(" .MaxPower = %i (*2mA)", desc.MaxPower); - LOG("}"); - if( desc.ConfigurationStr ) { - char *tmp = USB_int_GetDeviceString(dev, 0, desc.ConfigurationStr); - LOG("ConfigurationStr = '%s'", tmp); - free(tmp); - } - - // TODO: Split here and allow some method of selection - - // Allocate device now that we have the configuration - dev = malloc(sizeof(tUSBDevice) + desc.NumInterfaces * sizeof(void*)); - memcpy(dev, &tmpdev, sizeof(tUSBDevice)); - dev->nInterfaces = desc.NumInterfaces; - - // Allocate a temp buffer for config info - cur_ptr = full_buf = malloc( LittleEndian16(desc.TotalLength) ); - USB_int_ReadDescriptor(dev, 0, 2, i, desc.TotalLength, full_buf); - - cur_ptr += desc.Length; - - // TODO: Interfaces - for( int j = 0; j < desc.NumInterfaces; j ++ ) - { - struct sDescriptor_Interface *iface; - tUSBInterface *dev_if; - iface = (void*)cur_ptr; - // TODO: Sanity check with remaining space - cur_ptr += sizeof(*iface); - - LOG("Interface %i/%i = {", i, j); - LOG(" .InterfaceNum = %i", iface->InterfaceNum); - LOG(" .NumEndpoints = %i", iface->NumEndpoints); - LOG(" .InterfaceClass = 0x%x", iface->InterfaceClass); - LOG(" .InterfaceSubClass = 0x%x", iface->InterfaceSubClass); - LOG(" .InterfaceProcol = 0x%x", iface->InterfaceProtocol); - - if( iface->InterfaceStr ) { - char *tmp = USB_int_GetDeviceString(dev, 0, iface->InterfaceStr); - LOG(" .InterfaceStr = %i '%s'", iface->InterfaceStr, tmp); - free(tmp); - } - LOG("}"); - - dev_if = malloc(sizeof(tUSBInterface) + iface->NumEndpoints*sizeof(dev_if->Endpoints[0])); - dev_if->Dev = dev; - dev_if->Driver = NULL; - dev_if->Data = NULL; - dev_if->nEndpoints = iface->NumEndpoints; - dev->Interfaces[j] = dev_if; - - // Copy interface data - for( int k = 0; k < iface->NumEndpoints; k ++ ) - { - struct sDescriptor_Endpoint *endpt; - endpt = (void*)cur_ptr; - // TODO: Sanity check with remaining space - cur_ptr += sizeof(*endpt); - - LOG("Endpoint %i/%i/%i = {", i, j, k); - LOG(" .Address = 0x%2x", endpt->Address); - LOG(" .Attributes = 0b%8b", endpt->Attributes); - LOG(" .MaxPacketSize = %i", LittleEndian16(endpt->MaxPacketSize)); - LOG(" .PollingInterval = %i", endpt->PollingInterval); - LOG("}"); - - dev_if->Endpoints[k].Next = NULL; - dev_if->Endpoints[k].Interface = dev_if; - dev_if->Endpoints[k].EndpointIdx = k; - dev_if->Endpoints[k].EndpointNum = endpt->Address & 0x7F; - dev_if->Endpoints[k].PollingPeriod = endpt->PollingInterval; - dev_if->Endpoints[k].MaxPacketSize = LittleEndian16(endpt->MaxPacketSize); - dev_if->Endpoints[k].Type = endpt->Attributes | (endpt->Address & 0x80); - dev_if->Endpoints[k].PollingAtoms = 0; - dev_if->Endpoints[k].InputData = NULL; - } - - // Initialise driver - dev_if->Driver = USB_int_FindDriverByClass( - ((int)iface->InterfaceClass << 16) - |((int)iface->InterfaceSubClass << 8) - |((int)iface->InterfaceProtocol << 0) - ); - if(!dev_if->Driver) { - Log_Notice("USB", "No driver for Class %02x:%02x:%02x", - iface->InterfaceClass, iface->InterfaceSubClass, iface->InterfaceProtocol - ); - } - else { - dev_if->Driver->Connected( dev_if ); - } - } - - free(full_buf); - } - - // Done. - LEAVE('-'); -} - -void USB_DeviceDisconnected(tUSBHub *Hub, int Port) -{ - -} - -void *USB_GetDeviceDataPtr(tUSBInterface *Dev) { return Dev->Data; } -void USB_SetDeviceDataPtr(tUSBInterface *Dev, void *Ptr) { Dev->Data = Ptr; } - -int USB_int_AllocateAddress(tUSBHost *Host) -{ - int i; - for( i = 1; i < 128; i ++ ) - { - if(Host->AddressBitmap[i/8] & (1 << (i%8))) - continue ; - Host->AddressBitmap[i/8] |= 1 << (i%8); - return i; - } - return 0; -} - -void USB_int_DeallocateAddress(tUSBHost *Host, int Address) -{ - Host->AddressBitmap[Address/8] &= ~(1 << (Address%8)); -} - diff --git a/Modules/USB/Core/usb_io.c b/Modules/USB/Core/usb_io.c deleted file mode 100644 index 25ff59d2..00000000 --- a/Modules/USB/Core/usb_io.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Acess2 USB Stack - * - By John Hodge (thePowersGang) - * - * usb_io.c - * - High-level IO - */ -#define DEBUG 0 - -#include -#include "usb.h" -#include "usb_lowlevel.h" -#include - -typedef struct sAsyncOp tAsyncOp; - -struct sAsyncOp -{ - tAsyncOp *Next; - tUSBEndpoint *Endpt; - int Length; - void *Data; -}; - -// === PROTOTYPES === -void USB_ReadDescriptor(tUSBInterface *Iface, int Type, int Index, int Length, void *Data); -void USB_Request(tUSBInterface *Iface, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data); -void USB_AsyncCallback(void *Ptr, void *Buf, int Length); -void USB_AsyncThread(void *unused); - -// === GLOBALS === -tWorkqueue gUSB_AsyncQueue; - -// === CODE === -void USB_ReadDescriptor(tUSBInterface *Iface, int Type, int Index, int Length, void *Data) -{ - USB_int_ReadDescriptor(Iface->Dev, 0, Type, Index, Length, Data); -} - -void USB_Request(tUSBInterface *Iface, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data) -{ - int endpt; - - // Sanity check - if(Endpoint < 0 || Endpoint >= Iface->nEndpoints) - return ; - - // Get endpoint number - if(Endpoint) - endpt = Iface->Endpoints[Endpoint-1].EndpointNum; - else - endpt = 0; - - USB_int_Request(Iface->Dev->Host, Iface->Dev->Address, endpt, Type, Req, Value, Index, Len, Data); -} - - -void USB_SendData(tUSBInterface *Dev, int Endpoint, int Length, void *Data) -{ - Log_Warning("USB", "TODO: Implement USB_SendData"); -} - -void USB_RecvData(tUSBInterface *Dev, int Endpoint, int Length, void *Data) -{ - Log_Warning("USB", "TODO: Implement USB_RecvData"); -} - -void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf, tUSB_DataCallback Callback) -{ - tAsyncOp *op; - tUSBHost *host; - - ENTER("pDev iEndpoint iLength pDataBuf", Dev, Endpoint, Length, DataBuf); - - op = malloc(sizeof(*op)); - op->Next = NULL; - op->Endpt = &Dev->Endpoints[Endpoint-1]; - op->Length = Length; - op->Data = DataBuf; - - // TODO: Handle transfers that are larger than one packet - - host = Dev->Dev->Host; - LOG("IN from %p %i:%i", host->Ptr, Dev->Dev->Address, op->Endpt->EndpointNum); - host->HostDef->SendIN( - host->Ptr, Dev->Dev->Address, op->Endpt->EndpointNum, - 0, USB_AsyncCallback, op, - DataBuf, Length - ); - - LEAVE('-'); - -// Log_Warning("USB", "TODO: Implement USB_RecvDataA"); -} - -void USB_AsyncCallback(void *Ptr, void *Buf, int Length) -{ - tAsyncOp *op = Ptr; - op->Length = Length; - LOG("adding %p to work queue", op); - Workqueue_AddWork(&gUSB_AsyncQueue, op); -} - -void USB_AsyncThread(void *Unused) -{ - Threads_SetName("USB Async IO Thread"); - for(;;) - { - tAsyncOp *op = Workqueue_GetWork(&gUSB_AsyncQueue); - tUSBInterface *iface = op->Endpt->Interface; - - LOG("op = %p", op); - - iface->Driver->Endpoints[op->Endpt->EndpointIdx].DataAvail( - iface, op->Endpt->EndpointIdx, - op->Length, op->Data); - } -} - diff --git a/Modules/USB/Core/usb_lowlevel.c b/Modules/USB/Core/usb_lowlevel.c deleted file mode 100644 index 0e53bad9..00000000 --- a/Modules/USB/Core/usb_lowlevel.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Acess 2 USB Stack - * - By John Hodge (thePowersGang) - * - * usb_lowlevel.c - * - Low Level IO - */ -#define DEBUG 1 -#include -#include "usb.h" -#include "usb_proto.h" -#include "usb_lowlevel.h" - -// === PROTOTYPES === -void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data); - int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address); - int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest); -char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index); - int _UTF16to8(Uint16 *Input, int InputLen, char *Dest); - -// === CODE === -void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data) -{ - void *hdl; - // TODO: Sanity check (and check that Type is valid) - struct sDeviceRequest req; - req.ReqType = Type; - req.Request = Req; - req.Value = LittleEndian16( Val ); - req.Index = LittleEndian16( Indx ); - req.Length = LittleEndian16( Len ); - - hdl = Host->HostDef->SendSETUP(Host->Ptr, Addr, EndPt, 0, NULL, NULL, &req, sizeof(req)); - - // TODO: Data toggle? - // TODO: Multi-packet transfers - if( Type & 0x80 ) - { - void *hdl2; - - hdl = Host->HostDef->SendIN(Host->Ptr, Addr, EndPt, 0, NULL, NULL, Data, Len); - - hdl2 = Host->HostDef->SendOUT(Host->Ptr, Addr, EndPt, 0, NULL, NULL, NULL, 0); - while( Host->HostDef->IsOpComplete(Host->Ptr, hdl2) == 0 ) - Time_Delay(1); - } - else - { - void *hdl2; - - if( Len > 0 ) - hdl = Host->HostDef->SendOUT(Host->Ptr, Addr, EndPt, 0, NULL, NULL, Data, Len); - else - hdl = NULL; - - // Status phase (DataToggle=1) - hdl2 = Host->HostDef->SendIN(Host->Ptr, Addr, EndPt, 1, NULL, NULL, NULL, 0); - while( Host->HostDef->IsOpComplete(Host->Ptr, hdl2) == 0 ) - Time_Delay(1); - } - return hdl; -} - -int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address) -{ - USB_int_Request(Host, 0, 0, 0x00, 5, Address & 0x7F, 0, 0, NULL); - return 0; -} - -int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest) -{ - const int ciMaxPacketSize = 0x400; - struct sDeviceRequest req; - int bToggle = 0; - void *final; - - req.ReqType = 0x80; - switch( Type & 0xF00 ) - { - case 0x000: req.ReqType |= (0 << 5); break; // Standard - case 0x100: req.ReqType |= (1 << 5); break; // Class - case 0x200: req.ReqType |= (2 << 5); break; // Vendor - } - - req.Request = 6; // GET_DESCRIPTOR - req.Value = LittleEndian16( ((Type & 0xFF) << 8) | (Index & 0xFF) ); - req.Index = LittleEndian16( 0 ); // TODO: Language ID - req.Length = LittleEndian16( Length ); - - Dev->Host->HostDef->SendSETUP( - Dev->Host->Ptr, Dev->Address, Endpoint, - 0, NULL, NULL, - &req, sizeof(req) - ); - - bToggle = 1; - while( Length > ciMaxPacketSize ) - { - Dev->Host->HostDef->SendIN( - Dev->Host->Ptr, Dev->Address, Endpoint, - bToggle, NULL, NULL, - Dest, ciMaxPacketSize - ); - bToggle = !bToggle; - Length -= ciMaxPacketSize; - } - - final = Dev->Host->HostDef->SendIN( - Dev->Host->Ptr, Dev->Address, Endpoint, - bToggle, INVLPTR, NULL, - Dest, Length - ); - - while( Dev->Host->HostDef->IsOpComplete(Dev->Host->Ptr, final) == 0 ) - Time_Delay(1); - - return 0; -} - -char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index) -{ - struct sDescriptor_String str; - int src_len, new_len; - char *ret; - - if(Index == 0) return strdup(""); - - USB_int_ReadDescriptor(Dev, Endpoint, 3, Index, sizeof(str), &str); - if(str.Length < 2) { - Log_Error("USB", "String %p:%i:%i:%i descriptor is undersized (%i)", - Dev->Host, Dev->Address, Endpoint, Index, str.Length); - return NULL; - } -// if(str.Length > sizeof(str)) { -// // IMPOSSIBLE! -// Log_Error("USB", "String is %i bytes, which is over prealloc size (%i)", -// str.Length, sizeof(str) -// ); -// } - src_len = (str.Length - 2) / sizeof(str.Data[0]); - - LOG("&str = %p, src_len = %i", &str, src_len); - - new_len = _UTF16to8(str.Data, src_len, NULL); - ret = malloc( new_len + 1 ); - _UTF16to8(str.Data, src_len, ret); - ret[new_len] = 0; - return ret; -} - -int _UTF16to8(Uint16 *Input, int InputLen, char *Dest) -{ - int str_len, cp_len; - Uint32 saved_bits = 0; - str_len = 0; - for( int i = 0; i < InputLen; i ++) - { - Uint32 cp; - Uint16 val = Input[i]; - if( val >= 0xD800 && val <= 0xDBFF ) - { - // Multibyte - Leading - if(i + 1 > InputLen) { - cp = '?'; - } - else { - saved_bits = (val - 0xD800) << 10; - saved_bits += 0x10000; - continue ; - } - } - else if( val >= 0xDC00 && val <= 0xDFFF ) - { - if( !saved_bits ) { - cp = '?'; - } - else { - saved_bits |= (val - 0xDC00); - cp = saved_bits; - } - } - else - cp = val; - - cp_len = WriteUTF8((Uint8*)Dest, cp); - if(Dest) - Dest += cp_len; - str_len += cp_len; - - saved_bits = 0; - } - - return str_len; -} - diff --git a/Modules/USB/Core/usb_lowlevel.h b/Modules/USB/Core/usb_lowlevel.h deleted file mode 100644 index 9159cbac..00000000 --- a/Modules/USB/Core/usb_lowlevel.h +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Acess2 USB Stack - * - By John Hodge (thePowersGang) - * - * usb_lowlevel.h - * - Low-Level USB IO Functions - */ -#ifndef _USB_LOWLEVEL_H_ -#define _USB_LOWLEVEL_H_ - -extern void *USB_int_Request(tUSBHost *Host, int Addr, int EndPt, int Type, int Req, int Val, int Indx, int Len, void *Data); -extern int USB_int_SendSetupSetAddress(tUSBHost *Host, int Address); -extern int USB_int_ReadDescriptor(tUSBDevice *Dev, int Endpoint, int Type, int Index, int Length, void *Dest); -extern char *USB_int_GetDeviceString(tUSBDevice *Dev, int Endpoint, int Index); - -#endif diff --git a/Modules/USB/Core/usb_poll.c b/Modules/USB/Core/usb_poll.c deleted file mode 100644 index b6927fed..00000000 --- a/Modules/USB/Core/usb_poll.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Acess2 USB Stack - * - By John Hodge (thePowersGang) - * - * usb_poll.c - * - Endpoint polling - */ -#define DEBUG 1 -#include -#include "usb.h" - -#define POLL_ATOM 25 // 25ms atom -#define POLL_MAX 256 // Max period that can be nominated -#define POLL_SLOTS ((int)(POLL_MAX/POLL_ATOM)) - -// === IMPORTS === -extern tUSBHost *gUSB_Hosts; - -// === PROTOTYPES === -void USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint); - -// === GLOBALS === -tUSBEndpoint *gUSB_PollQueues[POLL_MAX/POLL_ATOM]; - int giUSB_PollPosition; // Index into gUSB_PollQueues - -// === CODE === -void USB_StartPollingEndpoint(tUSBInterface *Iface, int Endpoint) -{ - tUSBEndpoint *endpt; - - // Some sanity checks - if(Endpoint <= 0 || Endpoint > Iface->nEndpoints) return ; - endpt = &Iface->Endpoints[Endpoint-1]; - if(endpt->PollingPeriod > POLL_MAX || endpt->PollingPeriod <= 0) - return ; - - // TODO: Check that this endpoint isn't already on the queue - - endpt->InputData = malloc(endpt->MaxPacketSize); - - // Determine polling period in atoms - endpt->PollingAtoms = (endpt->PollingPeriod + POLL_ATOM-1) / POLL_ATOM; - if(endpt->PollingAtoms > POLL_SLOTS) endpt->PollingAtoms = POLL_SLOTS; - // Add to poll queue - // TODO: Locking - { - int idx = giUSB_PollPosition + 1; - if(idx >= POLL_SLOTS) idx -= POLL_SLOTS; - endpt->Next = gUSB_PollQueues[idx]; - gUSB_PollQueues[idx] = endpt; - } -} - -/** - * \brief USB polling thread - */ -int USB_PollThread(void *unused) -{ - Threads_SetName("USB Polling Thread"); - for(;;) - { - tUSBEndpoint *ep, *prev; - - if(giUSB_PollPosition == 0) - { - // Check hosts - for( tUSBHost *host = gUSB_Hosts; host; host = host->Next ) - { - host->HostDef->CheckPorts(host->Ptr); - } - } - -// Log_Debug("USBPoll", "giUSB_PollPosition = %i", giUSB_PollPosition); - - // A little evil for neater code - prev = (void*)( (tVAddr)&gUSB_PollQueues[giUSB_PollPosition] - offsetof(tUSBEndpoint, Next) ); - - // Process queue -// LOG("giUSB_PollPosition = %i", giUSB_PollPosition); - for( ep = gUSB_PollQueues[giUSB_PollPosition]; ep; prev = ep, ep = ep->Next ) - { - int period_in_atoms = ep->PollingAtoms; -// LOG("%i: ep = %p", giUSB_PollPosition, ep); - - // Check for invalid entries - if(period_in_atoms < 0 || period_in_atoms > POLL_ATOM) - { - Log_Warning("USB", "Endpoint on polling queue with invalid period"); - continue ; - } - // Check for entries to delete - if(period_in_atoms == 0) - { - // Remove - prev->Next = ep->Next; - ep->PollingAtoms = -1; // Mark as removed - ep = prev; // Make sure prev is kept valid - continue ; - } - - // Read data - // TODO: Check the endpoint - // TODO: Async checking? - // - Send the read request on all of them then wait for the first to complete - USB_RecvDataA( - ep->Interface, ep->EndpointIdx+1, - ep->MaxPacketSize, ep->InputData, - ep->Interface->Driver->Endpoints[ep->EndpointIdx].DataAvail - ); - - // Call callback - - // Reschedule - if( period_in_atoms != POLL_SLOTS ) - { - int newqueue_id = (giUSB_PollPosition + period_in_atoms) % POLL_SLOTS; - tUSBEndpoint **newqueue = &gUSB_PollQueues[newqueue_id]; - - prev->Next = ep->Next; - - ep->Next = *newqueue; - *newqueue = ep; - ep = prev; - } - } - giUSB_PollPosition ++; - if(giUSB_PollPosition == POLL_SLOTS) - giUSB_PollPosition = 0; - // TODO: Check for a longer delay - Time_Delay(POLL_ATOM); - } -} - diff --git a/Modules/USB/Core/usb_proto.h b/Modules/USB/Core/usb_proto.h deleted file mode 100644 index 5641dd24..00000000 --- a/Modules/USB/Core/usb_proto.h +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Acess2 USB Stack - * - By John Hodge (thePowersGang) - * - * usb_proto.h - * - USB Core Protocol Definitions - */ -#ifndef _USB_PROTO_H_ -#define _USB_PROTO_H_ - -struct sDeviceRequest -{ - Uint8 ReqType; - Uint8 Request; - Uint16 Value; - Uint16 Index; - Uint16 Length; -}; - -/* - */ -struct sDescriptor_Device -{ - Uint8 Length; - Uint8 Type; // = 1 - Uint16 USBVersion; // BCD, 0x210 = 2.10 - Uint8 DeviceClass; - Uint8 DeviceSubClass; - Uint8 DeviceProtocol; - Uint8 MaxPacketSize; - - Uint16 VendorID; - Uint16 ProductID; - Uint16 DeviceID; // BCD - - Uint8 ManufacturerStr; - Uint8 ProductStr; - Uint8 SerialNumberStr; - - Uint8 NumConfigurations; -} PACKED; - -struct sDescriptor_Configuration -{ - Uint8 Length; - Uint8 Type; // = 2 - - Uint16 TotalLength; - Uint8 NumInterfaces; - Uint8 ConfigurationValue; - Uint8 ConfigurationStr; - Uint8 AttributesBmp; - Uint8 MaxPower; // in units of 2 mA -} PACKED; - -struct sDescriptor_String -{ - Uint8 Length; - Uint8 Type; // = 3 - - Uint16 Data[128-1]; // (256 bytes - 2 bytes) / Uint16 -} PACKED; - -struct sDescriptor_Interface -{ - Uint8 Length; - Uint8 Type; // = 4 - - Uint8 InterfaceNum; - Uint8 AlternateSetting; - Uint8 NumEndpoints; // Excludes endpoint 0 - - Uint8 InterfaceClass; // - Uint8 InterfaceSubClass; - Uint8 InterfaceProtocol; - - Uint8 InterfaceStr; -} PACKED; - -struct sDescriptor_Endpoint -{ - Uint8 Length; - Uint8 Type; // = 5 - Uint8 Address; // 3:0 Endpoint Num, 7: Direction (1=IN) - /** - * 1:0 - Transfer Type - * - 00 = Control - * - 01 = Isochronous - * - 10 = Bulk - * - 11 = Interrupt - * 3:2 - Synchronisation type (Isonchronous only) - * - 00 = No Synchronisation - * - 01 = Asynchronous - * - 10 = Adaptive - * - 11 = Synchronous - * 5:4 - Usage type (Isonchronous only) - * - 00 = Data endpoint - * - 01 = Feedback endpoint - */ - Uint8 Attributes; - - Uint16 MaxPacketSize; - - /** - * - */ - Uint8 PollingInterval; -} PACKED; - -#endif - diff --git a/Modules/USB/HID/hid.h b/Modules/USB/HID/hid.h deleted file mode 100644 index 373496e8..00000000 --- a/Modules/USB/HID/hid.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Acess2 USB Stack HID Driver - * - By John Hodge (thePowersGang) - * - * hid.h - * - Core header - */ -#ifndef _HID_H_ -#define _HID_H_ - -// Report Descriptor Types -// - 0: Main -// > 8: Input - Axis/Button etc -// > 9: Output - LED/Position -// > B: Feature - -// > A: Collection - Group of Input/Output/Feature -// > C: End collection -// - 1: Global (Not restored on main) -// - 2: Local -// > -// - 3: Reserved/Unused - -// I/O/F Data Values -#define HID_IOF_CONSTANT 0x001 //!< Host Read-only -#define HID_IOF_VARIABLE 0x002 //!< - -struct sLongItem -{ - Uint8 Header; // 0xFE (Tag 15, Type 3, Size 2) - Uint8 DataSize; - Uint8 Tag; -}; - -struct sDescriptor_HID -{ - Uint8 Length; - Uint8 Type; // - Uint16 Version; // 0x0111 = 1.11 - Uint8 CountryCode; - Uint8 NumDescriptors; // >= 1 - struct { - Uint8 DescType; - Uint16 DescLen; - } Descriptors[]; -} - -#endif diff --git a/Modules/USB/HID/keysyms.h b/Modules/USB/HID/keysyms.h deleted file mode 100644 index 558464e5..00000000 --- a/Modules/USB/HID/keysyms.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Acess2 USB Stack HID Driver - * - By John Hodge (thePowersGang) - * - * usb_keysyms.h - * - USB HID Keyboard Symbols - */ -#ifndef _USB_KEYSYMS_H_ -#define _USB_KEYSYMS_H_ - -enum eUSB_Keysyms -{ - KEYSYM_NONE, - KEYSYM_ERRORROLLOVER, - KEYSYM_POSTFAIL, - KEYSYM_ERRORUNDEFINED, - // 0x04 / 4 - KEYSYM_a, KEYSYM_b, KEYSYM_c, - KEYSYM_d, KEYSYM_e, KEYSYM_f, - KEYSYM_g, KEYSYM_h, KEYSYM_i, - KEYSYM_j, KEYSYM_k, KEYSYM_l, - KEYSYM_m, KEYSYM_n, KEYSYM_o, - KEYSYM_p, KEYSYM_q, KEYSYM_r, - KEYSYM_s, KEYSYM_t, KEYSYM_u, - KEYSYM_v, KEYSYM_w, KEYSYM_x, - KEYSYM_y, KEYSYM_z, - - // 0x1E / 30 - KEYSYM_1, KEYSYM_2, - KEYSYM_3, KEYSYM_4, - KEYSYM_5, KEYSYM_6, - KEYSYM_7, KEYSYM_8, - KEYSYM_9, KEYSYM_0, - - KEYSYM_RETURN, // Enter - KEYSYM_ESC, // Esc. - KEYSYM_BACKSP, // Backspace - KEYSYM_TAB, // Tab - KEYSYM_SPACE, // Spacebar - KEYSYM_MINUS, // - _ - KEYSYM_EQUALS, // = + - KEYSYM_SQUARE_OPEN, // [ { - KEYSYM_SQUARE_CLOSE, // ] } - KEYSYM_BACKSLASH, // \ | - KEYSYM_HASH_TILDE, // # ~ (Non-US) - KEYSYM_SEMICOLON, // ; : - KEYSYM_QUOTE, // ' " - KEYSYM_GRAVE_TILDE, // Grave Accent, Tilde - KEYSYM_COMMA, // , < - KEYSYM_PERIOD, // . > - KEYSYM_SLASH, // / ? - KEYSYM_CAPS, // Caps Lock - KEYSYM_F1, KEYSYM_F2, - KEYSYM_F3, KEYSYM_F4, - KEYSYM_F5, KEYSYM_F6, - KEYSYM_F7, KEYSYM_F8, - KEYSYM_F9, KEYSYM_F10, - KEYSYM_F11, KEYSYM_F12, - KEYSYM_PRINTSCREEN, - KEYSYM_SCROLLLOCK, - KEYSYM_PAUSE, - KEYSYM_INSERT, - KEYSYM_HOME, - KEYSYM_PGUP, - KEYSYM_DELETE, - KEYSYM_END, - KEYSYM_PGDN, - KEYSYM_RIGHTARROW, - KEYSYM_LEFTARROW, - KEYSYM_DOWNARROW, - KEYSYM_UPARROW, - - KEYSYM_NUMLOCK, - KEYSYM_KPSLASH, - KEYSYM_KPSTAR, - KEYSYM_KPMINUS, - KEYSYM_KPPLUS, - KEYSYM_KPENTER, - KEYSYM_KP1, - KEYSYM_KP2, - KEYSYM_KP3, - KEYSYM_KP4, - KEYSYM_KP5, - KEYSYM_KP7, - KEYSYM_KP8, - KEYSYM_KP9 - KEYSYM_KP0, - KEYSYM_KPPERIOD, - - KEYSYM_NONUS_BACKSLASH, - KEYSYM_APPLICATION, // Windows Key - KEYSYM_POWER, - KEYSYM_KPEQUALS, - - KEYSYM_F13, KEYSYM_F14, - KEYSYM_F15, KEYSYM_F16, - KEYSYM_F17, KEYSYM_F18, - KEYSYM_F19, KEYSYM_F20, - KEYSYM_F21, KEYSYM_F22, - KEYSYM_F23, KEYSYM_F24, - KEYSYM_EXECUTE, - KEYSYM_HELP, - KEYSYM_MENU, - KEYSYM_SELECT, - KEYSYM_STOP, - KEYSYM_AGAIN, - KEYSYM_UNDO, - KEYSYM_CUT, - KEYSYM_COPY, - KEYSYM_PASTE, - KEYSYM_FIND, - KEYSYM_MUTE, - KEYSYM_VOLUP, - KEYSYM_VOLDN, - KEYSYM_LOCKING_CAPS, // Physically toggles - KEYSYM_LOGKING_NUM, - KEYSYM_LOGKING_SCROLL, - KEYSYM_KPCOMMA - - // TODO: Define the rest -}; - -#endif - diff --git a/Modules/USB/HID/main.c b/Modules/USB/HID/main.c deleted file mode 100644 index 31375b0b..00000000 --- a/Modules/USB/HID/main.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Acess2 USB Stack HID Driver - * - By John Hodge (thePowersGang) - * - * main.c - * - Driver Core - */ -#define DEBUG 0 -#define VERSION VER2(0,1) -#include -#include - -// === PROTOTYPES === - int HID_Initialise(const char **Arguments); -void HID_DeviceConnected(tUSBInterface *Dev); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, USB_HID, HID_Initialise, NULL, "USB_Core", NULL); -tUSBDriver gHID_Driver = { - .Name = "HID", - .Match = {.Class = {0x030000, 0xFF0000}}, - .Connected = HID_DeviceConnected, -}; - -// === CODE === -int HID_Initialise(const char **Arguments) -{ - USB_RegisterDriver( &gHID_Driver ); - return 0; -} - -void HID_DeviceConnected(tUSBInterface *Dev) -{ - -} - diff --git a/Modules/USB/Makefile.tpl b/Modules/USB/Makefile.tpl deleted file mode 100644 index 8df70988..00000000 --- a/Modules/USB/Makefile.tpl +++ /dev/null @@ -1,3 +0,0 @@ -CATEGORY = USB - --include ../../Makefile.tpl diff --git a/Modules/USB/UHCI/Makefile b/Modules/USB/UHCI/Makefile deleted file mode 100644 index 798159af..00000000 --- a/Modules/USB/UHCI/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# - -OBJ = uhci.o -CPPFLAGS = -I../Core/include -NAME = UHCI - --include ../Makefile.tpl diff --git a/Modules/USB/UHCI/uhci.c b/Modules/USB/UHCI/uhci.c deleted file mode 100644 index 7b40b143..00000000 --- a/Modules/USB/UHCI/uhci.c +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Acess 2 USB Stack - * - By John Hodge (thePowersGang) - * - * Universal Host Controller Interface - */ -#define DEBUG 0 -#define VERSION VER2(0,5) -#include -#include -#include -#include -#include -#include "uhci.h" - -// === CONSTANTS === -#define MAX_CONTROLLERS 4 -#define NUM_TDs 1024 - -// === PROTOTYPES === - int UHCI_Initialise(char **Arguments); -void UHCI_Cleanup(); -tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont); -void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD); -void *UHCI_int_SendTransaction(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); -void *UHCI_DataIN(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); -void *UHCI_DataOUT(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); -void *UHCI_SendSetup(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length); - int UHCI_IsTransferComplete(void *Ptr, void *Handle); - int UHCI_Int_InitHost(tUHCI_Controller *Host); -void UHCI_CheckPortUpdate(void *Ptr); -void UHCI_InterruptHandler(int IRQ, void *Ptr); -// -static void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value); -static void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value); -static void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value); -static Uint16 _InWord(tUHCI_Controller *Host, int Reg); - -// === GLOBALS === -MODULE_DEFINE(0, VERSION, USB_UHCI, UHCI_Initialise, NULL, "USB_Core", NULL); -tUHCI_TD gaUHCI_TDPool[NUM_TDs]; -tUHCI_Controller gUHCI_Controllers[MAX_CONTROLLERS]; -tUSBHostDef gUHCI_HostDef = { - .SendIN = UHCI_DataIN, - .SendOUT = UHCI_DataOUT, - .SendSETUP = UHCI_SendSetup, - .CheckPorts = UHCI_CheckPortUpdate, - .IsOpComplete = UHCI_IsTransferComplete - }; - -// === CODE === -/** - * \fn int UHCI_Initialise() - * \brief Called to initialise the UHCI Driver - */ -int UHCI_Initialise(char **Arguments) -{ - int i=0, id=-1; - int ret; - - ENTER(""); - - // Enumerate PCI Bus, getting a maximum of `MAX_CONTROLLERS` devices - while( (id = PCI_GetDeviceByClass(0x0C0300, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS ) - { - tUHCI_Controller *cinfo = &gUHCI_Controllers[i]; - Uint32 base_addr; - // NOTE: Check "protocol" from PCI? - - cinfo->PciId = id; - base_addr = PCI_GetBAR(id, 4); - - if( base_addr & 1 ) - { - cinfo->IOBase = base_addr & ~1; - cinfo->MemIOMap = NULL; - } - else - { - cinfo->MemIOMap = (void*)MM_MapHWPages(base_addr, 1); - } - cinfo->IRQNum = PCI_GetIRQ(id); - - Log_Debug("UHCI", "Controller PCI #%i: IO Base = 0x%x, IRQ %i", - id, base_addr, cinfo->IRQNum); - - IRQ_AddHandler(cinfo->IRQNum, UHCI_InterruptHandler, cinfo); - - // Initialise Host - ret = UHCI_Int_InitHost(&gUHCI_Controllers[i]); - // Detect an error - if(ret != 0) { - LEAVE('i', ret); - return ret; - } - - cinfo->RootHub = USB_RegisterHost(&gUHCI_HostDef, cinfo, 2); - LOG("cinfo->RootHub = %p", cinfo->RootHub); - - i ++; - } - - if(i == 0) { - LEAVE('i', MODULE_ERR_NOTNEEDED); - return MODULE_ERR_NOTNEEDED; - } - - if(i == MAX_CONTROLLERS) { - Log_Warning("UHCI", "Over "EXPAND_STR(MAX_CONTROLLERS)" UHCI controllers detected, ignoring rest"); - } - LEAVE('i', MODULE_ERR_OK); - return MODULE_ERR_OK; -} - -/** - * \fn void UHCI_Cleanup() - * \brief Called just before module is unloaded - */ -void UHCI_Cleanup() -{ -} - -tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont) -{ - int i; - for(i = 0; i < NUM_TDs; i ++) - { - if(gaUHCI_TDPool[i].Link == 0) { - gaUHCI_TDPool[i].Link = 1; - gaUHCI_TDPool[i].Control = 1 << 23; - return &gaUHCI_TDPool[i]; - } - // Still in use? Skip - if( gaUHCI_TDPool[i].Control & (1 << 23) ) - continue ; - // Is there a callback on it? Skip - if( gaUHCI_TDPool[i]._info.Callback ) - continue ; - // TODO: Garbage collect, but that means removing from the list too - #if 0 - // Ok, this is actually unused - gaUHCI_TDPool[i].Link = 1; - gaUHCI_TDPool[i].Control = 1 << 23; - return &gaUHCI_TDPool[i]; - #endif - } - return NULL; -} - -tUHCI_TD *UHCI_int_GetTDFromPhys(tPAddr PAddr) -{ - // TODO: Fix this to work with a non-contiguous pool - static tPAddr td_pool_base; - const int pool_size = NUM_TDs; - int offset; - if(!td_pool_base) td_pool_base = MM_GetPhysAddr( (tVAddr)gaUHCI_TDPool ); - offset = (PAddr - td_pool_base) / sizeof(gaUHCI_TDPool[0]); - if( offset < 0 || offset >= pool_size ) - { - Log_Error("UHCI", "TD PAddr %P not from pool", PAddr); - return NULL; - } - return gaUHCI_TDPool + offset; -} - -void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD) -{ - int next_frame = (_InWord(Cont, FRNUM) + 2) & (1024-1); - tUHCI_TD *prev_td; - Uint32 link; - - // TODO: How to handle FRNUM incrementing while we are in this function? - - // Empty list - if( Cont->FrameList[next_frame] & 1 ) - { - // TODO: Ensure 32-bit paddr - Cont->FrameList[next_frame] = MM_GetPhysAddr( (tVAddr)TD ); - TD->Control |= (1 << 24); // Ensure that there is an interrupt for each used frame - LOG("next_frame = %i", next_frame); - return; - } - - // Find the end of the list - link = Cont->FrameList[next_frame]; - do { - prev_td = UHCI_int_GetTDFromPhys(link); - link = prev_td->Link; - } while( !(link & 1) ); - - // Append - prev_td->Link = MM_GetPhysAddr( (tVAddr)TD ); - - LOG("next_frame = %i, prev_td = %p", next_frame, prev_td); -} - -/** - * \brief Send a transaction to the USB bus - * \param Cont Controller pointer - * \param Addr Function Address * 16 + Endpoint - * \param bTgl Data toggle value - */ -void *UHCI_int_SendTransaction( - tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, - tUSBHostCb Cb, void *CbData, void *Data, size_t Length) -{ - tUHCI_TD *td; - - if( Length > 0x400 ) return NULL; // Controller allows up to 0x500, but USB doesn't - - td = UHCI_int_AllocateTD(Cont); - - if( !td ) { - // TODO: Wait for one to free? - Log_Error("UHCI", "No avaliable TDs, transaction dropped"); - return NULL; - } - - td->Link = 1; - td->Control = (Length - 1) & 0x7FF; - td->Control |= (1 << 23); - td->Token = ((Length - 1) & 0x7FF) << 21; - td->Token |= (bTgl & 1) << 19; - td->Token |= (Addr & 0xF) << 15; - td->Token |= ((Addr/16) & 0xFF) << 8; - td->Token |= Type; - - // TODO: Ensure 32-bit paddr - if( ((tVAddr)Data & (PAGE_SIZE-1)) + Length > PAGE_SIZE ) { - Log_Warning("UHCI", "TODO: Support non single page transfers (%x + %x > %x)", - (tVAddr)Data & (PAGE_SIZE-1), Length, PAGE_SIZE - ); - // TODO: Need to enable IOC to copy the data back -// td->BufferPointer = - td->_info.bCopyData = 1; - return NULL; - } - else { - td->BufferPointer = MM_GetPhysAddr( (tVAddr)Data ); - td->_info.bCopyData = 0; - } - - // Interrupt on completion - if( Cb ) { - td->Control |= (1 << 24); - LOG("IOC Cb=%p CbData=%p", Cb, CbData); - td->_info.Callback = Cb; // NOTE: if ERRPTR then the TD is kept allocated until checked - td->_info.CallbackPtr = CbData; - } - - td->_info.DataPtr = Data; - - UHCI_int_AppendTD(Cont, td); - - return td; -} - -void *UHCI_DataIN(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length) -{ - return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0x69, DataTgl, Cb, CbData, Buf, Length); -} - -void *UHCI_DataOUT(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length) -{ - return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0xE1, DataTgl, Cb, CbData, Buf, Length); -} - -void *UHCI_SendSetup(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length) -{ - return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0x2D, DataTgl, Cb, CbData, Buf, Length); -} - -int UHCI_IsTransferComplete(void *Ptr, void *Handle) -{ - tUHCI_TD *td = Handle; - int ret; - ret = !(td->Control & (1 << 23)); - if(ret) { - td->_info.Callback = NULL; - td->Link = 0; - } - return ret; -} - -// === INTERNAL FUNCTIONS === -/** - * \fn int UHCI_Int_InitHost(tUCHI_Controller *Host) - * \brief Initialises a UHCI host controller - * \param Host Pointer - Host to initialise - */ -int UHCI_Int_InitHost(tUHCI_Controller *Host) -{ - ENTER("pHost", Host); - - _OutWord( Host, USBCMD, 4 ); // GRESET - // TODO: Wait for at least 10ms - _OutWord( Host, USBCMD, 0 ); // GRESET - - // Allocate Frame List - // - 1 Page, 32-bit address - // - 1 page = 1024 4 byte entries - Host->FrameList = (void *) MM_AllocDMA(1, 32, &Host->PhysFrameList); - if( !Host->FrameList ) { - Log_Warning("UHCI", "Unable to allocate frame list, aborting"); - LEAVE('i', -1); - return -1; - } - LOG("Allocated frame list 0x%x (0x%x)", Host->FrameList, Host->PhysFrameList); - memsetd( Host->FrameList, 1, 1024 ); // Clear List (Disabling all entries) - - //! \todo Properly fill frame list - - // Set frame length to 1 ms - _OutByte( Host, SOFMOD, 64 ); - - // Set Frame List - _OutDWord( Host, FLBASEADD, Host->PhysFrameList ); - _OutWord( Host, FRNUM, 0 ); - - // Enable Interrupts - _OutWord( Host, USBINTR, 0x000F ); - PCI_ConfigWrite( Host->PciId, 0xC0, 2, 0x2000 ); - - // Enable processing - _OutWord( Host, USBCMD, 0x0001 ); - - LEAVE('i', 0); - return 0; -} - -void UHCI_CheckPortUpdate(void *Ptr) -{ - tUHCI_Controller *Host = Ptr; - // Enable ports - for( int i = 0; i < 2; i ++ ) - { - int port = PORTSC1 + i*2; - Uint16 status; - - status = _InWord(Host, port); - // Check for port change - if( !(status & 0x0002) ) continue; - _OutWord(Host, port, 0x0002); - - // Check if the port is connected - if( !(status & 1) ) - { - // Tell the USB code it's gone. - USB_DeviceDisconnected(Host->RootHub, i); - continue; - } - else - { - LOG("Port %i has something", i); - // Reset port (set bit 9) - LOG("Reset"); - _OutWord(Host, port, 0x0200); - Time_Delay(50); // 50ms delay - _OutWord(Host, port, _InWord(Host, port) & ~0x0200); - // Enable port - LOG("Enable"); - Time_Delay(50); // 50ms delay - _OutWord(Host, port, _InWord(Host, port) | 0x0004); - // Tell USB there's a new device - USB_DeviceConnected(Host->RootHub, i); - } - } -} - -void UHCI_InterruptHandler(int IRQ, void *Ptr) -{ - tUHCI_Controller *Host = Ptr; - int frame = (_InWord(Host, FRNUM) - 1) & 0x3FF; - Uint16 status = _InWord(Host, USBSTS); -// Log_Debug("UHCI", "UHIC Interrupt, status = 0x%x, frame = %i", status, frame); - - // Interrupt-on-completion - if( status & 1 ) - { - tPAddr link; - - for( int i = 0; i < 10; i ++ ) - { - link = Host->FrameList[frame]; - Host->FrameList[frame] = 1; - while( link && !(link & 1) ) - { - tUHCI_TD *td = UHCI_int_GetTDFromPhys(link); - int byte_count = (td->Control&0x7FF)+1; - LOG("link = 0x%x, td = %p, byte_count = %i", link, td, byte_count); - // Handle non-page aligned destination - // TODO: This will break if the destination is not in global memory - if(td->_info.bCopyData) - { - void *ptr = (void*)MM_MapTemp(td->BufferPointer); - Log_Debug("UHCI", "td->_info.DataPtr = %p", td->_info.DataPtr); - memcpy(td->_info.DataPtr, ptr, byte_count); - MM_FreeTemp((tVAddr)ptr); - } - // Callback - if(td->_info.Callback && td->_info.Callback != INVLPTR) - { - LOG("Calling cb %p", td->_info.Callback); - td->_info.Callback(td->_info.CallbackPtr, td->_info.DataPtr, byte_count); - td->_info.Callback = NULL; - } - link = td->Link; - if( td->_info.Callback != INVLPTR ) - td->Link = 0; - } - - if(frame == 0) - frame = 0x3ff; - else - frame --; - } - -// Host->LastCleanedFrame = frame; - } - - LOG("status = 0x%02x", status); - _OutWord(Host, USBSTS, status); -} - -void _OutByte(tUHCI_Controller *Host, int Reg, Uint8 Value) -{ - if( Host->MemIOMap ) - ((Uint8*)Host->MemIOMap)[Reg] = Value; - else - outb(Host->IOBase + Reg, Value); -} - -void _OutWord(tUHCI_Controller *Host, int Reg, Uint16 Value) -{ - if( Host->MemIOMap ) - Host->MemIOMap[Reg/2] = Value; - else - outw(Host->IOBase + Reg, Value); -} - -void _OutDWord(tUHCI_Controller *Host, int Reg, Uint32 Value) -{ - if( Host->MemIOMap ) - ((Uint32*)Host->MemIOMap)[Reg/4] = Value; - else - outd(Host->IOBase + Reg, Value); -} - -Uint16 _InWord(tUHCI_Controller *Host, int Reg) -{ - if( Host->MemIOMap ) - return Host->MemIOMap[Reg/2]; - else - return inw(Host->IOBase + Reg); -} - diff --git a/Modules/USB/UHCI/uhci.h b/Modules/USB/UHCI/uhci.h deleted file mode 100644 index a93459a9..00000000 --- a/Modules/USB/UHCI/uhci.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * AcessOS Version 1 - * USB Stack - * - Universal Host Controller Interface - */ -#ifndef _UHCI_H_ -#define _UHCI_H_ - -// === TYPES === -typedef struct sUHCI_Controller tUHCI_Controller; -typedef struct sUHCI_TD tUHCI_TD; -typedef struct sUHCI_QH tUHCI_QH; - -// === STRUCTURES === -struct sUHCI_Controller -{ - /** - * \brief PCI Device ID - */ - Uint16 PciId; - - /** - * \brief IO Base Address - */ - Uint16 IOBase; - - /** - * \brief Memory Mapped-IO base address - */ - Uint16 *MemIOMap; - - /** - * \brief IRQ Number assigned to the device - */ - int IRQNum; - - /** - * \brief Number of the last frame to be cleaned - */ - int LastCleanedFrame; - - /** - * \brief Frame list - * - * 31:4 - Frame Pointer - * 3:2 - Reserved - * 1 - QH/TD Selector - * 0 - Terminate (Empty Pointer) - */ - Uint32 *FrameList; - - /** - * \brief Physical Address of the Frame List - */ - tPAddr PhysFrameList; - - tUSBHub *RootHub; -}; - -struct sUHCI_TD -{ - /** - * \brief Next Entry in list - * - * 31:4 - Address - * 3 - Reserved - * 2 - Depth/Breadth Select - * 1 - QH/TD Select - * 0 - Terminate (Last in List) - */ - Uint32 Link; - - /** - * \brief Control and Status Field - * - * 31:30 - Reserved - * 29 - Short Packet Detect (Input Only) - * 28:27 - Number of Errors Allowed - * 26 - Low Speed Device (Communicating with a low speed device) - * 25 - Isynchonious Select - * 24 - Interrupt on Completion (IOC) - * 23:16 - Status - * 23 - Active - * 22 - Stalled - * 21 - Data Buffer Error - * 20 - Babble Detected - * 19 - NAK Detected - * 18 - CRC/Timout Error - * 17 - Bitstuff Error - * 16 - Reserved - * 15:11 - Reserved - * 10:0 - Actual Length (Number of bytes transfered) - */ - Uint32 Control; - - /** - * \brief Packet Header - * - * 31:21 - Maximum Length (0=1, Max 0x4FF, 0x7FF=0) - * 20 - Reserved - * 19 - Data Toggle - * 18:15 - Endpoint - * 14:8 - Device Address - * 7:0 - PID (Packet Identifcation) - Only 96, E1, 2D allowed - * - * 0x96 = Data IN - * 0xE1 = Data Out - * 0x2D = Setup - */ - Uint32 Token; - - /** - * \brief Pointer to the data to send - */ - Uint32 BufferPointer; - - struct - { - tUSBHostCb Callback; - void *CallbackPtr; - void *DataPtr; - int bCopyData; - } _info; -} __attribute__((aligned(16))); - -struct sUHCI_QH -{ - /** - * \brief Next Entry in list - * - * 31:4 - Address - * 3:2 - Reserved - * 1 - QH/TD Select - * 0 - Terminate (Last in List) - */ - Uint32 Next; - - - /** - * \brief Next Entry in list - * - * 31:4 - Address - * 3:2 - Reserved - * 1 - QH/TD Select - * 0 - Terminate (Last in List) - */ - Uint32 Child; -}; - -// === ENUMERATIONS === -enum eUHCI_IOPorts { - /** - * \brief USB Command Register - * - * 15:8 - Reserved - * 7 - Maximum Packet Size selector (1: 64 bytes, 0: 32 bytes) - * 6 - Configure Flag (No Hardware Effect) - * 5 - Software Debug (Don't think it will be needed) - * 4 - Force Global Resume - * 3 - Enter Global Suspend Mode - * 2 - Global Reset (Resets all devices on the bus) - * 1 - Host Controller Reset (Reset just the controller) - * 0 - Run/Stop - */ - USBCMD = 0x00, - /** - * \brief USB Status Register - * - * 15:6 - Reserved - * 5 - HC Halted, set to 1 when USBCMD:RS is set to 0 - * 4 - Host Controller Process Error (Errors related to the bus) - * 3 - Host System Error (Errors related to the OS/PCI Bus) - * 2 - Resume Detect (Set if a RESUME command is sent to the Controller) - * 1 - USB Error Interrupt - * 0 - USB Interrupts (Set if a transaction with the IOC bit set is completed) - */ - USBSTS = 0x02, - /** - * \brief USB Interrupt Enable Register - * - * 15:4 - Reserved - * 3 - Short Packet Interrupt Enable - * 2 - Interrupt on Complete (IOC) Enable - * 1 - Resume Interrupt Enable - * 0 - Timout / CRC Error Interrupt Enable - */ - USBINTR = 0x04, - /** - * \brief Frame Number (Index into the Frame List) - * - * 15:11 - Reserved - * 10:0 - Index (Incremented each approx 1ms) - */ - FRNUM = 0x06, - /** - * \brief Frame List Base Address - * - * 31:12 - Pysical Address >> 12 - * 11:0 - Reserved (Set to Zero) - */ - FLBASEADD = 0x08, // 32-bit - /** - * \brief Start-of-frame Modify Register - * \note 8-bits only - * - * Sets the size of a frame - * Frequency = (11936+n)/12000 kHz - * - * 7 - Reserved - * 6:0 - - */ - SOFMOD = 0x0C, // 8bit - /** - * \brief Port Status and Controll Register (Port 1) - * - * 15:13 - Reserved - * 12 - Suspend - * 11:10 - Reserved - * 9 - Port Reset - * 8 - Low Speed Device Attached - * 5:4 - Line Status - * 3 - Port Enable/Disable Change - Used for detecting device removal - * 2 - Port Enable/Disable - * 1 - Connect Status Change - * 0 - Current Connect Status - */ - PORTSC1 = 0x10, - /** - * \brief Port Status and Controll Register (Port 2) - * - * See ::PORTSC1 - */ - PORTSC2 = 0x12 -}; - -#endif diff --git a/Modules/armv7/GIC/Makefile b/Modules/armv7/GIC/Makefile deleted file mode 100644 index d4a72b6a..00000000 --- a/Modules/armv7/GIC/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ := gic.o -NAME := GIC - --include ../Makefile.tpl diff --git a/Modules/armv7/GIC/gic.c b/Modules/armv7/GIC/gic.c deleted file mode 100644 index 8dbab3a3..00000000 --- a/Modules/armv7/GIC/gic.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * ARMv7 GIC Support - * - By John Hodge (thePowersGang) - * - * gic.c - * - GIC Core - */ -#define DEBUG 1 - -#include -#include -#include "gic.h" -#include - -#define N_IRQS 1024 - -// === IMPORTS === -extern void *gpIRQHandler; - -// === TYPES === -typedef void (*tIRQ_Handler)(int, void*); - -// === PROTOTYPES === - int GIC_Install(char **Arguments); -void GIC_IRQHandler(void); - -// === GLOBALS === -MODULE_DEFINE(0, 0x100, armv7_GIC, GIC_Install, NULL, NULL); -Uint32 *gpGIC_DistributorBase; -Uint32 *gpGIC_InterfaceBase; -tPAddr gGIC_DistributorAddr; -tPAddr gGIC_InterfaceAddr; -tIRQ_Handler gaIRQ_Handlers[N_IRQS]; -void *gaIRQ_HandlerData[N_IRQS]; - -// === CODE === -int GIC_Install(char **Arguments) -{ - // Realview PB - gGIC_InterfaceAddr = 0x1e000000; - gGIC_DistributorAddr = 0x1e001000; - - // Initialise - gpGIC_InterfaceBase = (void*)MM_MapHWPages(gGIC_InterfaceAddr, 1); - LOG("gpGIC_InterfaceBase = %p", gpGIC_InterfaceBase); - gpGIC_DistributorBase = (void*)MM_MapHWPages(gGIC_DistributorAddr, 1); - LOG("gpGIC_DistributorBase = %p", gpGIC_DistributorBase); - - gpGIC_InterfaceBase[GICC_PMR] = 0xFF; - gpGIC_InterfaceBase[GICC_CTLR] = 1; // Enable CPU - gpGIC_DistributorBase[GICD_CTLR] = 1; // Enable Distributor - - gpIRQHandler = GIC_IRQHandler; - - __asm__ __volatile__ ("cpsie if"); // Enable IRQs and FIQs - - return MODULE_ERR_OK; -} - -void GIC_IRQHandler(void) -{ - Uint32 num = gpGIC_InterfaceBase[GICC_IAR]; -// Log_Debug("GIC", "IRQ 0x%x", num); - gaIRQ_Handlers[num]( num, gaIRQ_HandlerData[num] ); - gpGIC_InterfaceBase[GICC_EOIR] = num; -} - -int IRQ_AddHandler(int IRQ, tIRQ_Handler Handler, void *Ptr) -{ - if( IRQ < 0 || IRQ >= N_IRQS-32 ) { - return 1; - } - - LOG("IRQ = %i", IRQ); - IRQ += 32; // 32 internal IRQs - LOG("IRQ = %i (after adjust)", IRQ); - LOG("mask = 0x%x", 1 << (IRQ & (31-1))); - gpGIC_DistributorBase[GICD_ISENABLER0+IRQ/32] = 1 << (IRQ & (32-1)); - ((Uint8*)&gpGIC_DistributorBase[GICD_ITARGETSR0])[IRQ] = 1; - -// Log_Warning("GIC", "TODO: Implement IRQ_AddHandler"); - - if( gaIRQ_Handlers[IRQ] ) - return 2; - - gaIRQ_Handlers[IRQ] = Handler; - gaIRQ_HandlerData[IRQ] = Ptr; - - return 0; -} - diff --git a/Modules/armv7/GIC/gic.h b/Modules/armv7/GIC/gic.h deleted file mode 100644 index d7a5a8dc..00000000 --- a/Modules/armv7/GIC/gic.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * ARMv7 GIC Support - * - By John Hodge (thePowersGang) - * - * gic.h - * - GIC Core Definitions - */ -#ifndef _ARM7_GIC_H_ -#define _ARM7_GIC_H_ - -enum eGICD_Registers -{ - GICD_CTLR = 0x000/4, // Distributor Control Register - GICD_TYPER = 0x004/4, // Interrupt Controller Type - GICD_IIDR = 0x008/4, // Distributor Implementer Identifcation - - GICD_IGROUPR0 = 0x080/4, // Interrupt Group Register (#0) - GICD_ISENABLER0 = 0x100/4, // Interrupt Set-Enable Register #0 (128*8=1024) - GICD_ICENABLER0 = 0x180/4, // Interrupt Clear-Enable Register #0 - GICD_ISPENDR0 = 0x200/4, // Interrupt Set-Pending Register #0 - GICD_ICPENDR0 = 0x280/4, // Interrupt Clear-Pending Register #0 - GICD_ISACTIVER0 = 0x300/4, // Interrupt Set-Active Register (GICv2) - GICD_ICACTIVER0 = 0x380/4, // Interrupt Clear-Active Register (GICv2) - - GICD_IPRIORITYR0 = 0x400/4, // Interrupt priority registers (254*4 = ) - - GICD_ITARGETSR0 = 0x800/4, // Interrupt Processor Targets Register (8*4) - - GICD_ICFGR0 = 0xC00/4, // Interrupt Configuration Register (64*4) - GICD_NSACR0 = 0xE00/4, // Non-secure Access Control Register (64*4) - GICD_SIGR = 0xF00/4, // Software Generated Interrupt Register (Write Only) - GICD_CPENDSGIR0 = 0xF10/4, // SGI Clear-Pending Registers (4*4) - GICD_SPENDSGIR0 = 0xF20/4, // SGI Set-Pending Registers (4*4) -}; - -enum eGICC_Registers -{ - GICC_CTLR = 0x000/4, // CPU Interface Control Register - GICC_PMR = 0x004/4, // Interrupt Priority Mask Register - GICC_BPR = 0x008/4, // Binary Point Register - GICC_IAR = 0x00C/4, // Interrupt Acknowledge Register - GICC_EOIR = 0x010/4, // End of Interrupt Register - GICC_RPR = 0x014/4, // Running Priority Register - GICC_HPPIR = 0x018/4, // Highest Priority Pending Interrupt Register - GICC_ABPR = 0x01C/4, // Aliased Binary Point Register - GICC_AIAR = 0x020/4, // Aliased Interrupt Acknowledge Register, - GICC_AEOIR = 0x024/4, // Aliased End of Interrupt Register - GICC_AHPPIR = 0x028/4, // Aliased Highest Priority Pending Interrupt Register - - GICC_APR0 = 0x0D0/4, // Active Priorities Registers (4*4) - GICC_NSAPR0 = 0x0E0/4, // Non-secure Active Priorities Registers (4*4) - - GICC_IIDR = 0x0FC/4, // CPU Interface Identifcation Register - GICC_DIR = 0x0FC/4, // Deactivate Interrupt Register (Write Only) -}; - -#endif diff --git a/Modules/armv7/Makefile.tpl b/Modules/armv7/Makefile.tpl deleted file mode 100644 index 58eb3c99..00000000 --- a/Modules/armv7/Makefile.tpl +++ /dev/null @@ -1,3 +0,0 @@ -CATEGORY = armv7 - --include ../../Makefile.tpl diff --git a/Modules/link.ld b/Modules/link.ld deleted file mode 100644 index 503f63d6..00000000 --- a/Modules/link.ld +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Acess2 Kernel Modules - * Linker Script - */ - -ENTRY(ModuleEntry) -OUTPUT_FORMAT(elf32-i386) - -SECTIONS -{ - . = 0 + SIZEOF_HEADERS; - - .text : AT(ADDR(.text)) { - textzero = .; - *(.text) - } - - .rodata ALIGN(0x1000): AT(ADDR(.rodata)) { - *(.rodata) - *(.rdata) - DriverInfo = .; - *(KMODULES) - } - - .data ALIGN (0x1000) : AT(ADDR(.data)) { - *(.data) - } - - .bss : AT(ADDR(.bss)) { - _sbss = .; - *(COMMON) - *(.bss) - _ebss = .; - } -} diff --git a/Modules/x86/ISADMA/Makefile b/Modules/x86/ISADMA/Makefile deleted file mode 100644 index b74c63fc..00000000 --- a/Modules/x86/ISADMA/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ := dma.o -NAME := ISADMA - --include ../Makefile.tpl diff --git a/Modules/x86/ISADMA/dma.c b/Modules/x86/ISADMA/dma.c deleted file mode 100644 index 732a93c3..00000000 --- a/Modules/x86/ISADMA/dma.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * AcessOS 1.0 - * DMA Driver - */ -#include -#include - -#define DMA_SIZE (0x2400) -#define DMA_ADDRESS(c) ((c)*DMA_SIZE+0x500) //Save Space for IDT and BDA - -#define LOWB(x) ((x)&0xFF) -#define HIB(x) (((x)>>8)&0xFF) -#define HIW(x) (((x)>>16)&0xFFFF) - -// === TYPES === -typedef struct -{ - int mode; - char *address; -} t_dmaChannel; - -// === PROTOTYPES === - int DMA_Install(char **Arguments); -void DMA_SetChannel(int Channel, int length, int read); - int DMA_ReadData(int channel, int count, void *buffer); - int DMA_WriteData(int channel, int count, const void *buffer); - -// === CONSTANTS === -const Uint8 cMASKPORT [8] = { 0x0A, 0x0A, 0x0A, 0x0A, 0xD4, 0xD4, 0xD4, 0xD4 }; -const Uint8 cMODEPORT [8] = { 0x0B, 0x0B, 0x0B, 0x0B, 0xD6, 0xD6, 0xD6, 0xD6 }; -const Uint8 cCLEARPORT[8] = { 0x0C, 0x0C, 0x0C, 0x0C, 0xD8, 0xD8, 0xD8, 0xD8 }; -const Uint8 cPAGEPORT [8] = { 0x87, 0x83, 0x81, 0x82, 0x8F, 0x8B, 0x89, 0x8A }; -const Uint8 cADDRPORT [8] = { 0x00, 0x02, 0x04, 0x06, 0xC0, 0xC4, 0xC8, 0xCC }; -const Uint8 cCOUNTPORT[8] = { 0x01, 0x03, 0x05, 0x07, 0xC2, 0xC6, 0xCA, 0xCE }; - -// === GLOBALS === -MODULE_DEFINE(0, 0x0100, x86_ISADMA, DMA_Install, NULL, NULL); -char *dma_addresses[8]; -t_dmaChannel dma_channels[8]; - -// === CODE === -/** - * \brief Initialise DMA channels - * \param Arguments Arguments passed at boot time - */ -int DMA_Install(char **Arguments) -{ - Uint i; - for(i=8;i--;) - { - outb( cMASKPORT[i], 0x04 | (i & 0x3) ); // mask channel - outb( cCLEARPORT[i], 0x00 ); - outb( cMODEPORT[i], 0x48 | (i & 0x3) ); //Read Flag - outb( 0xd8, 0xff); //Reset Flip-Flop - outb( cADDRPORT[i], LOWB(DMA_ADDRESS(i)) ); // send address - outb( cADDRPORT[i], HIB(DMA_ADDRESS(i)) ); // send address - outb( 0xd8, 0xff); //Reset Flip-Flop - outb( cCOUNTPORT[i], LOWB(DMA_SIZE) ); // send size - outb( cCOUNTPORT[i], HIB(DMA_SIZE) ); // send size - outb( cPAGEPORT[i], LOWB(HIW(DMA_ADDRESS(i))) ); // send page - outb( cMASKPORT[i], i & 0x3 ); // unmask channel - - dma_channels[i].mode = 0; - dma_addresses[i] = (char*)DMA_ADDRESS(i); - dma_addresses[i] += KERNEL_BASE; - } - return MODULE_ERR_OK; -} - -/** - * \fn void DMA_SetChannel(int Channel, int length, int read) - * \brief Set DMA Channel Length and RW - */ -void DMA_SetChannel(int Channel, int length, int read) -{ - Uint chan = Channel & 7; - read = !!read; - if(length > DMA_SIZE) length = DMA_SIZE; - length --; //Adjust for DMA - outb( cMASKPORT[chan], 0x04 | (chan & 0x3) ); // mask channel - outb( cCLEARPORT[chan], 0x00 ); - outb( cMODEPORT[chan], (0x44 + (!read)*4) | (chan & 0x3) ); - outb( cADDRPORT[chan], LOWB(DMA_ADDRESS(chan)) ); // send address - outb( cADDRPORT[chan], HIB(DMA_ADDRESS(chan)) ); // send address - outb( cPAGEPORT[chan], HIW(DMA_ADDRESS(chan)) ); // send page - outb( cCOUNTPORT[chan], LOWB(length) ); // send size - outb( cCOUNTPORT[chan], HIB(length) ); // send size - outb( cMASKPORT[chan], chan & 0x3 ); // unmask channel - dma_addresses[chan] = (char*)DMA_ADDRESS(chan); - dma_addresses[chan] += KERNEL_BASE; -} - -/** - * \fn void DMA_ReadData(int channel, int count, void *buffer) - * \brief Read data from a DMA buffer - */ -int DMA_ReadData(int channel, int count, void *buffer) -{ - if(channel < 0 || channel > 7) - return -1; - if(count < 0 || count > DMA_SIZE) - return -2; - //LogF("memcpy(*0x%x, dma_channels[channel].address, count)\n", buffer - memcpy(buffer, dma_addresses[channel], count); - return 0; -} - -/** - * \fn void DMA_WriteData(int channel, int count, void *buffer) - * \brief Write data to a DMA buffer - */ -int DMA_WriteData(int channel, int count, const void *buffer) -{ - if(channel < 0 || channel > 7) - return -1; - if(count < 0 || count > DMA_SIZE) - return -2; - - memcpy(dma_addresses[channel], buffer, count); - - return 0; -} diff --git a/Modules/x86/ISADMA/include/dma.h b/Modules/x86/ISADMA/include/dma.h deleted file mode 100644 index b1a6d1a6..00000000 --- a/Modules/x86/ISADMA/include/dma.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Acess2 DMA Driver - */ -#ifndef _DMA_H_ -#define _DMA_H_ - -extern void DMA_SetChannel(int channel, int length, int read); -extern int DMA_ReadData(int channel, int count, void *buffer); -extern int DMA_WriteData(int channel, int count, void *buffer); - -#endif diff --git a/Modules/x86/Makefile.tpl b/Modules/x86/Makefile.tpl deleted file mode 100644 index 61b234f3..00000000 --- a/Modules/x86/Makefile.tpl +++ /dev/null @@ -1,3 +0,0 @@ -CATEGORY = x86 - --include ../../Makefile.tpl diff --git a/Modules/x86/VGAText/Makefile b/Modules/x86/VGAText/Makefile deleted file mode 100644 index 8f1aee6d..00000000 --- a/Modules/x86/VGAText/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# - -OBJ := vga.o -NAME := VGAText - --include ../Makefile.tpl diff --git a/Modules/x86/VGAText/vga.c b/Modules/x86/VGAText/vga.c deleted file mode 100644 index 41f0bd01..00000000 --- a/Modules/x86/VGAText/vga.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Acess2 VGA Controller Driver - */ -#define DEBUG 0 -#include -#include -#include -#include - -// === CONSTANTS === -#define VGA_WIDTH 80 -#define VGA_HEIGHT 25 - -// === PROTOTYPES === - int VGA_Install(char **Arguments); -Uint64 VGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer); - int VGA_IOCtl(tVFS_Node *Node, int Id, void *Data); -Uint8 VGA_int_GetColourNibble(Uint16 col); -Uint16 VGA_int_GetWord(const tVT_Char *Char); -void VGA_int_SetCursor(Sint16 x, Sint16 y); -// --- 2D Acceleration Functions -- -void VGA_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour); -void VGA_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H); - -// === GLOBALS === -MODULE_DEFINE(0, 0x000A, x86_VGAText, VGA_Install, NULL, NULL); -tVFS_NodeType gVGA_NodeType = { - //.Read = VGA_Read, - .Write = VGA_Write, - .IOCtl = VGA_IOCtl - }; -tDevFS_Driver gVGA_DevInfo = { - NULL, "x86_VGAText", - { - .NumACLs = 0, - .Size = VGA_WIDTH*VGA_HEIGHT*sizeof(tVT_Char), - .Type = &gVGA_NodeType - } -}; -Uint16 *gVGA_Framebuffer = (void*)( KERNEL_BASE|0xB8000 ); - int giVGA_BufferFormat = VIDEO_BUFFMT_TEXT; -tDrvUtil_Video_2DHandlers gVGA_2DFunctions = { - NULL, - VGA_2D_Fill, - VGA_2D_Blit -}; - -// === CODE === -/** - * \fn int VGA_Install(char **Arguments) - */ -int VGA_Install(char **Arguments) -{ - Uint8 byte; - - // Enable Bright Backgrounds - inb(0x3DA); // Reset flipflop - outb(0x3C0, 0x30); // Index 0x10, PAS - byte = inb(0x3C1); - byte &= ~8; // Disable Blink - outb(0x3C0, byte); // Write value - - - // Install DevFS - DevFS_AddDevice( &gVGA_DevInfo ); - - return MODULE_ERR_OK; -} - -/** - * \brief Writes a string of bytes to the VGA controller - */ -Uint64 VGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer) -{ - if( giVGA_BufferFormat == VIDEO_BUFFMT_TEXT ) - { - int num = Length / sizeof(tVT_Char); - int ofs = Offset / sizeof(tVT_Char); - int i = 0; - const tVT_Char *chars = Buffer; - Uint16 word; - - //ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); - - for( ; num--; i ++, ofs ++) - { - word = VGA_int_GetWord( &chars[i] ); - gVGA_Framebuffer[ ofs ] = word; - } - - //LEAVE('X', Length); - return Length; - } - else if( giVGA_BufferFormat == VIDEO_BUFFMT_2DSTREAM ) - { - return DrvUtil_Video_2DStream(NULL, Buffer, Length, &gVGA_2DFunctions, sizeof(gVGA_2DFunctions)); - } - else - { - return 0; - } -} - -/** - * \fn int VGA_IOCtl(tVFS_Node *Node, int Id, void *Data) - * \brief IO Control Call - */ -int VGA_IOCtl(tVFS_Node *Node, int ID, void *Data) -{ - int rv; - switch(ID) - { - case DRV_IOCTL_TYPE: return DRV_TYPE_VIDEO; - case DRV_IOCTL_IDENT: memcpy(Data, "VGA\0", 4); return 1; - case DRV_IOCTL_VERSION: *(int*)Data = 50; return 1; - case DRV_IOCTL_LOOKUP: return 0; - - case VIDEO_IOCTL_GETSETMODE: return 0; // Mode 0 only - case VIDEO_IOCTL_FINDMODE: - if( !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)) ) return -1; - ((tVideo_IOCtl_Mode*)Data)->id = 0; // Text Only! - case VIDEO_IOCTL_MODEINFO: - if( !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)) ) return -1; - if( ((tVideo_IOCtl_Mode*)Data)->id != 0) return 0; - ((tVideo_IOCtl_Mode*)Data)->width = VGA_WIDTH*giVT_CharWidth; - ((tVideo_IOCtl_Mode*)Data)->height = VGA_HEIGHT*giVT_CharHeight; - ((tVideo_IOCtl_Mode*)Data)->bpp = 4; - return 1; - - case VIDEO_IOCTL_SETBUFFORMAT: - if( !CheckMem(Data, sizeof(int)) ) return -1; - switch( *(int*)Data ) - { - case VIDEO_BUFFMT_TEXT: - case VIDEO_BUFFMT_2DSTREAM: - rv = giVGA_BufferFormat; - giVGA_BufferFormat = *(int*)Data; -// Log_Debug("VGA", "Buffer format set to %i", giVGA_BufferFormat); - return rv; - default: - break; - } - return -1; - - case VIDEO_IOCTL_SETCURSOR: - VGA_int_SetCursor( ((tVideo_IOCtl_Pos*)Data)->x, ((tVideo_IOCtl_Pos*)Data)->y ); - return 1; - } - return 0; -} - -/** - * \fn Uint8 VGA_int_GetColourNibble(Uint16 col) - * \brief Converts a 12-bit colour into a VGA 4-bit colour - */ -Uint8 VGA_int_GetColourNibble(Uint16 col) -{ - Uint8 ret = 0; - int bright = 0; - - col = col & 0xCCC; - col = ((col>>2)&3) | ((col>>4)&0xC) | ((col>>6)&0x30); - bright = ( (col & 2 ? 1 : 0) + (col & 8 ? 1 : 0) + (col & 32 ? 1 : 0) ) / 2; - - switch(col) - { - // Black - case 0x00: ret = 0x0; break; - // Dark Grey - case 0x15: ret = 0x8; break; - // Blues - case 0x01: - case 0x02: ret = 0x1; break; - case 0x03: ret = 0x9; break; - // Green - case 0x04: - case 0x08: ret = 0x2; break; - case 0x0C: ret = 0xA; break; - // Reds - case 0x10: - case 0x20: ret = 0x4; break; - case 0x30: ret = 0xC; break; - // Light Grey - case 0x2A: ret = 0x7; break; - // White - case 0x3F: ret = 0xF; break; - - default: - ret |= (col & 0x03 ? 1 : 0); - ret |= (col & 0x0C ? 2 : 0); - ret |= (col & 0x30 ? 4 : 0); - ret |= (bright ? 8 : 0); - break; - } - return ret; -} - -/** - * \fn Uint16 VGA_int_GetWord(tVT_Char *Char) - * \brief Convers a character structure to a VGA character word - */ -Uint16 VGA_int_GetWord(const tVT_Char *Char) -{ - Uint16 ret; - Uint16 col; - - // Get Character - if(Char->Ch < 128) - ret = Char->Ch; - else { - switch(Char->Ch) - { - default: ret = 0; break; - } - } - - col = VGA_int_GetColourNibble(Char->BGCol); - ret |= col << 12; - - col = VGA_int_GetColourNibble(Char->FGCol); - ret |= col << 8; - - return ret; -} - -/** - * \fn void VGA_int_SetCursor(Sint16 x, Sint16 y) - * \brief Updates the cursor position - */ -void VGA_int_SetCursor(Sint16 x, Sint16 y) -{ - int pos = x+y*VGA_WIDTH; - if(x == -1 || y == -1) - pos = -1; - outb(0x3D4, 14); - outb(0x3D5, pos >> 8); - outb(0x3D4, 15); - outb(0x3D5, pos); -} - -void VGA_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour) -{ - tVT_Char ch; - Uint16 word, *buf; - - X /= giVT_CharWidth; - W /= giVT_CharWidth; - Y /= giVT_CharHeight; - H /= giVT_CharHeight; - - ch.Ch = 0x20; - ch.BGCol = (Colour & 0x0F0000) >> (16-8); - ch.BGCol |= (Colour & 0x000F00) >> (8-4); - ch.BGCol |= (Colour & 0x00000F); - word = VGA_int_GetWord(&ch); - - Log("Fill (%i,%i) %ix%i with 0x%x", X, Y, W, H, word); - - if( X > VGA_WIDTH || Y > VGA_HEIGHT ) return ; - if( X + W > VGA_WIDTH ) W = VGA_WIDTH - X; - if( Y + H > VGA_HEIGHT ) H = VGA_HEIGHT - Y; - - buf = gVGA_Framebuffer + Y*VGA_WIDTH + X; - - - while( H -- ) { - int i; - for( i = 0; i < W; i ++ ) - *buf++ = word; - buf += VGA_WIDTH - W; - } -} - -void VGA_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H) -{ - Uint16 *src, *dst; - - DstX /= giVT_CharWidth; - SrcX /= giVT_CharWidth; - W /= giVT_CharWidth; - - DstY /= giVT_CharHeight; - SrcY /= giVT_CharHeight; - H /= giVT_CharHeight; - -// Log("(%i,%i) from (%i,%i) %ix%i", DstX, DstY, SrcX, SrcY, W, H); - - if( SrcX > VGA_WIDTH || SrcY > VGA_HEIGHT ) return ; - if( SrcX + W > VGA_WIDTH ) W = VGA_WIDTH - SrcX; - if( SrcY + H > VGA_HEIGHT ) H = VGA_HEIGHT - SrcY; - if( DstX > VGA_WIDTH || DstY > VGA_HEIGHT ) return ; - if( DstX + W > VGA_WIDTH ) W = VGA_WIDTH - DstX; - if( DstY + H > VGA_HEIGHT ) H = VGA_HEIGHT - DstY; - - - src = gVGA_Framebuffer + SrcY*VGA_WIDTH + SrcX; - dst = gVGA_Framebuffer + DstY*VGA_WIDTH + DstX; - - if( src > dst ) - { - // Simple copy - while( H-- ) { - memcpy(dst, src, W*2); - dst += VGA_WIDTH; - src += VGA_WIDTH; - } - } - else - { - dst += H*VGA_WIDTH; - src += H*VGA_WIDTH; - while( H -- ) { - int i; - dst -= VGA_WIDTH-W; - src -= VGA_WIDTH-W; - for( i = W; i --; ) *--dst = *--src; - } - } -} diff --git a/Notes/AcessFS.txt b/Notes/AcessFS.txt new file mode 100644 index 00000000..7c74bebd --- /dev/null +++ b/Notes/AcessFS.txt @@ -0,0 +1,54 @@ +Acess File System +- Database Design + +== Data Strutures == +Blocks are of a size specified in the superblock +- Superblock + > Fixed offset: 1024 bytes +- Field Table + > Offset set in superblock +- Index Table +- Inode Table + +=== Superblock === +struct sSuperblock { + Uint8 Magic[4]; // == '\xACFS'+Version + Uint8 BlockSize; // TrueSize = 2^(7+BlockSize) +}; + +=== Field Table === +struct sFieldTableEntry { + Uint16 Ident; + Uint8 Type; + Uint8 Length; + char Text[]; +} FieldTable[SuperBlock.NFields]; + +=== Index Table == +struct sIndexTableEntry { + Uint16 Field; + Uint16 CheckSum; + Uint32 Block; +} IndexTable[SuperBlock.NFields]; + +=== Index Table entry == +struct { + Uint32 NumEntries; + Uint32 Links[]; +}; + +=== Inode Table === +struct sInodeTable { + +}; + +=== Inode === +struct sInodeEntry { + Uint16 Name; + Uint8 Size; + Uint8 Checksum; + Uint8 data[]; +}; + +Each `sInodeEntry` defines an entry in a "database" + diff --git a/Notes/Spinlocks.txt b/Notes/Spinlocks.txt new file mode 100644 index 00000000..b4f6b84c --- /dev/null +++ b/Notes/Spinlocks.txt @@ -0,0 +1,26 @@ +SHORTLOCK() + cli; lock cmpxchg +SHORTREL() + lock and ; sti + + +LONGLOCK() + mov eax, 1 + lock cmpxchg lock.lock, eax + if(eax) { + SHORTLOCK(lock.listLock) + // add to list (linked list, 4 static entries) + SHORTREL(lock.listLock) + for(;;) + { + check owner + mov eax, 1 + lock cmpxchg lock.lock, eax + if(!eax) break; // got lock + Threads_Sleep(); + } + } + +LONGREL() + lock and lock.lock, 0 + pop off front of list, free entry, wake thread diff --git a/Notes/VFS - Select.txt b/Notes/VFS - Select.txt new file mode 100644 index 00000000..70e1993c --- /dev/null +++ b/Notes/VFS - Select.txt @@ -0,0 +1,25 @@ +select() +- Implemented using a wait queue for every file descriptor +- That requires waiting on sockets to be centeralised + +All wait tasks (reads on VTerm, Pipes, PTYs, network sockets) use the kernel +version of select with an inifinite timeout. +They then signal the VFS using their VFS node pointer as a reference + +The VFS function select() +- Maintains a list of processes on each node (if select has been called on that node) +- Each process maybe has a semaphore on it (to use the semaphore code to maintain the process list) + > Could maybe use a mutex instead + + +VFS_Select(int, fd_set* read, fd_set* write, fd_set* except) + +read is the set of sockets that we are waiting to read from +write " " " be able to write to +except " " " for possible errors on + + +Hence, each VFS_Node has three listener lists (or just pointers) +- One for when data is avaliable +- One for when space is avaliable for writing +- One for when an error occurs (closed pipe, etc ...) diff --git a/Notes/VTerm.txt b/Notes/VTerm.txt new file mode 100644 index 00000000..6173108b --- /dev/null +++ b/Notes/VTerm.txt @@ -0,0 +1,53 @@ + +== Read == +- UTF-8 / UCS-4 Character Stream + > Selected with mode call + +== Write == +UTF-8 Emulation Text Mode: +- Emuates a character device + > VT-100/ANSI Control Codes + > Characters/Symbols are sent as UTF-8 + +/* +Native Text Mode: +- NxM 64-bit entries + > UCS-32 Codepoint (if a diacritic is encountered, the previous character is modified) + > 12-bit (16 encoded) Foreground + > 12-bit (16 encoded) Background +*/ + +Framebuffer Graphics: +- WxH 32-bit (3x 8-bit channels) framebuffer +- Write to entire framebuffer + +Accellerated Graphics: +- Command Stream + > Each Node.Write call is a single command + + NOP (-) + + Direct (Uint16 X, Y, W, H, Uint32 Data[]) + + Blit (Uint16 W, H, SrcX, SrxY, DstX, DstY, Uint32 Data[]) + + Fill (Uint16 X, Y, W, H) + + Rect (Uint16 X, Y, W, H) + + Line (Uint16 X, Y, W, H) + + Text (Uint16 X, Y, Size, Font) + + ShowTile (Uint16 ID, Uint16 X, Y) + + DelTile (Uint16 ID) +- Extra IOCtls + + int LoadFont(char *Path) + + UnloadFont(int *ID) + + int MakeTile(struct {Uint16 W, H, Uint32 Data[]} *Img) + + DelTile(int *ID) +- Allow fast switch between Accel/Framebuffer? +- Min Reqd Tile Size 32x32 + > Tiles should be driver emulated if unavaliable by hardware + +3D Graphics: (Can be emulated if not avaliable, or just denied) +- Command Stream + > + + NOP (-) + + FlipBuffer (-) + + LoadTexture(Uint16 ID, W, H, Uint32 Data[]) + + UnloadTexture(Uint16 ID) + + SetTexture(Uint16 ID) + + Triangle (Uint16 Texture, Uint32[3+3+2][3])