Compare commits

...

110 Commits

Author SHA1 Message Date
958a3ed263
feat(userspace/apps/test): update utest
Some checks failed
Build OS / Build Cross-Compiler & Toolchain (push) Has been cancelled
Build OS / Deploy Documentation to GitHub Pages (push) Has been cancelled
Build OS / Analyze (c-cpp) (push) Has been cancelled
Build OS / Build amd64 (push) Has been cancelled
Build OS / Build i386 (push) Has been cancelled
Build OS / Build aarch64 (push) Has been cancelled
Build OS / Build arm (push) Has been cancelled
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-05 04:23:19 +00:00
a0e3993a3f
feat(userspace/libc): implement <math.h> header
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-05 04:21:06 +00:00
edeecf7831
fix(userspace/apps/test): adjust fflush(stdout) calls for better output control
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-05 04:19:34 +00:00
90fb9c7952
test(userspace/apps/test): expand math function tests for accuracy
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-05 04:18:40 +00:00
0a52ef4f68
fix(userspace/apps/test): update expected results for rounding and special functions
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-05 03:53:59 +00:00
5e0a80fa1c
feat(userspace/libc): implementation <fenv.h> header
FIXME: testing required!
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-05 03:23:19 +00:00
784f3c3df9
chore: standardize indentation for CMake files
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-05 01:22:45 +02:00
68acf428a2
fix(userspace/libc): disable debug info in memory allocation functions
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-03 18:26:00 +00:00
9105c63465
refactor(userspace/apps/test): ♻️ move all functions in one file
it's just too much...

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-03 18:25:05 +00:00
659805960b
fix(userspace/libc): wrong puts() implementation
The implementation didn't fully followed the POSIX.1-2024 standard "The puts() function shall write the string pointed to by s, followed by a <newline>, to the standard output stream stdout. The terminating null byte shall not be written."

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-03 12:08:39 +00:00
451c5405e0
fix(userspace/apps/test): fix noreturn compiler warning
fix "warning: 'noreturn' function does return"

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-03 00:58:19 +00:00
bd02b976a2
fix(kernel/tty): temporal removal of ICANON checking
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 23:57:46 +00:00
9bffe3e013
chore(vscode): rename preLaunchTask from launch-qemu to QEMU
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 23:33:28 +00:00
84298fc4eb
fix(kernel/driver): set unused file system operation pointers to nullptr
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 23:31:30 +00:00
87a2dc9444
fix(kernel/driver): node device & offset were not set for new created files under /dev
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 23:29:13 +00:00
426a84a1a9
docs(kernel): short doc for __check_op macro
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 23:14:40 +00:00
8eed8909da
chore(vscode): update launch configuration to include libc_test
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 22:47:43 +00:00
09d0af1ea6
refactor(userspace/libs): rename libdemo to libexample
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 22:02:02 +00:00
7b85636f8f
fix(drivers): remove drivers that are now in kernel
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 21:37:52 +00:00
95585fce5f
fix(kernel/driver): remove unused device handling code in daemon
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 21:37:10 +00:00
bf1e3432d7
feat(kernel/drivers): migrate drivers to the kernel
make the drivers builtin

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 21:37:01 +00:00
f824df9aad
feat(kernel/driver): implement built-in driver support
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 21:34:16 +00:00
426499090e
feat(kernel/driver): add CreateDeviceFile function in the API
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 21:32:00 +00:00
1af2cf657d
feat(kernel/driver): add CreateDeviceFile method
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 21:30:15 +00:00
f31d11f7ad
fix(kernel/pci): fix MapPCIAddresses when BAR size of zero
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 21:28:28 +00:00
851a8c140f
feat(kernel/driver): add ReloadDriver method to manage driver reloading
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 21:27:20 +00:00
ae2617dca2
refactor(driver/api): delegate memory allocation and deallocation to DriverManager
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-02 21:26:00 +00:00
adba9cc348
refactor(driver/api): fix formatting
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-01 23:33:55 +00:00
232e06f8f3
refactor(kernel/pci): simplify PCI device initialization by delegating to PCIManager
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-01 23:32:54 +00:00
aea8a7bb08
feat(kernel/pci): add device initialization method for PCI devices
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-01 23:32:14 +00:00
65ab83b42b
fix(kernel/pci): map BAR address using PWT and PCD flags
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-03-01 23:31:31 +00:00
d3f6d51ed2
build(vscode): add separated tasks for building bootloader, kernel, drivers, userspace, and image
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-28 00:41:17 +00:00
ca8dc6429b
fix(kernel): fix empty initialization of std::string (str = "")
_size is 0 which fails the memcpy checks

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-27 18:13:51 +00:00
cfb5d9a0f4
build(tools): fix gdb error 'Scripting in the "Python" language is not supported in this copy of GDB.'
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-26 23:20:10 +00:00
8dab45ecfd
build(devcontainer): improve Dev Container development
The toolchain and qemu can be built inside the container + running the qemu inside the container

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-26 23:10:17 +00:00
88a5f06325
build(tools): rewrite makefile to be more efficient and easy to understand
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-26 23:07:36 +00:00
aab84cd3ab
chore(devcontainer): rename dev container (libc_test)
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-26 23:04:57 +00:00
a3719eef3e
feat(initrd): add /etc/hosts file
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-26 15:21:54 +00:00
0769a82f4b
feat(userspace/libc): update math functions
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-26 02:10:06 +00:00
f978f2487e
feat(userspace/libc): add math stub functions
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-25 16:17:37 +00:00
8859bfc438
chore(vscode): add Dev Container
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-25 16:16:41 +00:00
5202601c4f
feat(userspace/libs/libm): add stub libm
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-25 16:13:42 +00:00
78141b28c7
feat(userspace/apps/test/libc_test): rewrite a lot of the code and improve debugging with vscode using .devcontainer
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-25 14:10:12 +00:00
3f2584ac09
chore(vscode): add 'kernel' scope to conventional commits
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-24 02:10:14 +02:00
a2ce579a61
fix(userspace/libc): update vscode stub macro with improved formatting
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-24 02:09:31 +02:00
0958cdf7f8
fix(kernel): add TZ environment variable to init process
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-24 02:08:42 +02:00
7f3b2b4dbb
fix(userspace/libc): fix tzset() function
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-22 03:26:49 +02:00
f6f46d1bbe
fix(userspace/libc): fix gmtime_r, localtime_r and mktime implementation
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-22 03:22:47 +02:00
c12ee67592
fix(userspace/libc): handle NULL input in getenv function
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-22 02:52:11 +02:00
74ff9579f3
fix(userspace/libc): fopen should set buffer_pos to -1
This change is a fix for fread() because it needs to pass the if statement: "if (stream->buffer_pos >= stream->buffer_size)"

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-22 02:43:06 +02:00
d7cbeb9eba
fix(userspace/libc): modify __libc_init and crt0 to initialize "environ"
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-22 02:41:41 +02:00
4d333f94bc
feat(userspace/apps/test/utest): add TestProcess function for executing test programs
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:58:30 +02:00
e003af38ff
chore(vscode): add userspace/apps/sys/init to conventional commit scopes
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:56:09 +02:00
d1c504f9a6
feat(userspace/apps/sys/init): handle termination signals for graceful shutdown
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:55:29 +02:00
d46ed57dd7
chore(vscode): add more conventional commit scopes for userspace
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:54:45 +02:00
a6d372aaad
feat(userspace/libs/libdemo): add template library
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:53:41 +02:00
b3e0b30147
refactor(tests): remove obsolete SIMD and web test files
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:47:12 +02:00
93e57f5dbe
feat(userspace/libc): add stub termios implementation
Stub implementation! This needs a rewrite!

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:45:20 +02:00
db5a42289a
feat(userspace/libc): implement feof() and ferror() functions
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:43:00 +02:00
77136acccd
feat(userspace/libc): implement <time.h> header
Not tested enough!

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:42:33 +02:00
258ac6e2f6
chore(vscode): add recommended extensions for improved development experience
ms-vscode.cpptools maziac.asm-code-lens editorconfig.editorconfig vivaxy.vscode-conventional-commits ms-vscode.hexeditor webfreak.debug ibm.output-colorizer gruntfuggly.todo-tree naumovs.color-highlight seven1bit.vscode-ext-ansi-color-highlight jeff-hykin.better-cpp-syntax aaron-bond.better-comments

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:40:21 +02:00
fbe4b94805
chore(vscode): add conventional commit scopes for kernel
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:38:52 +02:00
2c2e7d9de3
build(userspace): update vscode launch configuration and Makefiles for utest and libc_test
Easier to debug

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:37:48 +02:00
9dfa750444
refactor(kernel/syscalls): simplify argument handling in HandleNativeSyscalls
Easier to debug

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:35:57 +02:00
4240183fa1
fix(userspace/libc): cast status to int in __check_errno for proper error handling
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-21 02:33:40 +02:00
42f26787fb
feat(userspace/libc): implement getdelim and getline functions
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-20 02:20:26 +02:00
8d08ab933a
feat(userspace/libc): implement fstatat, lstat & stat
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-20 02:17:19 +02:00
c83c542f5b
feat(userspace/libc): implement fstat()
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-20 02:12:02 +02:00
08319ef4c7
docs(syscalls): add documentation for FBIOGET_SCREEN_INFO ioctl
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-20 02:09:09 +02:00
f4168e2815
refactor(userspace/libc): implement pthread_sigmask, sigaddset, sigfillset & sigprocmask
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-20 02:07:40 +02:00
123d11e4e3
feat(userspace/libc): implement system() function
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-20 02:07:04 +02:00
b1a107fb65
refactor(userspace/libc): replace syscall2 with call_kill in kill function
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-20 02:00:26 +02:00
d375acd0ec
fix(userspace/libc): include <ctype.h> in stdlib.c
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-20 01:53:21 +02:00
5e6e63ab1a
feat(userspace/libc): implement <ctype.h> header
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-20 01:52:37 +02:00
ab4bc55270
feat(userspace/libc): implement atoi, strtol, and strtoll functions in <stdlib.h>
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-20 01:49:41 +02:00
e5acf4a99e
feat(userspace/libc): implement getpid, getppid & getuid
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-20 01:49:12 +02:00
5089cfa81b
feat(userspace/libc): implement <pwd.h> header
Implemented everything except endpwent() and setpwent()

NOT TESTED!

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-20 01:45:04 +02:00
b5fce27037
feat(userspace/libc): implement getchar() and getc()
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-19 21:57:51 +02:00
0238f62894
fix(userspace/libc): fix error handling in ioctl function
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-19 21:03:53 +02:00
ce59b6ea03
feat(userspace/libc): extend termios header with input/output speed and winsize structure
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-19 20:59:48 +02:00
9dcb5abe89
feat(userspace/libc): add termios header file for terminal I/O control
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-19 20:43:37 +02:00
4ac29bbce1
feat(userspace/libc): define file descriptor macros for standard input/output
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-19 20:42:56 +02:00
155f3bfab5
docs(kernel/api): add documentation for FBIOGET_SCREEN_INFO
Add documentation for FBIOGET_SCREEN_INFO macro

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-19 19:47:10 +02:00
e960f9fcc8
chore(userspace/libc): update vscode workspace config
Add conventionalCommits.scopes with "userspace/libc"

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-19 18:55:28 +02:00
4bb8ce6d00
feat(userspace/libc): complete <string.h> implementation
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-19 18:54:20 +02:00
2ae18af9a0
feat(userspace/apps/test/libc_test): add more tests
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-19 18:53:54 +02:00
e61b5824db
refactor(userspace/apps/test/libc_test): remove deprecated string test files
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-15 15:35:28 +02:00
0f9a1915d1
feat(userspace/libc): implement all <string.h> functions
Implement all string.h functions except some which require locale which is not implemented yet

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-14 22:15:20 +02:00
6dfefc90c4
feat(userspace/libc): implement strcpy function
Add strcpy implementation in string.c

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-14 20:07:20 +02:00
c688bd7a97
feat: synchronize syscalls.h
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-14 19:59:59 +02:00
e927d93a48
feat(kernel): add stub device /dev/fb0
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-14 19:57:29 +02:00
d9235c6f90
userspace/libc: implement qsort, realloc and reallocarray functions in stdlib
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-14 02:19:14 +02:00
afa87ec5f3
userspace/libc: implement strcoll()
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-14 02:18:40 +02:00
22d01c7a51
userspace/libc: add <sys/socket.h> for socket programming support
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-14 02:18:15 +02:00
dfe9bbdbfa
userspace/libc: implement read, pread, write & pwrite
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-14 02:17:47 +02:00
92ef18b412
userspace/libc: implement alphasort, fdopendir, opendir, posix_getdents, readdir & scandir
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-14 02:17:06 +02:00
d0a8d9dd62
userspace/libc: add <sys/uio.h> header for vector I/O operations
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-14 02:15:40 +02:00
f3e145e6f7
userspace/libc: add <netinet/in.h> header for IPv4 and IPv6 support
FIXME: not sure if this is implemented correctly

ref: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/netinet_in.h.html
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-14 02:15:18 +02:00
d9433256ab
userspace/libc: add <arpa/inet.h>
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-14 02:13:48 +02:00
ade1c77361
userspace/test: add web server test program
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-14 02:11:30 +02:00
3e656854bc
userspace/test: implement more tests in libc_test
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-13 01:14:11 +02:00
6c3eefa85d
userspace/libc: add stub uname function
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-12 02:53:44 +02:00
2384791793
userspace/libc: add stub gethostname function
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-12 02:53:13 +02:00
97af4d855f
userspace/libc: implement <sys/fcntl>
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-12 02:52:39 +02:00
b0f0982fd4
userspace/libc: update function signatures in fcntl.h
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-12 02:52:01 +02:00
bd1d117283
userspace/libc: add ioctl function
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-12 02:51:14 +02:00
a1b040360c
userspace/apps/sys: add uname command
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-12 02:50:27 +02:00
8f7938a1e5
userspace/libc: implement vfprintf function
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-12 02:47:45 +02:00
b05868d120
userspace/libc: implement fprintf function
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-12 02:43:43 +02:00
24fd486764
userspace/libc: implement strncpy function
Signed-off-by: EnderIce2 <enderice2@protonmail.com>
2025-02-12 02:42:51 +02:00
748 changed files with 15176 additions and 17676 deletions

112
.devcontainer/Dockerfile Normal file
View File

@ -0,0 +1,112 @@
FROM mcr.microsoft.com/devcontainers/cpp:1-ubuntu-24.04
ARG REINSTALL_CMAKE_VERSION_FROM_SOURCE="none"
COPY ./reinstall-cmake.sh /tmp/
RUN <<EOF
if [ "${REINSTALL_CMAKE_VERSION_FROM_SOURCE}" != "none" ]; then
chmod +x /tmp/reinstall-cmake.sh && /tmp/reinstall-cmake.sh ${REINSTALL_CMAKE_VERSION_FROM_SOURCE};
fi
rm -f /tmp/reinstall-cmake.sh
EOF
# Update system and set noninteractive env
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update
# Required packages for building gcc & binutils
RUN apt-get -y install --no-install-recommends \
build-essential \
bison \
flex \
libgmp-dev \
libmpfr-dev \
texinfo \
file \
python3-dev
# Required packages for building gcc & binutils
RUN <<EOF
wget https://launchpad.net/ubuntu/+archive/primary/+files/autoconf_2.69-11.1_all.deb -O /tmp/autoconf.deb
sudo dpkg --force-all -i /tmp/autoconf.deb
EOF
# Required packages for building gcc & binutils
RUN <<EOF
wget https://ftp.gnu.org/gnu/automake/automake-1.15.1.tar.gz -O /tmp/automake.tar.gz
tar -xzf /tmp/automake.tar.gz -C /tmp
cd /tmp/automake-1.15.1
./configure && make && sudo make install
EOF
# Required packages for building qemu
RUN apt-get -y install --no-install-recommends \
git \
libglib2.0-dev \
libfdt-dev \
libpixman-1-dev \
zlib1g-dev \
ninja-build \
libaio-dev \
libbluetooth-dev \
libcapstone-dev \
libbrlapi-dev \
libbz2-dev \
libcap-ng-dev \
libcurl4-gnutls-dev \
libgtk-3-dev \
libibverbs-dev \
libjpeg8-dev \
libncurses5-dev \
libnuma-dev \
librbd-dev \
librdmacm-dev \
libsasl2-dev \
libsdl2-dev \
libseccomp-dev \
libsnappy-dev \
libssh-dev \
libvde-dev \
libvdeplug-dev \
libvte-2.91-dev \
libxen-dev \
liblzo2-dev \
valgrind \
xfslibs-dev \
libnfs-dev \
libiscsi-dev \
python3-venv \
libslirp-dev \
ovmf
# Required packages for building test apps in userspace
RUN apt-get -y install --no-install-recommends \
mingw-w64
# Required packages for building the OS
RUN apt-get -y install --no-install-recommends \
grub2-common \
xorriso \
mtools \
grub-common \
grub-efi-amd64-bin \
grub-efi-amd64-signed \
grub-gfxpayload-lists \
grub-pc-bin \
grub-pc \
grub2-common
# Configure git
RUN <<EOF
git config --global advice.detachedHead false
git config --global core.autocrlf input
git config --global init.defaultbranch master
EOF
# Set display environment variable for QEMU
ENV DISPLAY=:0
ENV NO_AT_BRIDGE=1
# Set PATH
RUN <<EOF
echo PATH=$PATH:/workspaces/Fennix/cross/bin >> /etc/profile
EOF

View File

@ -0,0 +1,51 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/cpp
{
"name": "Fennix",
"build": {
"dockerfile": "Dockerfile"
},
"customizations": {
"vscode": {
"extensions": [
"ms-azuretools.vscode-docker",
"eamodio.gitlens",
"EditorConfig.EditorConfig",
"naumovs.color-highlight",
"Gruntfuggly.todo-tree",
"ms-vscode.hexeditor",
"vivaxy.vscode-conventional-commits",
"webfreak.debug",
"maziac.asm-code-lens",
"Seven1bit.vscode-ext-ansi-color-highlight",
"ms-vsliveshare.vsliveshare",
"maziac.hex-hover-converter",
"ms-vscode.makefile-tools"
]
}
},
// From this line below are for qemu, so not that important.
"mounts": [
"source=/tmp/.X11-unix,target=/tmp/.X11-unix,type=bind,consistency=cached",
"source=${localEnv:XAUTHORITY},target=/home/vscode/.Xauthority,type=bind,consistency=cached",
"source=/dev/kvm,target=/dev/kvm,type=bind,consistency=cached",
"source=/run/user/1000/pulse/native,target=/run/user/1000/pulse/native,type=bind,consistency=cached"
],
"runArgs": [
"--privileged"
]
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "gcc -v",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

View File

@ -0,0 +1,59 @@
#!/usr/bin/env bash
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------
#
set -e
CMAKE_VERSION=${1:-"none"}
if [ "${CMAKE_VERSION}" = "none" ]; then
echo "No CMake version specified, skipping CMake reinstallation"
exit 0
fi
# Cleanup temporary directory and associated files when exiting the script.
cleanup() {
EXIT_CODE=$?
set +e
if [[ -n "${TMP_DIR}" ]]; then
echo "Executing cleanup of tmp files"
rm -Rf "${TMP_DIR}"
fi
exit $EXIT_CODE
}
trap cleanup EXIT
echo "Installing CMake..."
apt-get -y purge --auto-remove cmake
mkdir -p /opt/cmake
architecture=$(dpkg --print-architecture)
case "${architecture}" in
arm64)
ARCH=aarch64 ;;
amd64)
ARCH=x86_64 ;;
*)
echo "Unsupported architecture ${architecture}."
exit 1
;;
esac
CMAKE_BINARY_NAME="cmake-${CMAKE_VERSION}-linux-${ARCH}.sh"
CMAKE_CHECKSUM_NAME="cmake-${CMAKE_VERSION}-SHA-256.txt"
TMP_DIR=$(mktemp -d -t cmake-XXXXXXXXXX)
echo "${TMP_DIR}"
cd "${TMP_DIR}"
curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_BINARY_NAME}" -O
curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_CHECKSUM_NAME}" -O
sha256sum -c --ignore-missing "${CMAKE_CHECKSUM_NAME}"
sh "${TMP_DIR}/${CMAKE_BINARY_NAME}" --prefix=/opt/cmake --skip-license
ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake
ln -s /opt/cmake/bin/ctest /usr/local/bin/ctest

View File

@ -15,3 +15,7 @@ indent_style = tab
[.github/**]
indent_style = space
indent_size = 2
[{CMakeLists.txt,*.cmake}]
indent_size = 2
indent_style = space

38
.vscode/launch.json vendored
View File

@ -20,14 +20,19 @@
},
{
"text": "add-symbol-file ${workspaceFolder}/../initrd_tmp_data/bin/utest",
"description": "Load /bin/utest",
"description": "/bin/utest (0x00400000)",
"ignoreFailures": true
},
{
"text": "add-symbol-file ${workspaceFolder}/../initrd_tmp_data/bin/libc_test",
"description": "/bin/libc_test (0x00600000)",
"ignoreFailures": true
},
{
"text": "source ${workspaceFolder}/../tools/.gdbinit"
}
],
"preLaunchTask": "launch-qemu"
"preLaunchTask": "QEMU"
},
{
"name": "Debug Kernel (x86)",
@ -48,14 +53,19 @@
},
{
"text": "add-symbol-file ${workspaceFolder}/../initrd_tmp_data/bin/utest",
"description": "Load /bin/utest",
"description": "/bin/utest (0x00400000)",
"ignoreFailures": true
},
{
"text": "add-symbol-file ${workspaceFolder}/../initrd_tmp_data/bin/libc_test",
"description": "/bin/libc_test (0x00600000)",
"ignoreFailures": true
},
{
"text": "source ${workspaceFolder}/../tools/.gdbinit"
}
],
"preLaunchTask": "launch-qemu"
"preLaunchTask": "QEMU"
},
{
"name": "Debug Kernel (arm64)",
@ -76,14 +86,19 @@
},
{
"text": "add-symbol-file ${workspaceFolder}/../initrd_tmp_data/bin/utest",
"description": "Load /bin/utest",
"description": "/bin/utest (0x00400000)",
"ignoreFailures": true
},
{
"text": "add-symbol-file ${workspaceFolder}/../initrd_tmp_data/bin/libc_test",
"description": "/bin/libc_test (0x00600000)",
"ignoreFailures": true
},
{
"text": "source ${workspaceFolder}/../tools/.gdbinit"
}
],
"preLaunchTask": "launch-qemu"
"preLaunchTask": "QEMU"
},
{
"name": "Debug Kernel (arm)",
@ -104,14 +119,19 @@
},
{
"text": "add-symbol-file ${workspaceFolder}/../initrd_tmp_data/bin/utest",
"description": "Load /bin/utest",
"description": "/bin/utest (0x00400000)",
"ignoreFailures": true
},
{
"text": "add-symbol-file ${workspaceFolder}/../initrd_tmp_data/bin/libc_test",
"description": "/bin/libc_test (0x00600000)",
"ignoreFailures": true
},
{
"text": "source ${workspaceFolder}/../tools/.gdbinit"
}
],
"preLaunchTask": "launch-qemu"
"preLaunchTask": "QEMU"
},
{
"type": "gdb",
@ -129,7 +149,7 @@
],
"internalConsoleOptions": "neverOpen",
"printCalls": false,
"preLaunchTask": "launch-qemu"
"preLaunchTask": "QEMU"
},
]
}

157
.vscode/tasks.json vendored
View File

@ -2,13 +2,160 @@
"version": "2.0.0",
"tasks": [
{
"label": "launch-qemu",
"label": "Build Bootloader",
"type": "shell",
"command": "make -C ../ build && make -C ../ vscode_debug_only",
"isBackground": true,
"problemMatcher": [],
"command": "make -C ../ build_bootloader",
"isBackground": false,
"group": {
"kind": "build",
"isDefault": false
},
"presentation": {
"reveal": "always",
"panel": "shared"
},
"options": {
"shell": {
"executable": "bash",
"args": [
"-c"
]
}
}
},
{
"label": "Build Kernel",
"type": "shell",
"command": "make -C ../ build_kernel",
"isBackground": false,
"dependsOn": [
"Build Bootloader"
],
"group": {
"kind": "build",
"isDefault": false
},
"presentation": {
"reveal": "always",
"panel": "shared"
},
"options": {
"shell": {
"executable": "bash",
"args": [
"-c"
]
}
}
},
{
"label": "Build Drivers",
"type": "shell",
"command": "make -C ../ build_drivers",
"isBackground": false,
"dependsOn": [
"Build Kernel"
],
"group": {
"kind": "build",
"isDefault": false
},
"presentation": {
"reveal": "always",
"panel": "shared"
},
"options": {
"shell": {
"executable": "bash",
"args": [
"-c"
]
}
}
},
{
"label": "Build Userspace",
"type": "shell",
"command": "make -C ../ build_userspace",
"isBackground": false,
"dependsOn": [
"Build Drivers"
],
"group": {
"kind": "build",
"isDefault": false
},
"presentation": {
"reveal": "always",
"panel": "shared"
},
"options": {
"shell": {
"executable": "bash",
"args": [
"-c"
]
}
}
},
{
"label": "Build Image",
"type": "shell",
"command": "make -C ../ build_image",
"isBackground": false,
"dependsOn": [
"Build Userspace"
],
"group": {
"kind": "build",
"isDefault": false
},
"presentation": {
"reveal": "always",
"panel": "shared"
},
"options": {
"shell": {
"executable": "bash",
"args": [
"-c"
]
}
}
},
{
"label": "QEMU",
"type": "shell",
"command": "make",
"args": [
"-C",
"../",
"vscode_debug_only"
],
"isBackground": true,
// "problemMatcher": "$tsc-watch",
"problemMatcher": [
{
"pattern": [
{
"regexp": ".",
"file": 1,
"location": 2,
"message": 3
}
],
"background": {
"activeOnStart": true,
"beginsPattern": ".",
"endsPattern": "CR0 update",
}
}
],
"dependsOn": [
"Build Image"
],
"group": {
"kind": "test",
"isDefault": true
},
"presentation": {
@ -25,4 +172,4 @@
}
}
]
}
}

View File

@ -1,35 +0,0 @@
default:
$(error Do not run this Makefile directly!)
S_SOURCES = $(shell find ./ -type f -name '*.S')
C_SOURCES = $(shell find ./ -type f -name '*.c')
CXX_SOURCES = $(shell find ./ -type f -name '*.cpp')
HEADERS = $(sort $(dir $(wildcard ../../include/*)))
OBJ = $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) $(S_SOURCES:.S=.o)
STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CXX_SOURCES:.cpp=.su)
FILENAME = $(notdir $(shell pwd)).drv
build: $(FILENAME)
mv $(FILENAME) $(OUTPUT_DIR)$(FILENAME)
$(FILENAME): $(OBJ)
$(info Linking $@)
$(CC) $(DRIVER_LDFLAGS) $(OBJ) $(OUTPUT_DIR)dcrt0.o -L$(OUTPUT_DIR) -lkernel -o $@
WARNCFLAG = -Wall -Wextra
%.o: %.c $(HEADERS)
$(info Compiling $<)
$(CC) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@
%.o: %.cpp $(HEADERS)
$(info Compiling $<)
$(CXX) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@
%.o: %.S
$(info Compiling $<)
$(AS) -o $@ $<
clean:
rm -f file.map $(OBJ) $(STACK_USAGE_OBJ)

View File

@ -1,881 +0,0 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <audio.h>
#include <regs.h>
#include <base.h>
#include <pci.h>
#include <io.h>
#include <fs.h>
#define DescriptorListLength 0x20
enum AudioVolumeValues
{
AV_Maximum = 0x0,
AV_Minimum = 0x3F,
};
enum AudioEncodingValues
{
AE_PCMs8,
AE_PCMu8,
AE_PCMs16le,
AE_PCMs20le,
AE_PCMs24le,
AE_PCMs32le,
AE_PCMu16le,
AE_PCMu20le,
AE_PCMu24le,
AE_PCMu32le,
AE_PCMs16be,
AE_PCMs20be,
AE_PCMs24be,
AE_PCMs32be,
AE_PCMu16be,
AE_PCMu20be,
AE_PCMu24be,
AE_PCMu32be,
};
enum NativeAudioMixerRegisters
{
/**
* @brief Reset Register
* @note Length: word
*/
NAM_Reset = 0x00,
/**
* @brief Master Volume Register
* @note Length: word
*/
NAM_MasterVolume = 0x02,
/**
* @brief Microphone Volume Register
* @note Length: word
*/
NAM_MicrophoneVolume = 0x0E,
/**
* @brief PCM Out Volume Register
* @note Length: word
*/
NAM_PCMOutVolume = 0x18,
/**
* @brief Select Record Input Register
* @note Length: word
*/
NAM_SelectRecordInput = 0x1A,
/**
* @brief Record Gain Register
* @note Length: word
*/
NAM_RecordGain = 0x1C,
/**
* @brief Record Gain Microphone Register
* @note Length: word
*/
NAM_RecordGainMicrophone = 0x1E,
};
enum NativeAudioBusMasterRegisters
{
/**
* @brief Register box for PCM IN
* @note Length: below
*/
NABM_PCMInBox = 0x00,
/**
* @brief Register box for PCM OUT
* @note Length: below
*/
NABM_PCMOutBox = 0x10,
/**
* @brief Register box for Microphone
* @note Length: below
*/
NABM_MicrophoneBox = 0x20,
/**
* @brief Global Control Register
* @note Length: dword
*/
NABM_GlobalControl = 0x2C, /* 0x30 */
/**
* @brief Global Status Register
* @note Length: dword
*/
NABM_GlobalStatus = 0x30, /* 0x34 */
};
enum NativeAudioBusMasterBoxOffsets
{
/**
* @brief Physical Address of Buffer Descriptor List
* @note Length: dword
*/
NABMBOFF_BufferDescriptorList = 0x00,
/**
* @brief Number of Actual Processed Buffer Descriptor Entry
* @note Length: byte
*/
NABMBOFF_BufferDescriptorEntry = 0x04,
/**
* @brief Number of all Descriptor Entries
* @note Length: byte
*/
NABMBOFF_DescriptorEntries = 0x05,
/**
* @brief Status of transferring Data
* @note Length: word
*/
NABMBOFF_Status = 0x06,
/**
* @brief Number of transferred Samples in Actual Processed Entry
* @note Length: word
*/
NABMBOFF_TransferredSamples = 0x08,
/**
* @brief Number of next processed Buffer Entry
* @note Length: byte
*/
NABMBOFF_NextProcessedBufferEntry = 0x0A,
/**
* @brief Transfer Control
* @note Length: byte
*/
NABMBOFF_TransferControl = 0x0B,
};
enum OutputPulseCodeModulationRegisters
{
/**
* @brief Physical Address of Buffer Descriptor List
* @note Length: dword
*/
PCMOUT_BufferDescriptorList = (int)NABM_PCMOutBox + (int)NABMBOFF_BufferDescriptorList,
/**
* @brief Number of Actual Processed Buffer Descriptor Entry
* @note Length: byte
*/
PCMOUT_BufferDescriptorEntry = (int)NABM_PCMOutBox + (int)NABMBOFF_BufferDescriptorEntry,
/**
* @brief Number of all Descriptor Entries
* @note Length: byte
*/
PCMOUT_DescriptorEntries = (int)NABM_PCMOutBox + (int)NABMBOFF_DescriptorEntries,
/**
* @brief Status of transferring Data
* @note Length: word
*/
PCMOUT_Status = (int)NABM_PCMOutBox + (int)NABMBOFF_Status,
/**
* @brief Number of transferred Samples in Actual Processed Entry
* @note Length: word
*/
PCMOUT_TransferredSamples = (int)NABM_PCMOutBox + (int)NABMBOFF_TransferredSamples,
/**
* @brief Number of next processed Buffer Entry
* @note Length: byte
*/
PCMOUT_NextProcessedBufferEntry = (int)NABM_PCMOutBox + (int)NABMBOFF_NextProcessedBufferEntry,
/**
* @brief Transfer Control
* @note Length: byte
*/
PCMOUT_TransferControl = (int)NABM_PCMOutBox + (int)NABMBOFF_TransferControl,
};
enum TransferControlRegisters
{
/**
* @brief DMA controller control
*
* 0 = Pause transfer
* 1 = Transfer sound data
*/
TC_DMAControllerControl = 0x01,
/**
* @brief Reset
*
* 0 = Remove reset condition
* 1 = Reset this NABM register box, this bit is cleared by card when is reset complete
*/
TC_TransferReset = 0x02,
/**
* @brief Last Buffer Entry Interrupt enable
*
* 0 = Disable interrupt
* 1 = Enable interrupt
*/
TC_LastBufferEntryInterruptEnable = 0x04,
/**
* @brief IOC Interrupt enable
*
* 0 = Disable interrupt
* 1 = Enable interrupt
*/
TC_IOCInterruptEnable = 0x08,
/**
* @brief Fifo ERROR Interrupt enable
*
* 0 = Disable interrupt
* 1 = Enable interrupt
*/
TC_FifoERRORInterruptEnable = 0x10,
};
enum GlobalControlRegisters
{
/**
* @brief Global Interrupt Enable
*
* 0 = Disable Interrupts
* 1 = Enable Interrupts
*/
GC_GlobalInterruptEnable = 0x01,
/**
* @brief Cold reset
*
* 0 = Device is in reset and can not be used
* 1 = Resume to operational state
*/
GC_ColdReset = 0x02,
/**
* @brief Warm reset
*/
GC_WarmReset = 0x04,
/**
* @brief Shut down
*
* 0 = Device is powered
* 1 = Shut down
*/
GC_ShutDown = 0x08,
/**
* @brief Channels for PCM Output
*
* 00 = 2 channels
* 01 = 4 channels
* 10 = 6 channels
* 11 = Reserved
*/
GC_ChannelsForPCMOutput = 0x30,
/**
* @brief PCM Output mode
*
* 00 = 16 bit samples
* 01 = 20 bit samples
*/
GC_PCMOutputMode = 0xC0,
};
struct BufferDescriptorList
{
/**
* @brief Physical Address to sound data in memory
* @note Length: dword
*/
uint32_t Address;
/**
* @brief Number of samples in this buffer
* @note Length: word
*/
uint16_t SampleCount;
/**
* @brief Flags
* @note Length: word
*
* Bit 15 = Interrupt fired when data from this entry is transferred
* Bit 14 = Last entry of buffer, stop playing
* Other bits = Reserved
*/
uint16_t Flags;
} __attribute__((packed));
uint16_t MixerVolume(uint8_t Left, uint8_t Right, bool Mute)
{
return ((uint16_t)((Right & 0x3F) |
((Left & 0x3F) << 0x8) |
(Mute & 1 << 0xF)));
}
class AC97Device
{
private:
PCIHeader0 *Header;
BufferDescriptorList *DescriptorList = nullptr;
uint16_t MixerAddress;
uint16_t BusMasterAddress;
AudioEncodingValues Encoding = AE_PCMs16le;
char Channels = 2;
uint8_t Volume = AV_Maximum;
bool Mute = false;
int SampleRate = 48000;
char SampleSize = 2;
public:
size_t write(uint8_t *Buffer, size_t Size)
{
if (Buffer == nullptr)
{
KernelLog("Invalid buffer.");
return -EINVAL;
}
if ((Size == 0) || (Size % (SampleSize * Channels)))
{
KernelLog("Invalid buffer length.");
return -EINVAL;
}
int TotalBDLToFill = (int)((Size + PAGE_SIZE - 1) >> 12);
while (Size > 0)
{
bool ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl);
if (ActiveDMA)
{
int RemainingBDL = 0;
do
{
int CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry);
int LastBDL = inb(BusMasterAddress + PCMOUT_DescriptorEntries);
RemainingBDL = LastBDL - CurrentBDL;
if (RemainingBDL < 0)
RemainingBDL += DescriptorListLength;
RemainingBDL += 1;
if (RemainingBDL >= DescriptorListLength - 1)
{
long SampleCount = DescriptorList[(CurrentBDL + 1) % DescriptorListLength].SampleCount / Channels;
if (SampleCount > 0)
Sleep(SampleCount * 1000 / SampleRate);
}
} while (RemainingBDL >= DescriptorListLength - 1 &&
!(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl));
}
uint8_t CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry);
uint8_t LastBDL = inb(BusMasterAddress + PCMOUT_DescriptorEntries);
uint8_t NextBDL = LastBDL % DescriptorListLength;
ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl);
if (ActiveDMA)
{
NextBDL = (uint8_t)((LastBDL + 1) % DescriptorListLength);
if (NextBDL == CurrentBDL)
continue;
}
do
{
size_t Wrote = (PAGE_SIZE > Size) ? size_t(Size)
: size_t(PAGE_SIZE);
if (Wrote == 0)
{
KernelLog("Wrote 0 bytes.");
break;
}
memcpy((void *)((uint64_t)DescriptorList[NextBDL].Address), Buffer, Wrote);
DescriptorList[NextBDL].Flags = 0;
Buffer += Wrote;
Size -= (unsigned int)Wrote;
DescriptorList[NextBDL].SampleCount = uint16_t(Wrote / SampleSize);
TotalBDLToFill--;
NextBDL = (uint8_t)((NextBDL + 1) % DescriptorListLength);
} while (TotalBDLToFill-- && NextBDL != CurrentBDL);
outb(BusMasterAddress + PCMOUT_DescriptorEntries, NextBDL - 1);
ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl);
if (!ActiveDMA)
{
// Start DMA
outb(BusMasterAddress + PCMOUT_TransferControl,
inb(BusMasterAddress + PCMOUT_TransferControl) | TC_DMAControllerControl);
}
}
return Size;
}
int ioctl(AudioIoctl, void *)
{
// if (Data->AudioCallback.Adjust._Volume)
// {
// Volume = (uint8_t)(0x3F - (0x3F * Data->AudioCallback.Adjust.Volume / 100));
// outw(BAR.MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute));
// // outw(BAR.MixerAddress + NAM_PCMOutVolume, MixerVolume(Volume, Volume, Mute));
// }
// else if (Data->AudioCallback.Adjust._Encoding)
// {
// fixme("Encoding changing not supported yet.");
// }
// else if (Data->AudioCallback.Adjust._SampleRate)
// {
// switch (Data->AudioCallback.Adjust.SampleRate)
// {
// case 0:
// {
// SampleRate = 8000;
// break;
// }
// case 1:
// {
// SampleRate = 11025;
// break;
// }
// case 2:
// {
// SampleRate = 16000;
// break;
// }
// case 3:
// {
// SampleRate = 22050;
// break;
// }
// case 4:
// {
// SampleRate = 32000;
// break;
// }
// case 5:
// {
// SampleRate = 44100;
// break;
// }
// case 6:
// {
// SampleRate = 48000;
// break;
// }
// case 7:
// {
// SampleRate = 88200;
// break;
// }
// case 8:
// {
// SampleRate = 96000;
// break;
// }
// default:
// {
// SampleRate = 16000;
// error("Invalid sample rate. Defaulting to 16000.");
// break;
// }
// }
// }
// else if (Data->AudioCallback.Adjust._Channels)
// {
// switch (Data->AudioCallback.Adjust.Channels)
// {
// case 0:
// {
// Channels = 1; // Mono
// break;
// }
// case 1:
// {
// Channels = 2; // Stereo
// break;
// }
// default:
// {
// Channels = 2;
// error("Invalid channel count. Defaulting to 2.");
// break;
// }
// }
// }
return 0;
}
void OnInterruptReceived(TrapFrame *)
{
uint16_t Status = inw(MixerAddress + PCMOUT_Status);
if (Status & TC_IOCInterruptEnable)
{
DebugLog("IOC");
outw(MixerAddress + PCMOUT_Status, TC_IOCInterruptEnable);
uint16_t CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry);
uint16_t LastBDL = (CurrentBDL + 2) & (DescriptorListLength - 1);
outb(BusMasterAddress + PCMOUT_DescriptorEntries, LastBDL);
KernelLog("FIXME: CurrentBDL: %d, LastBDL: %d", CurrentBDL, LastBDL);
}
else if (Status & TC_LastBufferEntryInterruptEnable)
{
DebugLog("Last buffer entry");
// Stop DMA
uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl));
TransferControl &= ~TC_DMAControllerControl;
outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl);
outw(MixerAddress + PCMOUT_Status, TC_LastBufferEntryInterruptEnable);
}
else if (Status & TC_FifoERRORInterruptEnable)
{
KernelLog("FIFO error");
outw(MixerAddress + PCMOUT_Status, TC_FifoERRORInterruptEnable);
}
else
{
DebugLog("Unknown interrupt status %#x", Status);
outw(MixerAddress + PCMOUT_Status, 0xFFFF);
}
}
void Panic()
{
uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl));
TransferControl &= ~(TC_LastBufferEntryInterruptEnable |
TC_IOCInterruptEnable |
TC_FifoERRORInterruptEnable);
outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl);
uint32_t GlobalControl = inl((uint16_t)(BusMasterAddress + NABM_GlobalControl));
GlobalControl &= ~GC_GlobalInterruptEnable;
GlobalControl |= GC_ShutDown;
outl((uint16_t)(BusMasterAddress + NABM_GlobalControl), GlobalControl);
}
AC97Device(PCIHeader0 *_Header)
: Header(_Header)
{
/* Native Audio Mixer Base Address */
uint32_t PCIBAR0 = Header->BAR0;
/* Native Audio Bus Master Base Address */
uint32_t PCIBAR1 = Header->BAR1;
// uint8_t Type = PCIBAR0 & 1;
MixerAddress = (uint16_t)(PCIBAR0 & (~3));
BusMasterAddress = PCIBAR1 & (~15);
uint16_t OutputPCMTransferControl = BusMasterAddress + PCMOUT_TransferControl;
/* DescriptorList address MUST be physical. */
DescriptorList = (BufferDescriptorList *)AllocateMemory(TO_PAGES(sizeof(BufferDescriptorList) * DescriptorListLength));
memset(DescriptorList, 0, sizeof(BufferDescriptorList) * DescriptorListLength);
uint16_t DLSampleCount = (uint16_t)(PAGE_SIZE / SampleSize);
for (int i = 0; i < DescriptorListLength; i++)
{
DescriptorList[i].Address = (uint32_t)(uintptr_t)AllocateMemory(TO_PAGES(sizeof(uint16_t *)));
DescriptorList[i].SampleCount = DLSampleCount;
DescriptorList[i].Flags = 0;
DebugLog("DescriptorList[%d] = { Address: %#lx, SampleCount: %d, Flags: %#lx }",
i,
DescriptorList[i].Address,
DescriptorList[i].SampleCount,
DescriptorList[i].Flags);
}
outw(MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute));
outw(MixerAddress + NAM_PCMOutVolume, MixerVolume(Volume, Volume, Mute));
Volume = 0x3F - (0x3F * /* VOL 50% */ 50 / 100);
outw(MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute));
outb(OutputPCMTransferControl, inb(OutputPCMTransferControl) | TC_TransferReset);
while (inb(OutputPCMTransferControl) & TC_TransferReset)
;
uint32_t GlobalControl = inl(BusMasterAddress + NABM_GlobalControl);
GlobalControl = (GlobalControl & ~((0x3U) << 0x16)); /* PCM 16-bit mode */
GlobalControl = (GlobalControl & ~((0x3U) << 20)); /* 2 channels */
GlobalControl |= GC_GlobalInterruptEnable;
GlobalControl &= ~GC_ShutDown;
outl(BusMasterAddress + PCMOUT_BufferDescriptorList,
(uint32_t)(uint64_t)DescriptorList);
outl(BusMasterAddress + NABM_GlobalControl, GlobalControl);
uint8_t TransferControl = inb(OutputPCMTransferControl);
TransferControl |= TC_IOCInterruptEnable |
TC_FifoERRORInterruptEnable;
outb(OutputPCMTransferControl, TransferControl);
// Stop DMA
outb(OutputPCMTransferControl, inb(OutputPCMTransferControl) & ~TC_DMAControllerControl);
}
~AC97Device()
{
outw(MixerAddress + NAM_MasterVolume, MixerVolume(AV_Maximum, AV_Maximum, true));
outw(MixerAddress + NAM_PCMOutVolume, MixerVolume(AV_Maximum, AV_Maximum, true));
// Stop DMA
outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl),
inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl)) & ~TC_DMAControllerControl);
// Disable interrupts
uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl));
TransferControl &= ~(TC_LastBufferEntryInterruptEnable |
TC_IOCInterruptEnable |
TC_FifoERRORInterruptEnable);
outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl);
// Disable global control
uint32_t GlobalControl = inl((uint16_t)(BusMasterAddress + NABM_GlobalControl));
GlobalControl &= ~GC_GlobalInterruptEnable;
GlobalControl |= GC_ShutDown;
outl((uint16_t)(BusMasterAddress + NABM_GlobalControl), GlobalControl);
}
};
AC97Device *Drivers[4] = {nullptr};
dev_t AudioID[4] = {(dev_t)-1};
#define OIR(x) OIR_##x
#define CREATE_OIR(x) \
void OIR_##x(TrapFrame *f) { Drivers[x]->OnInterruptReceived(f); }
CREATE_OIR(0);
CREATE_OIR(1);
CREATE_OIR(2);
CREATE_OIR(3);
int __fs_Open(struct Inode *, int, mode_t) { return 0; }
int __fs_Close(struct Inode *) { return 0; }
ssize_t __fs_Read(struct Inode *, void *, size_t, off_t) { return 0; }
ssize_t __fs_Write(struct Inode *Node, const void *Buffer, size_t Size, off_t)
{
return Drivers[AudioID[Node->GetMinor()]]->write((uint8_t *)Buffer, Size);
}
int __fs_Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
{
return Drivers[AudioID[Node->GetMinor()]]->ioctl((AudioIoctl)Request, Argp);
}
const struct InodeOperations AudioOps = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = __fs_Read,
.Write = __fs_Write,
.Truncate = nullptr,
.Open = __fs_Open,
.Close = __fs_Close,
.Ioctl = __fs_Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
PCIArray *Devices;
EXTERNC int cxx_Panic()
{
PCIArray *ctx = Devices;
short Count = 0;
while (ctx != nullptr)
{
if (Drivers[Count] != nullptr)
Drivers[Count]->Panic();
Count++;
ctx = (PCIArray *)ctx->Next;
}
return 0;
}
EXTERNC int cxx_Probe()
{
uint16_t VendorIDs[] = {0x8086, PCI_END};
uint16_t DeviceIDs[] = {0x2415, PCI_END};
Devices = GetPCIDevices(VendorIDs, DeviceIDs);
if (Devices == nullptr)
{
KernelLog("No AC'97 device found.");
return -ENODEV;
}
PCIArray *ctx = Devices;
bool Found = false;
size_t Count = 0;
while (ctx != nullptr)
{
if (Count++ > sizeof(Drivers) / sizeof(AC97Device *))
break;
PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header;
uint32_t PCIBAR0 = PCIBaseAddress->BAR0;
uint8_t Type = PCIBAR0 & 1;
if (Type != 1)
{
KernelLog("Device %x:%x.%d BAR0 is not I/O.",
PCIBaseAddress->Header.VendorID,
PCIBaseAddress->Header.DeviceID,
PCIBaseAddress->Header.ProgIF);
continue;
}
Found = true;
ctx = (PCIArray *)ctx->Next;
}
if (!Found)
{
KernelLog("No valid AC'97 device found.");
return -EINVAL;
}
return 0;
}
EXTERNC int cxx_Initialize()
{
PCIArray *ctx = Devices;
size_t Count = 0;
while (ctx != nullptr)
{
if (Count > sizeof(Drivers) / sizeof(AC97Device *))
break;
PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header;
uint32_t PCIBAR0 = PCIBaseAddress->BAR0;
uint8_t Type = PCIBAR0 & 1;
if (Type != 1)
{
KernelLog("Device %x:%x.%d BAR0 is not I/O.",
PCIBaseAddress->Header.VendorID,
PCIBaseAddress->Header.DeviceID,
PCIBaseAddress->Header.ProgIF);
continue;
}
InitializePCI(ctx->Device);
Drivers[Count] = new AC97Device((PCIHeader0 *)ctx->Device->Header);
/* FIXME: bad code */
switch (Count)
{
case 0:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(0));
break;
case 1:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(1));
break;
case 2:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(2));
break;
case 3:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(3));
break;
default:
break;
}
dev_t ret = RegisterDevice(AUDIO_TYPE_PCM, &AudioOps);
AudioID[Count] = ret;
Count++;
ctx = (PCIArray *)ctx->Next;
}
return 0;
}
EXTERNC int cxx_Finalize()
{
PCIArray *ctx = Devices;
size_t Count = 0;
while (ctx != nullptr)
{
if (Count++ > sizeof(Drivers) / sizeof(AC97Device *))
break;
PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header;
uint32_t PCIBAR0 = PCIBaseAddress->BAR0;
uint8_t Type = PCIBAR0 & 1;
if (Type != 1)
{
KernelLog("Device %x:%x.%d BAR0 is not I/O.",
PCIBaseAddress->Header.VendorID,
PCIBaseAddress->Header.DeviceID,
PCIBaseAddress->Header.ProgIF);
continue;
}
delete Drivers[Count++];
ctx->Device->Header->Command |= PCI_COMMAND_INTX_DISABLE;
ctx = (PCIArray *)ctx->Next;
}
for (size_t i = 0; i < sizeof(AudioID) / sizeof(dev_t); i++)
{
if (AudioID[i] != (dev_t)-1)
UnregisterDevice(AudioID[i]);
}
return 0;
}

View File

@ -1,25 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <types.h>
EXTERNC int cxx_Panic();
EXTERNC int cxx_Probe();
EXTERNC int cxx_Initialize();
EXTERNC int cxx_Finalize();

View File

@ -1,31 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include <base.h>
#include "ac97.hpp"
int DriverEntry() { return cxx_Initialize(); }
int DriverFinal() { return cxx_Finalize(); }
int DriverPanic() { return cxx_Panic(); }
int DriverProbe() { return cxx_Probe(); }
DriverInfo("ac97",
"Audio Codec '97 Driver",
"EnderIce2",
0, 0, 1,
"GPLv3");

View File

@ -1,35 +0,0 @@
default:
$(error Do not run this Makefile directly!)
S_SOURCES = $(shell find ./ -type f -name '*.S')
C_SOURCES = $(shell find ./ -type f -name '*.c')
CXX_SOURCES = $(shell find ./ -type f -name '*.cpp')
HEADERS = $(sort $(dir $(wildcard ../../include/*)))
OBJ = $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) $(S_SOURCES:.S=.o)
STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CXX_SOURCES:.cpp=.su)
FILENAME = $(notdir $(shell pwd)).drv
build: $(FILENAME)
mv $(FILENAME) $(OUTPUT_DIR)$(FILENAME)
$(FILENAME): $(OBJ)
$(info Linking $@)
$(CC) $(DRIVER_LDFLAGS) $(OBJ) $(OUTPUT_DIR)dcrt0.o -L$(OUTPUT_DIR) -lkernel -o $@
WARNCFLAG = -Wall -Wextra
%.o: %.c $(HEADERS)
$(info Compiling $<)
$(CC) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@
%.o: %.cpp $(HEADERS)
$(info Compiling $<)
$(CXX) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@
%.o: %.S
$(info Compiling $<)
$(AS) -o $@ $<
clean:
rm -f file.map $(OBJ) $(STACK_USAGE_OBJ)

View File

@ -1,263 +0,0 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <audio.h>
#include <regs.h>
#include <base.h>
#include <pci.h>
#include <io.h>
#include <fs.h>
#include "hda.hpp"
class HDADevice
{
private:
PCIHeader0 *Header;
bool Initialized = false;
ControllerRegisters *CTL;
uint32_t *CORB;
uint64_t *RIRB;
public:
bool IsInitialized() { return Initialized; }
size_t write(uint8_t *, size_t Size)
{
return Size;
}
int ioctl(AudioIoctl, void *)
{
return 0;
}
void OnInterruptReceived(TrapFrame *)
{
}
void Panic()
{
}
HDADevice(PCIHeader0 *_Header)
: Header(_Header),
CORB((uint32_t *)(uintptr_t)AllocateMemory(1)),
RIRB((uint64_t *)AllocateMemory(1))
{
CTL = (ControllerRegisters *)(uintptr_t)Header->BAR0;
KernelLog("Unimplemented HDA driver");
return;
Initialized = true;
}
~HDADevice()
{
if (!Initialized)
return;
}
};
HDADevice *Drivers[4] = {nullptr};
dev_t AudioID[4] = {(dev_t)-1};
#define OIR(x) OIR_##x
#define CREATE_OIR(x) \
void OIR_##x(TrapFrame *f) { Drivers[x]->OnInterruptReceived(f); }
CREATE_OIR(0);
CREATE_OIR(1);
CREATE_OIR(2);
CREATE_OIR(3);
int __fs_Open(struct Inode *, int, mode_t) { return 0; }
int __fs_Close(struct Inode *) { return 0; }
ssize_t __fs_Read(struct Inode *, void *, size_t, off_t) { return 0; }
ssize_t __fs_Write(struct Inode *Node, const void *Buffer, size_t Size, off_t)
{
return Drivers[AudioID[Node->GetMinor()]]->write((uint8_t *)Buffer, Size);
}
int __fs_Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
{
return Drivers[AudioID[Node->GetMinor()]]->ioctl((AudioIoctl)Request, Argp);
}
const struct InodeOperations AudioOps = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = __fs_Read,
.Write = __fs_Write,
.Truncate = nullptr,
.Open = __fs_Open,
.Close = __fs_Close,
.Ioctl = __fs_Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
PCIArray *Devices;
EXTERNC int cxx_Panic()
{
PCIArray *ctx = Devices;
short Count = 0;
while (ctx != nullptr)
{
if (Drivers[Count] != nullptr)
Drivers[Count]->Panic();
Count++;
ctx = (PCIArray *)ctx->Next;
}
return 0;
}
EXTERNC int cxx_Probe()
{
uint16_t VendorIDs[] = {0x8086, /* Intel */
0x15AD, /* VMware */
PCI_END};
uint16_t DeviceIDs[] = {0x9D71 /* Sunrise Point-LP HD Audio */,
0x2668 /* ICH6 */,
0x293E /* ICH9 */,
PCI_END};
Devices = GetPCIDevices(VendorIDs, DeviceIDs);
if (Devices == nullptr)
{
KernelLog("No HDA device found.");
return -ENODEV;
}
PCIArray *ctx = Devices;
bool Found = false;
size_t Count = 0;
while (ctx != nullptr)
{
if (Count++ > sizeof(Drivers) / sizeof(HDADevice *))
break;
PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header;
uint32_t PCIBAR0 = PCIBaseAddress->BAR0;
uint8_t Type = PCIBAR0 & 1;
if (Type == 1)
{
KernelLog("Device %x:%x.%d BAR0 is I/O.",
PCIBaseAddress->Header.VendorID,
PCIBaseAddress->Header.DeviceID,
PCIBaseAddress->Header.ProgIF);
continue;
}
Found = true;
ctx = (PCIArray *)ctx->Next;
}
if (!Found)
{
KernelLog("No valid HDA device found.");
return -EINVAL;
}
return 0;
}
EXTERNC int cxx_Initialize()
{
PCIArray *ctx = Devices;
size_t Count = 0;
while (ctx != nullptr)
{
if (Count > sizeof(Drivers) / sizeof(HDADevice *))
break;
PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header;
uint32_t PCIBAR0 = PCIBaseAddress->BAR0;
uint8_t Type = PCIBAR0 & 1;
if (Type == 1)
{
KernelLog("Device %x:%x.%d BAR0 is I/O.",
PCIBaseAddress->Header.VendorID,
PCIBaseAddress->Header.DeviceID,
PCIBaseAddress->Header.ProgIF);
continue;
}
InitializePCI(ctx->Device);
Drivers[Count] = new HDADevice((PCIHeader0 *)ctx->Device->Header);
if (Drivers[Count]->IsInitialized())
{
dev_t ret = RegisterDevice(AUDIO_TYPE_PCM, &AudioOps);
AudioID[Count] = ret;
Count++;
}
ctx = (PCIArray *)ctx->Next;
}
if (Count == 0)
{
KernelLog("No valid HDA device found.");
return -EINVAL;
}
return 0;
}
EXTERNC int cxx_Finalize()
{
PCIArray *ctx = Devices;
size_t Count = 0;
while (ctx != nullptr)
{
if (Count++ > sizeof(Drivers) / sizeof(HDADevice *))
break;
PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header;
uint32_t PCIBAR0 = PCIBaseAddress->BAR0;
uint8_t Type = PCIBAR0 & 1;
if (Type == 1)
{
KernelLog("Device %x:%x.%d BAR0 is I/O.",
PCIBaseAddress->Header.VendorID,
PCIBaseAddress->Header.DeviceID,
PCIBaseAddress->Header.ProgIF);
continue;
}
delete Drivers[Count++];
ctx = (PCIArray *)ctx->Next;
}
for (size_t i = 0; i < sizeof(AudioID) / sizeof(dev_t); i++)
{
if (AudioID[i] != (dev_t)-1)
UnregisterDevice(AudioID[i]);
}
return 0;
}

View File

@ -1,31 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include <base.h>
#include "hda.hpp"
int DriverEntry() { return cxx_Initialize(); }
int DriverFinal() { return cxx_Finalize(); }
int DriverPanic() { return cxx_Panic(); }
int DriverProbe() { return cxx_Probe(); }
DriverInfo("hda",
"Intel High Definition Audio Driver",
"EnderIce2",
0, 0, 1,
"GPLv3");

View File

@ -1,35 +0,0 @@
default:
$(error Do not run this Makefile directly!)
S_SOURCES = $(shell find ./ -type f -name '*.S')
C_SOURCES = $(shell find ./ -type f -name '*.c')
CXX_SOURCES = $(shell find ./ -type f -name '*.cpp')
HEADERS = $(sort $(dir $(wildcard ../../include/*)))
OBJ = $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) $(S_SOURCES:.S=.o)
STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CXX_SOURCES:.cpp=.su)
FILENAME = $(notdir $(shell pwd)).drv
build: $(FILENAME)
mv $(FILENAME) $(OUTPUT_DIR)$(FILENAME)
$(FILENAME): $(OBJ)
$(info Linking $@)
$(CC) $(DRIVER_LDFLAGS) $(OBJ) $(OUTPUT_DIR)dcrt0.o -L$(OUTPUT_DIR) -lkernel -o $@
WARNCFLAG = -Wall -Wextra
%.o: %.c $(HEADERS)
$(info Compiling $<)
$(CC) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@
%.o: %.cpp $(HEADERS)
$(info Compiling $<)
$(CXX) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@
%.o: %.S
$(info Compiling $<)
$(AS) -o $@ $<
clean:
rm -f file.map $(OBJ) $(STACK_USAGE_OBJ)

View File

@ -1,45 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include <base.h>
int DriverEntry()
{
return 0;
}
int DriverFinal()
{
return 0;
}
int DriverPanic()
{
return 0;
}
int DriverProbe()
{
/* Nothing to do */
return 0;
}
DriverInfo("fat",
"File Allocation Table Driver",
"EnderIce2",
0, 0, 1,
"GPLv3");

View File

@ -103,6 +103,7 @@ typedef union
uint8_t Raw;
} PS2_OUTPUT_PORT;
#ifndef __kernel__
void PIC_EOI(uint8_t IRQ);
void IRQ_MASK(uint8_t IRQ);
void IRQ_UNMASK(uint8_t IRQ);
@ -114,11 +115,11 @@ uint8_t PS2ReadStatus();
uint8_t PS2ReadAfterACK();
void PS2ClearOutputBuffer();
int PS2ACKTimeout();
#endif // !__kernel__
#define WaitOutput PS2Wait(DriverID, true)
#define WaitInput PS2Wait(DriverID, false)
#define PS2_KBD_CMD_SET_LEDS 0xED
#define PS2_KBD_CMD_ECHO 0xEE
#define PS2_KBD_CMD_SCAN_CODE_SET 0xF0
@ -189,7 +190,6 @@ typedef union
uint8_t Raw;
} PS2_KBD_TYPEMATIC;
#define PS2_MOUSE_CMD_SET_SCALING_1_1 0xE6
#define PS2_MOUSE_CMD_SET_SCALING_2_1 0xE7
#define PS2_MOUSE_CMD_SET_RESOLUTION 0xE8

View File

@ -69,7 +69,10 @@ typedef enum
BLOCK_TYPE_FLOPPY = DEVICE_TYPE_BLOCK + 128,
} DeviceType;
#ifndef __kernel__
EXTERNC dev_t CreateDeviceFile(const char *name, mode_t mode, const struct InodeOperations *Operations);
EXTERNC dev_t RegisterDevice(DeviceType Type, const struct InodeOperations *Operations);
EXTERNC int UnregisterDevice(dev_t Device);
#endif // !__kernel__
#endif // !__FENNIX_API_DEVICE_H__

View File

@ -377,7 +377,9 @@ struct FileSystemInfo
void *PrivateData;
} __attribute__((packed));
#ifndef __kernel__
dev_t RegisterFileSystem(struct FileSystemInfo *Info, struct Inode *Root);
int UnregisterFileSystem(dev_t Device);
#endif // !__kernel__
#endif // !__FENNIX_API_FILESYSTEM_H__

View File

@ -239,6 +239,8 @@ typedef struct
};
} InputReport;
#ifndef __kernel__
EXTERNC int ReportInputEvent(InputReport *Report);
#endif // !__kernel__
#endif // !__FENNIX_API_INPUT_H__

View File

@ -171,11 +171,13 @@ extern "C"
{
#endif
#ifndef __kernel__
PCIArray *GetPCIDevices(uint16_t Vendors[], uint16_t Devices[]);
void InitializePCI(PCIDevice *Device);
uint32_t GetBAR(uint8_t Index, PCIDevice *Device);
uint8_t iLine(PCIDevice *Device);
uint8_t iPin(PCIDevice *Device);
#endif // !__kernel__
#ifdef __cplusplus
}

View File

@ -531,6 +531,28 @@ static_assert((int)__SYS_SIG_IGN == (int)___SYS_SIG_IGN, "SIG_IGN values do not
typedef int __SYS_clockid_t;
typedef unsigned int __SYS_socklen_t;
typedef struct FramebufferScreenInfo
{
__UINT32_TYPE__ Width;
__UINT32_TYPE__ Height;
__UINT32_TYPE__ Pitch;
__UINT32_TYPE__ Bpp;
__UINT32_TYPE__ Size;
} FramebufferScreenInfo;
/**
* @brief Get framebuffer screen info
*
* @code
* struct FramebufferScreenInfo info;
* int ioctl(fd, FBIOGET_SCREEN_INFO, &info);
* @endcode
*
* @param fd File descriptor of the framebuffer device
* @param info Pointer to the framebuffer screen info structure
*/
#define FBIOGET_SCREEN_INFO 0xf0
/**
* @brief List of syscalls
*

View File

@ -1,35 +0,0 @@
default:
$(error Do not run this Makefile directly!)
S_SOURCES = $(shell find ./ -type f -name '*.S')
C_SOURCES = $(shell find ./ -type f -name '*.c')
CXX_SOURCES = $(shell find ./ -type f -name '*.cpp')
HEADERS = $(sort $(dir $(wildcard ../../include/*)))
OBJ = $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) $(S_SOURCES:.S=.o)
STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CXX_SOURCES:.cpp=.su)
FILENAME = $(notdir $(shell pwd)).drv
build: $(FILENAME)
mv $(FILENAME) $(OUTPUT_DIR)$(FILENAME)
$(FILENAME): $(OBJ)
$(info Linking $@)
$(CC) $(DRIVER_LDFLAGS) $(OBJ) $(OUTPUT_DIR)dcrt0.o -L$(OUTPUT_DIR) -lkernel -o $@
WARNCFLAG = -Wall -Wextra
%.o: %.c $(HEADERS)
$(info Compiling $<)
$(CC) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@
%.o: %.cpp $(HEADERS)
$(info Compiling $<)
$(CXX) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@
%.o: %.S
$(info Compiling $<)
$(AS) -o $@ $<
clean:
rm -f file.map $(OBJ) $(STACK_USAGE_OBJ)

View File

@ -1,39 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __FENNIX_DRIVER_AIP_H__
#define __FENNIX_DRIVER_AIP_H__
#include <types.h>
#include <aip.h>
#include <regs.h>
extern uint8_t Device1ID[];
extern uint8_t Device2ID[];
void PS2KbdInterruptHandler(TrapFrame *);
int InitializeKeyboard();
int FinalizeKeyboard();
int DetectPS2Keyboard();
void PS2MouseInterruptHandler(TrapFrame *);
int InitializeMouse();
int FinalizeMouse();
int DetectPS2Mouse();
int DetectUART();
#endif // !__FENNIX_DRIVER_AIP_H__

View File

@ -1,235 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include "aip.h"
#include <errno.h>
#include <base.h>
bool IsKeyboard(uint8_t ID)
{
/* Common keyboard IDs */
return ID == 0xAB || ID == 0xAC || ID == 0x5D ||
ID == 0x2B || ID == 0x47 || ID == 0x60;
}
bool IsMouse(uint8_t ID)
{
/* Common mouse IDs */
return ID == 0x00 || ID == 0x03 || ID == 0x04;
}
const char *GetPS2DeviceName(uint8_t ID, uint8_t SubID)
{
switch (ID)
{
case 0x00:
return "Standard PS/2 Mouse";
case 0x03:
return "Mouse with scroll wheel";
case 0x04:
return "Mouse 5 buttons";
case 0xAB:
{
switch (SubID)
{
case 0x83: /* Normal */
case 0x41: /* Translated */
case 0xC1: /* Normal + Translated */
return "Standard PS/2 Keyboard";
case 0x84:
case 0x54:
return "IBM Thinkpad/Spacesaver Keyboard";
case 0x85:
return "NCD N-97/122-Key Host Connect(ed) Keyboard";
case 0x86:
return "122-Key Keyboard";
case 0x90:
return "Japanese \"G\" Keyboard";
case 0x91:
return "Japanese \"P\" Keyboard";
case 0x92:
return "Japanese \"A\" Keyboard";
default:
return "Unknown PS/2 Keyboard";
}
}
case 0xAC:
{
switch (SubID)
{
case 0xA1:
return "NCD Sun Keyboard";
default:
return "Unknown NCD Sun Keyboard";
}
}
case 0x5D:
case 0x2B:
return "Trust Keyboard";
case 0x47:
case 0x60:
return "NMB SGI Keyboard";
default:
return "Unknown PS/2 Device";
}
}
uint8_t Device1ID[2] = {0x00, 0x00};
uint8_t Device2ID[2] = {0x00, 0x00};
bool DualChannel = false;
int DriverEntry()
{
PS2WriteCommand(PS2_CMD_DISABLE_PORT_1);
PS2WriteCommand(PS2_CMD_DISABLE_PORT_2);
PS2ClearOutputBuffer();
PS2WriteCommand(PS2_CMD_READ_CONFIG);
PS2_CONFIGURATION cfg = {.Raw = PS2ReadData()};
DualChannel = cfg.Port2Clock;
if (DualChannel)
KernelLog("Dual channel PS/2 controller detected");
cfg.Port1Interrupt = 1;
cfg.Port2Interrupt = 1;
cfg.Port1Translation = 1;
PS2WriteCommand(PS2_CMD_WRITE_CONFIG);
PS2WriteData(cfg.Raw);
PS2WriteCommand(PS2_CMD_TEST_CONTROLLER);
uint8_t test = PS2ReadData();
if (test != PS2_TEST_PASSED)
{
KernelLog("PS/2 controller self test failed (%#x)", test);
return -EFAULT;
}
PS2WriteCommand(PS2_CMD_WRITE_CONFIG);
PS2WriteData(cfg.Raw);
// bool port2avail = false;
// if (DualChannel)
// {
// PS2WriteCommand(PS2_CMD_ENABLE_PORT_1);
// PS2WriteCommand(PS2_CMD_READ_CONFIG);
// cfg.Raw = PS2ReadData();
// port2avail = cfg.Port2Clock;
// PS2WriteCommand(PS2_CMD_DISABLE_PORT_1);
// }
PS2WriteCommand(PS2_CMD_TEST_PORT_1);
test = PS2ReadData();
if (test != 0x00)
{
KernelLog("PS/2 Port 1 self test failed (%#x)", test);
return -EFAULT;
}
if (DualChannel)
{
PS2WriteCommand(PS2_CMD_TEST_PORT_2);
test = PS2ReadData();
if (test != 0x00)
{
KernelLog("PS/2 Port 2 self test failed (%#x)", test);
return -EFAULT;
}
}
PS2WriteCommand(PS2_CMD_ENABLE_PORT_1);
if (DualChannel)
PS2WriteCommand(PS2_CMD_ENABLE_PORT_2);
int errK = InitializeKeyboard();
int errM = 0;
if (DualChannel)
errM = InitializeMouse();
/** A device may fail, but if the other one works,
* we can still use it.
*/
if (errK != 0 && errM != 0)
return -ENODEV;
return 0;
}
int DriverFinal()
{
FinalizeKeyboard();
FinalizeMouse();
PS2WriteCommand(PS2_CMD_DISABLE_PORT_1);
PS2WriteCommand(PS2_CMD_DISABLE_PORT_2);
return 0;
}
int DriverPanic()
{
PS2WriteCommand(PS2_CMD_DISABLE_PORT_1);
PS2WriteCommand(PS2_CMD_DISABLE_PORT_2);
return 0;
}
void __intStub() {}
int DriverProbe()
{
RegisterInterruptHandler(1, __intStub);
RegisterInterruptHandler(12, __intStub);
int kbd = DetectPS2Keyboard();
int mouse = DetectPS2Mouse();
int uart = DetectUART();
UnregisterAllInterruptHandlers(__intStub);
if (kbd != 0 && mouse != 0 && uart != 0)
return -ENODEV;
if (kbd == 0)
{
if (!IsKeyboard(Device1ID[0]))
{
KernelLog("PS/2 Port 1 is not a keyboard");
// return -EINVAL;
}
}
if (mouse == 0)
{
if (!IsMouse(Device2ID[0]))
{
KernelLog("PS/2 Port 2 is not a mouse");
// return -EINVAL;
}
}
KernelPrint("PS/2 Port 1: %s (0x%X 0x%X)",
GetPS2DeviceName(Device1ID[0], Device1ID[1]),
Device1ID[0], Device1ID[1]);
KernelPrint("PS/2 Port 2: %s (0x%X 0x%X)",
GetPS2DeviceName(Device2ID[0], Device2ID[1]),
Device2ID[0], Device2ID[1]);
return 0;
}
DriverInfo("aip",
"Advanced Integrated Peripheral Driver",
"EnderIce2",
0, 0, 1,
"GPLv3");

View File

@ -1,244 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include "aip.h"
#include <errno.h>
#include <base.h>
#include <fs.h>
#include <input.h>
dev_t MouseDevID = -1;
bool PacketReady = false;
bool FourPackets = false;
bool MouseButton45 = false;
uint8_t Cycle = 0;
PS2_MOUSE_PACKET Packet = {0};
InputReport mir = {0};
void PS2MouseInterruptHandler(TrapFrame *)
{
uint8_t data = PS2ReadData();
if (data == PS2_MOUSE_RESP_ACK ||
data == PS2_MOUSE_RESP_RESEND)
return;
if (!PacketReady)
{
switch (Cycle)
{
case 0:
{
if ((data & 0b00001000 /* Always 1 */) == 0)
return;
Packet.Base.Raw = data;
Cycle++;
break;
}
case 1:
{
Packet.XMovement = data;
Cycle++;
break;
}
case 2:
{
Packet.YMovement = data;
if (FourPackets)
Cycle++;
else
{
Cycle = 0;
PacketReady = true;
}
break;
}
case 3:
{
Packet.ZMovement.Raw = data;
Cycle = 0;
PacketReady = true;
break;
}
default:
break;
}
return;
}
/* https://stackoverflow.com/a/3208376/9352057 */
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte) \
((byte) & 0x80 ? '1' : '0'), \
((byte) & 0x40 ? '1' : '0'), \
((byte) & 0x20 ? '1' : '0'), \
((byte) & 0x10 ? '1' : '0'), \
((byte) & 0x08 ? '1' : '0'), \
((byte) & 0x04 ? '1' : '0'), \
((byte) & 0x02 ? '1' : '0'), \
((byte) & 0x01 ? '1' : '0')
DebugLog("PS/2 Mouse Packet: [" BYTE_TO_BINARY_PATTERN ":" BYTE_TO_BINARY_PATTERN ":" BYTE_TO_BINARY_PATTERN ":" BYTE_TO_BINARY_PATTERN "] LB:%d RB:%d MB:%d A1:%d XS:%d YS:%d XO:%d YO:%d | X:%03d Y:%03d | Z:%d B4:%d B5:%d A0:%d A0:%d",
BYTE_TO_BINARY(Packet.Base.Raw),
BYTE_TO_BINARY(Packet.XMovement),
BYTE_TO_BINARY(Packet.YMovement),
BYTE_TO_BINARY(Packet.ZMovement.Raw),
Packet.Base.LeftButton, Packet.Base.RightButton, Packet.Base.MiddleButton,
Packet.Base.Always1,
Packet.Base.XSign, Packet.Base.YSign,
Packet.Base.XOverflow, Packet.Base.YOverflow,
Packet.XMovement, Packet.YMovement,
Packet.ZMovement.Z, Packet.ZMovement.Button4, Packet.ZMovement.Button5,
Packet.ZMovement.Always0, Packet.ZMovement.Always0_2);
int X, Y;
X = Packet.XMovement - (Packet.Base.XSign ? 256 : 0);
Y = Packet.YMovement - (Packet.Base.YSign ? 256 : 0);
if (Packet.Base.XOverflow)
X = 0;
if (Packet.Base.YOverflow)
Y = 0;
mir.Type = INPUT_TYPE_MOUSE;
mir.Device = MouseDevID;
mir.Mouse.LeftButton = Packet.Base.LeftButton;
mir.Mouse.RightButton = Packet.Base.RightButton;
mir.Mouse.MiddleButton = Packet.Base.MiddleButton;
mir.Mouse.Button4 = Packet.ZMovement.Button4;
mir.Mouse.Button5 = Packet.ZMovement.Button5;
mir.Mouse.X = X;
mir.Mouse.Y = -Y;
mir.Mouse.Z = Packet.ZMovement.Z;
ReportInputEvent(&mir);
PacketReady = false;
}
void MouseSampleRate(uint8_t SampleRate)
{
PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(PS2_MOUSE_CMD_SET_SAMPLE_RATE);
PS2ReadData();
PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(SampleRate);
PS2ReadData();
}
int __fs_ms_Ioctl(struct Inode *, unsigned long, void *)
{
return 0;
}
const struct InodeOperations MouseOps = {
.Ioctl = __fs_ms_Ioctl,
};
int InitializeMouse()
{
PS2WriteData(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(PS2_MOUSE_CMD_RESET);
uint8_t test = PS2ReadData();
if (test != PS2_MOUSE_RESP_TEST_PASSED &&
test != PS2_MOUSE_RESP_ACK)
{
KernelLog("PS/2 mouse reset failed! (%#x)", test);
return -EFAULT;
}
RegisterInterruptHandler(12, PS2MouseInterruptHandler);
MouseDevID = RegisterDevice(INPUT_TYPE_MOUSE, &MouseOps);
PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(PS2_MOUSE_CMD_SET_DEFAULTS);
PS2ReadData();
PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(PS2_MOUSE_CMD_ENABLE_DATA_REPORTING);
MouseSampleRate(200);
MouseSampleRate(100);
MouseSampleRate(80);
PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(PS2_MOUSE_CMD_READ_ID);
uint8_t Device2ID = PS2ReadData();
KernelLog("PS/2 Mouse ID: %#x", Device2ID);
MouseSampleRate(200);
MouseSampleRate(200);
MouseSampleRate(80);
PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(PS2_MOUSE_CMD_READ_ID);
Device2ID = PS2ReadData();
KernelLog("PS/2 Mouse ID: %#x", Device2ID);
if (Device2ID >= 3 && Device2ID <= 4)
FourPackets = true;
if (Device2ID == 4)
MouseButton45 = true;
return 0;
}
int FinalizeMouse()
{
PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
UnregisterDevice(MouseDevID);
return 0;
}
int DetectPS2Mouse()
{
PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
if (PS2ACKTimeout() != 0)
KernelLog("PS/2 mouse failed to disable data reporting!");
PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(PS2_MOUSE_CMD_READ_ID);
if (PS2ACKTimeout() != 0)
KernelLog("PS/2 mouse failed to read ID!");
uint8_t recByte;
int timeout = 1000000;
while (timeout--)
{
recByte = PS2ReadData();
if (recByte != PS2_ACK)
break;
}
Device2ID[0] = recByte;
timeout = 1000000;
while (timeout--)
{
recByte = PS2ReadData();
if (recByte != PS2_ACK)
break;
}
Device2ID[1] = recByte;
KernelLog("PS2 Mouse Device: 0x%X 0x%X", Device2ID[0], Device2ID[1]);
return 0;
}

View File

@ -1,646 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include "aip.h"
#include <errno.h>
#include <base.h>
#include <fs.h>
#include <input.h>
#include <io.h>
#define SERIAL_ENABLE_DLAB 0x80
#define SERIAL_BUFFER_EMPTY 0x20
enum Ports
{
COM1 = 0x3F8,
COM2 = 0x2F8,
COM3 = 0x3E8,
COM4 = 0x2E8,
COM5 = 0x5F8,
COM6 = 0x4F8,
COM7 = 0x5E8,
COM8 = 0x4E8,
LPT1 = 0x378,
LPT2 = 0x278,
LPT3 = 0x3BC
};
enum SerialSpeed
{
RATE_50_HI = 0x09,
RATE_50_LO = 0x00,
RATE_300_HI = 0x01,
RATE_300_LO = 0x80,
RATE_600_HI = 0x00,
RATE_600_LO = 0xC0,
RATE_2400_HI = 0x00,
RATE_2400_LO = 0x30,
RATE_4800_HI = 0x00,
RATE_4800_LO = 0x18,
RATE_9600_HI = 0x00,
RATE_9600_LO = 0x0C,
RATE_19200_HI = 0x00,
RATE_19200_LO = 0x06,
RATE_38400_HI = 0x00,
RATE_38400_LO = 0x03,
RATE_57600_HI = 0x00,
RATE_57600_LO = 0x02,
RATE_115200_HI = 0x00,
RATE_115200_LO = 0x01
};
/*
. Table of Registers .
/---------------------------------------------------------------------\
| Base Address | DLAB | R/W | Abr | Register Name |
|---------------------------------------------------------------------|
| +0 | =0 | W | - | Transmitter Holding Buffer |
| | =0 | R | - | Receiver Buffer |
| | =1 | R/W | - | Divisor Latch Low Byte |
| +1 | =0 | R/W | IER | Interrupt Enable Register |
| | =1 | R/W | - | Divisor Latch High Byte |
| +2 | - | R | IIR | Interrupt Identification Register |
| | - | W | FCR | FIFO Control Register |
| +3 | - | R/W | LCR | Line Control Register |
| +4 | - | R/W | MCR | Modem Control Register |
| +5 | - | R | LSR | Line Status Register |
| +6 | - | R | MSR | Modem Status Register |
| +7 | - | R/W | - | Scratch Register |
\---------------------------------------------------------------------/
Source:
Interfacing the Serial / RS232 Port V5.0
Table 5 : Table of Registers
*/
/** Interrupt Enable Register */
typedef union
{
struct
{
/* Enable Received Data Available Interrupt */
uint8_t InterruptOnReceive : 1;
/* Enable Transmitter Holding Register Empty Interrupt */
uint8_t InterruptOnTransmitter : 1;
/* Enable Receiver Line Status Interrupt */
uint8_t LineStatusInterrupt : 1;
/* Enable Modem Status Interrupt */
uint8_t ModemStatusInterrupt : 1;
/* Enables Sleep Mode (16750) */
uint8_t SleepMode : 1;
/* Enables Low Power Mode (16750) */
uint8_t LowPowerMode : 1;
/* Reserved */
uint8_t __reserved : 2;
};
uint8_t raw;
} IER;
/** Interrupt Identification Register */
typedef union
{
struct
{
/* Interrupt pending */
uint8_t InterruptPending : 1;
/**
* Interrupt Status
*
* 00b = Modem Status Interrupt
* 01b = Transmitter Holding Register Empty Interrupt
* 10b = Received Data Available Interrupt
* 11b = Receiver Line Status Interrupt
*/
uint8_t InterruptStatus : 2;
/**
* 16550 Time-out Interrupt Pending
*
* @note Reserved on 8250, 16450
*/
uint8_t TimeOutIP : 1;
/** Reserved */
uint8_t __reserved : 1;
/** 64 Byte Fifo Enabled (16750 only) */
uint8_t FIFO64 : 1;
/**
* Enable FIFO
*
* 00b = No FIFO
* 01b = FIFO Enabled but Unusable
* 11b = FIFO Enabled
*/
uint8_t FIFO : 2;
};
uint8_t raw;
} IIR;
/** First In / First Out Control Register */
typedef union
{
struct
{
/** Enable FIFO's */
uint8_t FIFO : 1;
/** Clear Receive FIFO */
uint8_t ClearRX : 1;
/** Clear Transmit FIFO */
uint8_t ClearTX : 1;
/** DMA Mode Select.
*
* Change status of RXRDY & TXRDY pins from mode 1 to mode 2.
*/
uint8_t DMAMode : 1;
/** Reserved */
uint8_t __reserved : 1;
/** Enable 64 Byte FIFO (16750 only) */
uint8_t FIFO64 : 1;
/** Interrupt Trigger Level
*
* 00b = 1 Byte
* 01b = 4 Bytes
* 10b = 8 Bytes
* 11b = 14 Bytes
*/
uint8_t TriggerLevel : 2;
};
uint8_t raw;
} FCR;
/** Line Control Register */
typedef union
{
struct
{
/** Word Length
*
* 00b = 5 bits
* 01b = 6 bits
* 10b = 7 bits
* 11b = 8 bits
*/
uint8_t WordLength : 2;
/** Length of Stop Bit
*
* 0b = One Stop Bit
* 1b = 2 Stop bits for words of length 6,7 or 8 bits or 1.5 Stop Bits for Word lengths of 5 bits.
*/
uint8_t StopBit : 1;
/** Parity Select
*
* 0b = No Parity
* 001b = Odd Parity
* 011b = Even Parity
* 101b = High Parity (Sticky)
* 111b = Low Parity (Sticky)
*/
uint8_t Parity : 3;
/** Set Break Enable */
uint8_t SetBreak : 1;
/**
* Divisor Latch Access
*
* 0b = Access to Receiver buffer, Transmitter buffer & Interrupt Enable Register
* 1b = Divisor Latch Access Bit
*/
uint8_t DLAB : 1;
};
uint8_t raw;
} LCR;
/** Modem Control Register */
typedef union
{
struct
{
/** Force Data Terminal Ready */
uint8_t DataTerminalReady : 1;
/** Force Request to Send */
uint8_t RequestToSend : 1;
/** Auxiliary Output 1 */
uint8_t Out1 : 1;
/** Auxiliary Output 2 */
uint8_t Out2 : 1;
/** Loopback Mode */
uint8_t Loopback : 1;
/** Autoflow Control Enabled (16750 only) */
uint8_t Autoflow : 1;
/** Reserved */
uint8_t __reserved : 2;
};
uint8_t raw;
} MCR;
/** Line Status Register */
typedef union
{
struct
{
/** Data Ready */
uint8_t DataReady : 1;
/** Overrun Error */
uint8_t OverrunError : 1;
/** Parity Error */
uint8_t ParityError : 1;
/** Framing Error */
uint8_t FramingError : 1;
/** Break Interrupt */
uint8_t BreakInterrupt : 1;
/** Empty Transmitter Holding Register */
uint8_t EmptyTransmitterHolding : 1;
/** Empty Data Holding Registers */
uint8_t EmptyDataHolding : 1;
/** Error in Received FIFO */
uint8_t ErrorReceivedFIFO : 1;
};
uint8_t raw;
} LSR;
/** Modem Status Register */
typedef union
{
struct
{
/** Delta Clear to Send */
uint8_t DeltaClearToSend : 1;
/** Delta Data Set Ready */
uint8_t DeltaDataSetReady : 1;
/** Trailing Edge Ring Indicator */
uint8_t TrailingEdgeRingIndicator : 1;
/** Delta Data Carrier Detect */
uint8_t DeltaDataCarrierDetect : 1;
/** Clear To Send */
uint8_t ClearToSend : 1;
/** Data Set Ready */
uint8_t DataSetReady : 1;
/** Ring Indicator */
uint8_t RingIndicator : 1;
/** Carrier Detect */
uint8_t CarrierDetect : 1;
};
uint8_t raw;
} MSR;
union UARTs
{
struct
{
uint8_t com1 : 1;
uint8_t com2 : 1;
uint8_t com3 : 1;
uint8_t com4 : 1;
uint8_t com5 : 1;
uint8_t com6 : 1;
uint8_t com7 : 1;
uint8_t com8 : 1;
uint8_t lpt1 : 1;
uint8_t lpt2 : 1;
uint8_t lpt3 : 1;
uint8_t __reserved : 5;
};
uint16_t raw;
} uart;
bool IsDataReady(uint16_t Port)
{
LSR lsr;
lsr.raw = inb(Port + 5);
return lsr.DataReady;
}
bool IsTransmitEmpty(uint16_t Port)
{
LSR lsr;
lsr.raw = inb(Port + 5);
return lsr.EmptyTransmitterHolding;
}
char ReadSerial(uint16_t Port)
{
while (!IsDataReady(Port))
Yield();
return inb(Port);
}
void WriteSerial(uint16_t Port, char Character)
{
while (!IsTransmitEmpty(Port))
Yield();
outb(Port, Character);
}
void ReportSerialReceived(uint8_t Data)
{
DebugLog("%c", Data);
}
void UartCOM24(TrapFrame *)
{
LSR lsr2, lsr4;
do
{
lsr2.raw = inb(COM2 + 5);
if (lsr2.DataReady)
ReportSerialReceived(inb(COM2));
lsr4.raw = inb(COM4 + 5);
if (lsr4.DataReady)
ReportSerialReceived(inb(COM4));
} while (lsr2.DataReady || lsr4.DataReady);
}
void UartCOM13(TrapFrame *)
{
LSR lsr1, lsr3;
do
{
lsr1.raw = inb(COM1 + 5);
if (lsr1.DataReady)
ReportSerialReceived(inb(COM1));
lsr3.raw = inb(COM3 + 5);
if (lsr3.DataReady)
ReportSerialReceived(inb(COM3));
} while (lsr1.DataReady || lsr3.DataReady);
}
bool InitializePort(uint16_t Port)
{
ECS;
LCR lcr = {0};
IER ier = {0};
FCR fcr = {0};
MCR mcr = {0};
outb(Port + 3, lcr.raw);
outb(Port + 1, ier.raw);
lcr.DLAB = 1;
outb(Port + 3, lcr.raw);
outb(Port + 0, RATE_115200_LO);
outb(Port + 1, RATE_115200_HI);
lcr.DLAB = 0;
lcr.WordLength = 0b11;
outb(Port + 3, lcr.raw);
fcr.FIFO = 1;
fcr.ClearRX = 1;
fcr.ClearTX = 1;
fcr.TriggerLevel = 0b11;
outb(Port + 2, fcr.raw);
mcr.DataTerminalReady = 1;
mcr.RequestToSend = 1;
mcr.Out2 = 1;
mcr.Loopback = 1;
outb(Port + 4, mcr.raw);
/* Test the serial port */
outb(Port + 0, 0x48);
uint8_t result = inb(Port + 0);
if (result != 0x48)
{
/* FIXME: DETECT BAUD RATE
Do multiple test to check if the output is garbage.
If so, reduce the baud rate until it works. */
LCS;
KernelPrint("Port %#X test failed!", Port);
return false;
}
/* Set normal operation mode */
mcr.DataTerminalReady = 1;
mcr.RequestToSend = 1;
mcr.Out1 = 1;
mcr.Out2 = 1;
mcr.Loopback = 0;
outb(Port + 4, mcr.raw);
/* Enable interrupts on receive */
ier.InterruptOnReceive = 1;
outb(Port + 1, ier.raw);
RegisterInterruptHandler(3, UartCOM24);
RegisterInterruptHandler(4, UartCOM13);
LCS;
KernelPrint("Port %#X initialized", Port);
return true;
}
int DetectUART()
{
uart.com1 = inb(COM1) != 0xFF ? true : false;
uart.com2 = inb(COM2) != 0xFF ? true : false;
uart.com3 = inb(COM3) != 0xFF ? true : false;
uart.com4 = inb(COM4) != 0xFF ? true : false;
uart.com5 = inb(COM5) != 0xFF ? true : false;
uart.com6 = inb(COM6) != 0xFF ? true : false;
uart.com7 = inb(COM7) != 0xFF ? true : false;
uart.com8 = inb(COM8) != 0xFF ? true : false;
uart.lpt1 = inb(LPT1) != 0xFF ? true : false;
uart.lpt2 = inb(LPT2) != 0xFF ? true : false;
uart.lpt3 = inb(LPT3) != 0xFF ? true : false;
if (uart.com1 == true)
if (InitializePort(COM1) == false)
uart.com1 = false;
if (uart.com2 == true)
if (InitializePort(COM2) == false)
uart.com1 = false;
if (uart.com3 == true)
if (InitializePort(COM3) == false)
uart.com1 = false;
if (uart.com4 == true)
if (InitializePort(COM4) == false)
uart.com1 = false;
if (uart.com5 == true)
if (InitializePort(COM5) == false)
uart.com1 = false;
if (uart.com6 == true)
if (InitializePort(COM6) == false)
uart.com1 = false;
if (uart.com7 == true)
if (InitializePort(COM7) == false)
uart.com1 = false;
if (uart.com8 == true)
if (InitializePort(COM8) == false)
uart.com1 = false;
if (uart.lpt1 == true)
KernelPrint("LPT1 is present");
if (uart.lpt2 == true)
KernelPrint("LPT2 is present");
if (uart.lpt3 == true)
KernelPrint("LPT3 is present");
return 0;
}
// static int once = 0;
// static uint8_t com4 = 0xFF;
// if (!once++)
// com4 = inb(0x2E8);
// if (com4 == 0xFF)
// CPU::Halt(true);
// char UserInputBuffer[256]{'\0'};
// int BackSpaceLimit = 0;
// while (true)
// {
// while ((inb(0x2E8 + 5) & 1) == 0)
// CPU::Pause();
// char key = inb(0x2E8);
// // debug("key: %d", key);
// if (key == '\x7f') /* Backspace (DEL) */
// {
// if (BackSpaceLimit <= 0)
// continue;
// char keyBuf[5] = {'\b', '\x1b', '[', 'K', '\0'};
// ExPrint(keyBuf);
// backspace(UserInputBuffer);
// BackSpaceLimit--;
// continue;
// }
// else if (key == '\x0d') /* Enter (CR) */
// {
// UserInput(UserInputBuffer);
// BackSpaceLimit = 0;
// UserInputBuffer[0] = '\0';
// continue;
// }
// else if (key == '\x1b') /* Escape */
// {
// char tmp[16]{'\0'};
// append(tmp, key);
// while ((inb(0x2E8 + 5) & 1) == 0)
// CPU::Pause();
// char key = inb(0x2E8);
// append(tmp, key);
// if (key == '[')
// {
// // 27 91
// // < 68
// // > 67
// // down 66
// // up 65
// while ((inb(0x2E8 + 5) & 1) == 0)
// CPU::Pause();
// key = inb(0x2E8);
// append(tmp, key);
// switch (key)
// {
// case 'A':
// key = KEY_D_UP;
// break;
// case 'B':
// key = KEY_D_DOWN;
// break;
// case 'C':
// key = KEY_D_RIGHT;
// break;
// case 'D':
// key = KEY_D_LEFT;
// break;
// default:
// {
// for (size_t i = 0; i < strlen(tmp); i++)
// {
// if ((int)sizeof(UserInputBuffer) <= BackSpaceLimit)
// continue;
// append(UserInputBuffer, tmp[i]);
// BackSpaceLimit++;
// char keyBuf[2] = {(char)tmp[i], '\0'};
// ExPrint(keyBuf);
// }
// continue;
// }
// }
// ArrowInput(key);
// continue;
// }
// }
// if ((int)sizeof(UserInputBuffer) <= BackSpaceLimit)
// continue;
// append(UserInputBuffer, key);
// BackSpaceLimit++;
// char keyBuf[2] = {(char)key, '\0'};
// ExPrint(keyBuf);
// }

View File

@ -1,35 +0,0 @@
default:
$(error Do not run this Makefile directly!)
S_SOURCES = $(shell find ./ -type f -name '*.S')
C_SOURCES = $(shell find ./ -type f -name '*.c')
CXX_SOURCES = $(shell find ./ -type f -name '*.cpp')
HEADERS = $(sort $(dir $(wildcard ../../include/*)))
OBJ = $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) $(S_SOURCES:.S=.o)
STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CXX_SOURCES:.cpp=.su)
FILENAME = $(notdir $(shell pwd)).drv
build: $(FILENAME)
mv $(FILENAME) $(OUTPUT_DIR)$(FILENAME)
$(FILENAME): $(OBJ)
$(info Linking $@)
$(CC) $(DRIVER_LDFLAGS) $(OBJ) $(OUTPUT_DIR)dcrt0.o -L$(OUTPUT_DIR) -lkernel -o $@
WARNCFLAG = -Wall -Wextra
%.o: %.c $(HEADERS)
$(info Compiling $<)
$(CC) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@
%.o: %.cpp $(HEADERS)
$(info Compiling $<)
$(CXX) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@
%.o: %.S
$(info Compiling $<)
$(AS) -o $@ $<
clean:
rm -f file.map $(OBJ) $(STACK_USAGE_OBJ)

View File

@ -1,897 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.h>
#include <errno.h>
#include <fs.h>
#include <input.h>
#include <regs.h>
#include <base.h>
#include <aip.h>
#include <io.h>
enum RPCMessages
{
MSG_OPEN,
MSG_SENDSIZE,
MSG_SENDPAYLOAD,
MSG_RECVSIZE,
MSG_RECVPAYLOAD,
MSG_RECVSTATUS,
MSG_CLOSE,
};
enum RPCStatus
{
STATUS_SUCCESS = 0x1,
STATUS_DORECV = 0x2,
STATUS_CPT = 0x10,
STATUS_HB = 0x80,
};
typedef struct
{
union
{
uint32_t ax;
uint32_t magic;
};
union
{
uint32_t bx;
size_t size;
};
union
{
uint32_t cx;
uint16_t command;
};
union
{
uint32_t dx;
uint16_t port;
};
uint32_t si;
uint32_t di;
} VMwareCommand;
#define VMWARE_MAGIC 0x564D5868
#define VMWARE_PORT 0x5658
#define VMWARE_PORTHB 0x5659
#define VMWARE_HYPERVISOR_HB 0x00000000
#define VMWARE_HYPERVISOR_OUT 0x00000001
#define CMD_GETVERSION 0xA
#define CMD_MESSAGE 0x1E
#define CMD_ABSPOINTER_DATA 0x27
#define CMD_ABSPOINTER_STATUS 0x28
#define CMD_ABSPOINTER_COMMAND 0x29
#define ABSPOINTER_ENABLE 0x45414552
#define ABSPOINTER_RELATIVE 0xF5
#define ABSPOINTER_ABSOLUTE 0x53424152
#define MESSAGE_RPCI 0x49435052
#define MESSAGE_TCLO 0x4f4c4354
#define FLAG_COOKIE 0x80000000
#define ToMsg(x) ((x) << 16 | CMD_MESSAGE)
#define HighWord(x) ((x & 0xFFFF0000) >> 16)
#define MESSAGE_HB_MSG 0
#define MESSAGE_OPEN_CHANNEL ToMsg(MSG_OPEN)
#define MESSAGE_CLOSE_CHANNEL ToMsg(MSG_CLOSE)
#define MESSAGE_SEND_SIZE ToMsg(MSG_SENDSIZE)
#define MESSAGE_SEND_PAYLOAD ToMsg(MSG_SENDPAYLOAD)
#define MESSAGE_RECV_SIZE ToMsg(MSG_RECVSIZE)
#define MESSAGE_RECV_PAYLOAD ToMsg(MSG_RECVPAYLOAD)
#define MESSAGE_RECV_STATUS ToMsg(MSG_RECVSTATUS)
#if defined(__amd64__)
#define VM_PORT(cmd, in_ebx, isi, idi, \
flags, magic, \
ax, bx, cx, dx, si, di) \
__asm__ __volatile__("movw $0x5658, %%dx\n" \
"inl %%dx, %%eax\n" \
: "=a"(ax), \
"=b"(bx), \
"=c"(cx), \
"=d"(dx), \
"=S"(si), \
"=D"(di) \
: "a"(magic), \
"b"(in_ebx), \
"c"(cmd), \
"d"(flags), \
"S"(isi), \
"D"(idi) : "memory")
#define VM_PORT_HB_OUT(cmd, in_ecx, isi, idi, \
flags, magic, bp, \
ax, bx, cx, dx, si, di) \
__asm__ __volatile__("push %%rbp\n" \
"mov %12, %%rbp\n" \
"movw $0x5659, %%dx\n" \
"rep outsb\n" \
"pop %%rbp\n" \
: "=a"(ax), \
"=b"(bx), \
"=c"(cx), \
"=d"(dx), \
"=S"(si), \
"=D"(di) \
: "a"(magic), \
"b"(cmd), \
"c"(in_ecx), \
"d"(flags), \
"S"(isi), \
"D"(idi), \
"r"(bp) : "memory", "cc")
#define VM_PORT_HB_IN(cmd, in_ecx, isi, idi, \
flags, magic, bp, \
ax, bx, cx, dx, si, di) \
__asm__ __volatile__("push %%rbp\n" \
"mov %12, %%rbp\n" \
"movw $0x5659, %%dx\n" \
"rep insb\n" \
"pop %%rbp\n" \
: "=a"(ax), \
"=b"(bx), \
"=c"(cx), \
"=d"(dx), \
"=S"(si), \
"=D"(di) \
: "a"(magic), \
"b"(cmd), \
"c"(in_ecx), \
"d"(flags), \
"S"(isi), \
"D"(idi), \
"r"(bp) : "memory", "cc")
#elif defined(__i386__)
#define VM_PORT(cmd, in_ebx, isi, idi, \
flags, magic, \
ax, bx, cx, dx, si, di)
#define VM_PORT_HB_OUT(cmd, in_ecx, isi, idi, \
flags, \
magic, bp, ax, \
bx, cx, dx, si, di)
#define VM_PORT_HB_IN(cmd, in_ecx, isi, idi, \
flags, magic, bp, \
ax, bx, cx, dx, si, di)
#endif
/* TODO:
- use vmcall or vmmcall instead of "out" and "in" if available
*/
typedef struct
{
int TCLOChannel;
uint16_t ChannelID;
uint32_t CookieHigh;
uint32_t CookieLow;
} ToolboxContext;
dev_t MouseDevID = -1;
int __strcmp(const char *l, const char *r)
{
for (; *l == *r && *l; l++, r++)
;
return *(unsigned char *)l - *(unsigned char *)r;
}
void __cpuid(uint32_t Function,
uint32_t *eax, uint32_t *ebx,
uint32_t *ecx, uint32_t *edx)
{
asmv("cpuid"
: "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
: "a"(Function));
}
bool __CheckHypervisorBit()
{
uint32_t eax, ebx, ecx, edx;
__cpuid(0x1, &eax, &ebx, &ecx, &edx);
if (!(ecx & (1 << 31)))
return false; /* Hypervisor not detected */
return true;
}
bool __VMwareBackdoorHypervisors()
{
const char hv[13] = {0};
uint32_t eax, ebx, ecx, edx;
__cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
*(uint32_t *)hv = ebx;
*(uint32_t *)(hv + 4) = ecx;
*(uint32_t *)(hv + 8) = edx;
if (__strcmp(hv, "VMwareVMware") != 0 &&
__strcmp(hv, "KVMKVMKVM") != 0 &&
__strcmp(hv, "TCGTCGTCGTCG") != 0)
{
return false;
}
return true;
}
bool IsVMwareBackdoorAvailable()
{
if (!__CheckHypervisorBit())
return false;
if (!__VMwareBackdoorHypervisors())
return false;
struct
{
union
{
uint32_t ax;
uint32_t magic;
};
union
{
uint32_t bx;
size_t size;
};
union
{
uint32_t cx;
uint16_t command;
};
union
{
uint32_t dx;
uint16_t port;
};
uint32_t si;
uint32_t di;
} cmd;
cmd.si = cmd.di = 0;
cmd.bx = ~0x564D5868;
cmd.command = 0xA;
cmd.magic = 0x564D5868;
cmd.port = 0x5658;
asmv("in %%dx, %0"
: "+a"(cmd.ax), "+b"(cmd.bx),
"+c"(cmd.cx), "+d"(cmd.dx),
"+S"(cmd.si), "+D"(cmd.di));
if (cmd.bx != 0x564D5868 ||
cmd.ax == 0xFFFFFFFF)
return false;
return true;
}
static int OpenMessageChannel(ToolboxContext *ctx, uint32_t Protocol)
{
uintptr_t ax, bx, cx, dx, si = 0, di = 0;
VM_PORT(MESSAGE_OPEN_CHANNEL,
(Protocol | FLAG_COOKIE), si, di,
0, VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
{
KernelLog("Failed to open message channel %#lx", Protocol);
return -EINVAL;
}
DebugLog("Opened message channel %d (Protocol: %#lx)",
HighWord(dx), Protocol);
ctx->ChannelID = (uint16_t)HighWord(dx);
ctx->CookieHigh = si;
ctx->CookieLow = di;
return 0;
}
static void MessageClose(ToolboxContext *ctx)
{
uintptr_t ax, bx, cx, dx,
si = ctx->CookieHigh,
di = ctx->CookieLow;
VM_PORT(MESSAGE_CLOSE_CHANNEL,
0, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
DebugLog("Closed message channel %d", ctx->ChannelID);
}
static uintptr_t MessageSendHB(ToolboxContext *ctx,
const char *Message)
{
uintptr_t ax, bx, cx, dx,
si = (uintptr_t)Message,
di = ctx->CookieLow,
bp = ctx->CookieHigh;
uint32_t ChannelID = ctx->ChannelID << 16;
size_t Size = StringLength(Message);
VM_PORT_HB_OUT((STATUS_SUCCESS << 16) | MESSAGE_HB_MSG,
Size, si, di,
VMWARE_HYPERVISOR_HB | ChannelID | VMWARE_HYPERVISOR_OUT,
VMWARE_MAGIC, bp,
ax, bx, cx, dx, si, di);
return bx;
}
static uintptr_t MessageSendLB(ToolboxContext *ctx,
const char *Message)
{
uintptr_t ax, bx,
cx = STATUS_SUCCESS << 16,
dx, si, di;
size_t Size = StringLength(Message);
while (Size &&
(HighWord(cx) & STATUS_SUCCESS))
{
uint32_t TotalBytes = MIN((uint32_t)Size, (uint32_t)4);
uint32_t Word = 0;
MemoryCopy(&Word, Message, TotalBytes);
Message += TotalBytes;
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_SEND_PAYLOAD,
Word, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
}
return cx;
}
static uintptr_t MessageReceiveHB(ToolboxContext *ctx,
char *Buffer,
size_t BufferSize)
{
uintptr_t ax, bx, cx, dx,
si = ctx->CookieHigh,
di = (uintptr_t)Buffer,
bp = ctx->CookieLow;
uint32_t ChannelID = ctx->ChannelID << 16;
VM_PORT_HB_IN((STATUS_SUCCESS << 16) | MESSAGE_HB_MSG,
BufferSize, si, di,
VMWARE_HYPERVISOR_HB | ChannelID | VMWARE_HYPERVISOR_OUT,
VMWARE_MAGIC, bp,
ax, bx, cx, dx, si, di);
return bx;
}
static uintptr_t MessageReceiveLB(ToolboxContext *ctx,
char *Buffer,
size_t BufferSize)
{
uintptr_t ax, bx,
cx = STATUS_SUCCESS << 16,
dx, si, di;
while (BufferSize)
{
uint32_t TotalBytes = MIN((uint32_t)BufferSize, (uint32_t)4);
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_RECV_PAYLOAD,
STATUS_SUCCESS, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
break;
MemoryCopy(Buffer, &bx, TotalBytes);
Buffer += TotalBytes;
BufferSize -= TotalBytes;
}
return cx;
}
static int MessageSend(ToolboxContext *ctx,
const char *Message)
{
uintptr_t ax, bx, cx, dx, si, di;
size_t Size = StringLength(Message);
int Retries = 0;
while (Retries < 2)
{
Retries++;
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_SEND_SIZE,
Size, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
{
KernelLog("Failed to send message size for \"%s\": %d",
Message, cx);
return -EINVAL;
}
bool HighBand = (HighWord(cx) & STATUS_HB) != 0;
if (HighBand)
bx = MessageSendHB(ctx, Message);
else
bx = MessageSendLB(ctx, Message);
int status = HighWord(bx);
if ((status & STATUS_SUCCESS) != 0)
{
DebugLog("Message \"%s\" sent", Message);
return 0;
}
else if ((status & STATUS_CPT) == 0)
{
KernelLog("Checkpoint occurred for message \"%s\"", Message);
continue;
}
else
break;
}
KernelLog("Failed to send message \"%s\": %#lx", Message, bx);
return -EINVAL;
}
static int MessageReceive(ToolboxContext *ctx,
char **Buffer,
size_t *BufferSize)
{
uintptr_t ax, bx, cx, dx, si, di;
int Retries = 0;
*Buffer = NULL;
*BufferSize = 0;
char *ReplyBuf = NULL;
size_t ReplyBufPages = 0;
size_t ReplySize = 0;
while (Retries < 2)
{
Retries++;
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_RECV_SIZE,
0, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
{
KernelLog("Failed to receive message size: %d", cx);
return -EINVAL;
}
else if ((HighWord(cx) & STATUS_DORECV) == 0)
{
DebugLog("No message to receive");
return -EAGAIN;
}
ReplySize = bx;
if (ReplyBuf != NULL)
FreeMemory(ReplyBuf, ReplyBufPages);
ReplyBufPages = ReplySize / 0x1000 + 1;
ReplyBuf = AllocateMemory(ReplyBufPages);
bool HighBand = (HighWord(cx) & STATUS_HB) != 0;
if (HighBand)
bx = MessageReceiveHB(ctx, ReplyBuf, ReplySize);
else
bx = MessageReceiveLB(ctx, ReplyBuf, ReplySize);
if ((HighWord(bx) & STATUS_SUCCESS) == 0)
{
if ((HighWord(bx) & STATUS_CPT) == 0)
{
KernelLog("Checkpoint occurred for message payload");
continue;
}
KernelLog("Failed to receive message payload: %d", HighWord(bx));
FreeMemory(ReplyBuf, ReplyBufPages);
return -EINVAL;
}
ReplyBuf[ReplySize] = '\0';
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_RECV_STATUS,
STATUS_SUCCESS, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
{
if ((HighWord(cx) & STATUS_CPT) == 0)
{
KernelLog("Retrying message receive");
continue;
}
KernelLog("Failed to receive message status: %d", HighWord(cx));
FreeMemory(ReplyBuf, ReplyBufPages);
return -EINVAL;
}
break;
}
if (ReplyBuf == NULL)
{
KernelLog("Failed to receive message");
return -EINVAL;
}
*Buffer = ReplyBuf;
*BufferSize = ReplySize;
DebugLog("Received message \"%s\"", ReplyBuf);
return 0;
}
static int SendRPCI(ToolboxContext *, const char *Request)
{
ToolboxContext rpci_ctx = {0};
int status = OpenMessageChannel(&rpci_ctx, MESSAGE_RPCI);
if (status < 0)
{
KernelLog("Failed to open RPCI channel: %d", status);
return status;
}
status = MessageSend(&rpci_ctx, Request);
if (status < 0)
{
KernelLog("Failed to send RPCI request: %d", status);
return status;
}
MessageClose(&rpci_ctx);
return 0;
}
int MsgEqual(const char *haystack, const char *needle)
{
return strstr(haystack, needle) == haystack;
}
static int DisplayGetSize(ToolboxContext *ctx)
{
if (ctx->TCLOChannel != -1)
MessageClose(ctx);
OpenMessageChannel(ctx, MESSAGE_TCLO);
char EmptyBuffer[256] = {'\0'};
MessageSend(ctx, EmptyBuffer);
while (true)
{
/* FIXME: buf memory leak */
char *buf;
size_t len;
int status = MessageReceive(ctx, &buf, &len);
if (status == -EAGAIN)
{
Sleep(1000);
continue;
}
else if (status < 0)
{
KernelLog("Failed to receive message");
return 1;
}
buf[StringLength(buf)] = '\0';
if (MsgEqual(buf, "reset"))
{
if (MessageSend(ctx, "OK ATR toolbox") < 0)
return 1;
}
else if (MsgEqual(buf, "ping"))
{
if (MessageSend(ctx, "OK ") < 0)
return 1;
}
else if (MsgEqual(buf, "Capabilities_Register"))
{
SendRPCI(ctx, "tools.capability.resolution_set 1");
SendRPCI(ctx, "tools.capability.resolution_server toolbox 1");
SendRPCI(ctx, "tools.capability.display_topology_set 1");
SendRPCI(ctx, "tools.capability.color_depth_set 1");
SendRPCI(ctx, "tools.capability.resolution_min 0 0");
SendRPCI(ctx, "tools.capability.unity 1");
if (MessageSend(ctx, "OK ") < 0)
return 1;
}
else if (MsgEqual(buf, "Resolution_Set"))
{
DebugLog("%s", buf);
if (MessageSend(ctx, "OK ") < 0)
return 1;
MessageClose(ctx);
return 0;
}
else
{
if (MessageSend(ctx, "ERROR Unknown command") < 0)
return 1;
}
}
}
pid_t dst_id = -1;
pid_t dst_pid = -1;
ToolboxContext *tb_ctx = NULL;
void DisplayScaleThread()
{
/* sizeof ToolboxContext */
tb_ctx = AllocateMemory(1);
Sleep(2000);
while (true)
{
if (DisplayGetSize(tb_ctx) != 0)
KernelLog("Failed to scale display");
Sleep(1000);
}
}
void CommandSend(VMwareCommand *cmd)
{
cmd->magic = VMWARE_MAGIC;
cmd->port = VMWARE_PORT;
asm volatile("in %%dx, %0"
: "+a"(cmd->ax), "+b"(cmd->bx),
"+c"(cmd->cx), "+d"(cmd->dx),
"+S"(cmd->si), "+D"(cmd->di));
}
void Absolute()
{
VMwareCommand cmd = {0};
/* Enable */
cmd.bx = ABSPOINTER_ENABLE;
cmd.command = CMD_ABSPOINTER_COMMAND;
CommandSend(&cmd);
/* Status */
cmd.bx = 0;
cmd.command = CMD_ABSPOINTER_STATUS;
CommandSend(&cmd);
/* Read data (1) */
cmd.bx = 1;
cmd.command = CMD_ABSPOINTER_DATA;
CommandSend(&cmd);
/* Enable absolute */
cmd.bx = ABSPOINTER_ABSOLUTE;
cmd.command = CMD_ABSPOINTER_COMMAND;
CommandSend(&cmd);
}
void Relative()
{
VMwareCommand cmd = {0};
cmd.bx = ABSPOINTER_RELATIVE;
cmd.command = CMD_ABSPOINTER_COMMAND;
CommandSend(&cmd);
}
InputReport ir = {0};
void InterruptHandler(TrapFrame *)
{
uint8_t Data = inb(0x60);
(void)Data;
VMwareCommand cmd = {0};
cmd.bx = 0;
cmd.command = CMD_ABSPOINTER_STATUS;
CommandSend(&cmd);
if (cmd.ax == 0xFFFF0000)
{
KernelLog("VMware mouse is not connected?");
Relative();
Absolute();
return;
}
if ((cmd.ax & 0xFFFF) < 4)
return;
cmd.bx = 4;
cmd.command = CMD_ABSPOINTER_DATA;
CommandSend(&cmd);
int Buttons = (cmd.ax & 0xFFFF);
/**
* How should I handle this?
* (cmd.[bx,cx] * Width) / 0xFFFF
* Maybe TODO: Width and Height API?
*/
uintptr_t AbsoluteX = cmd.bx;
uintptr_t AbsoluteY = cmd.cx;
ir.Type = INPUT_TYPE_MOUSE;
ir.Device = MouseDevID;
ir.Mouse.X = AbsoluteX;
ir.Mouse.Y = AbsoluteY;
ir.Mouse.Z = (int8_t)cmd.dx;
ir.Mouse.Absolute = 1;
ir.Mouse.LeftButton = Buttons & 0x20;
ir.Mouse.RightButton = Buttons & 0x10;
ir.Mouse.MiddleButton = Buttons & 0x08;
// ir.Mouse.Button4 = 0x0;
// ir.Mouse.Button5 = 0x0;
// ir.Mouse.Button6 = 0x0;
// ir.Mouse.Button7 = 0x0;
// ir.Mouse.Button8 = 0x0;
ReportInputEvent(&ir);
}
int __fs_Ioctl(struct Inode *, unsigned long Request, void *)
{
switch (Request)
{
case 0x1:
Relative();
break;
case 0x2:
Absolute();
break;
default:
return -EINVAL;
}
return 0;
}
const struct InodeOperations MouseOps = {
.Ioctl = __fs_Ioctl,
};
bool ToolboxSupported = false;
int DriverEntry()
{
ToolboxContext tb_ctx = {0};
/* Test if it's supported */
int status = OpenMessageChannel(&tb_ctx, MESSAGE_TCLO);
if (status == 0)
{
ToolboxSupported = true;
MessageClose(&tb_ctx);
dst_id = CreateKernelThread(0, "VMware Display Scale",
(void *)DisplayScaleThread, NULL);
dst_pid = GetCurrentProcess();
}
PS2WriteCommand(PS2_CMD_ENABLE_PORT_2);
PS2WriteCommand(PS2_CMD_READ_CONFIG);
PS2_CONFIGURATION config = {.Raw = PS2ReadData()};
config.Port2Interrupt = 1;
PS2WriteCommand(PS2_CMD_WRITE_CONFIG);
PS2WriteData(config.Raw);
PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(PS2_MOUSE_CMD_SET_DEFAULTS);
PS2ReadData();
PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(PS2_MOUSE_CMD_ENABLE_DATA_REPORTING);
PS2ReadData();
Absolute();
/**
* If we have another driver using the PS/2 mouse, we need to
* override its interrupt handler.
*/
OverrideInterruptHandler(12, InterruptHandler);
MouseDevID = RegisterDevice(INPUT_TYPE_MOUSE, &MouseOps);
return 0;
}
int DriverFinal()
{
PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
Relative();
UnregisterDevice(MouseDevID);
if (ToolboxSupported)
{
KillThread(dst_id, dst_pid, 0);
if (tb_ctx->TCLOChannel != -1)
MessageClose(tb_ctx);
FreeMemory(tb_ctx, 1);
}
return 0;
}
int DriverPanic()
{
Relative();
PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
PS2WriteData(PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
return 0;
}
int DriverProbe()
{
if (!IsVMwareBackdoorAvailable())
return -ENODEV;
return 0;
}
DriverInfo("vmware",
"VMware Tools Driver",
"EnderIce2",
0, 0, 1,
"GPLv3");

View File

@ -1,35 +0,0 @@
default:
$(error Do not run this Makefile directly!)
S_SOURCES = $(shell find ./ -type f -name '*.S')
C_SOURCES = $(shell find ./ -type f -name '*.c')
CXX_SOURCES = $(shell find ./ -type f -name '*.cpp')
HEADERS = $(sort $(dir $(wildcard ../../include/*)))
OBJ = $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) $(S_SOURCES:.S=.o)
STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CXX_SOURCES:.cpp=.su)
FILENAME = $(notdir $(shell pwd)).drv
build: $(FILENAME)
mv $(FILENAME) $(OUTPUT_DIR)$(FILENAME)
$(FILENAME): $(OBJ)
$(info Linking $@)
$(CC) $(DRIVER_LDFLAGS) $(OBJ) $(OUTPUT_DIR)dcrt0.o -L$(OUTPUT_DIR) -lkernel -o $@
WARNCFLAG = -Wall -Wextra
%.o: %.c $(HEADERS)
$(info Compiling $<)
$(CC) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@
%.o: %.cpp $(HEADERS)
$(info Compiling $<)
$(CXX) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@
%.o: %.S
$(info Compiling $<)
$(AS) -o $@ $<
clean:
rm -f file.map $(OBJ) $(STACK_USAGE_OBJ)

View File

@ -1,512 +0,0 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <netools.h>
#include <errno.h>
#include <regs.h>
#include <base.h>
#include <pci.h>
#include <network.h>
#include <io.h>
#include "e1000.hpp"
class E1000Device
{
private:
PCIHeader0 *Header;
uint16_t DeviceID;
bool Initialized = false;
bool EEPROMAvailable;
struct BARData
{
uint8_t Type;
uint16_t IOBase;
uint64_t MemoryBase;
} BAR;
#define E1000_NUM_RX_DESC 32
#define E1000_NUM_TX_DESC 8
RXDescriptor *RX[E1000_NUM_RX_DESC];
TXDescriptor *TX[E1000_NUM_TX_DESC];
uint16_t RXCurrent;
uint16_t TXCurrent;
const int BaseBufferSize = 8192;
const int AdditionalBytes = 16;
uint32_t CurrentPacket;
void WriteCMD(uint16_t Address, uint32_t Value)
{
if (BAR.Type == 0)
mmoutl((void *)(BAR.MemoryBase + Address), Value);
else
{
outl(BAR.IOBase, Address);
outl(BAR.IOBase + 4, Value);
}
}
uint32_t ReadCMD(uint16_t Address)
{
if (BAR.Type == 0)
return mminl((void *)(BAR.MemoryBase + Address));
else
{
outl(BAR.IOBase, Address);
return inl(BAR.IOBase + 0x4);
}
}
uint32_t ReadEEPROM(uint8_t Address)
{
uint16_t Data = 0;
uint32_t temp = 0;
if (EEPROMAvailable)
{
WriteCMD(REG::EEPROM, (1) | ((uint32_t)(Address) << 8));
while (!((temp = ReadCMD(REG::EEPROM)) & (1 << 4)))
;
}
else
{
WriteCMD(REG::EEPROM, (1) | ((uint32_t)(Address) << 2));
while (!((temp = ReadCMD(REG::EEPROM)) & (1 << 1)))
;
}
Data = (uint16_t)((temp >> 16) & 0xFFFF);
return Data;
}
void InitializeRX()
{
DebugLog("Initializing RX...");
uintptr_t Ptr = (uintptr_t)AllocateMemory(TO_PAGES(sizeof(RXDescriptor) *
E1000_NUM_RX_DESC +
AdditionalBytes));
for (int i = 0; i < E1000_NUM_RX_DESC; i++)
{
RX[i] = (RXDescriptor *)(Ptr + i * 16);
RX[i]->Address = (uint64_t)AllocateMemory(TO_PAGES(BaseBufferSize + AdditionalBytes));
RX[i]->Status = 0;
}
#pragma GCC diagnostic ignored "-Wshift-count-overflow"
WriteCMD(REG::TXDESCLO, (uint32_t)(Ptr >> 32));
WriteCMD(REG::TXDESCHI, (uint32_t)(Ptr & 0xFFFFFFFF));
WriteCMD(REG::RXDESCLO, (uint32_t)Ptr);
WriteCMD(REG::RXDESCHI, 0);
WriteCMD(REG::RXDESCLEN, E1000_NUM_RX_DESC * 16);
WriteCMD(REG::RXDESCHEAD, 0);
WriteCMD(REG::RXDESCTAIL, E1000_NUM_RX_DESC - 1);
RXCurrent = 0;
WriteCMD(REG::RCTRL, RCTL::EN | RCTL::SBP | RCTL::UPE |
RCTL::MPE | RCTL::LBM_NONE |
RTCL::RDMTS_HALF | RCTL::BAM |
RCTL::SECRC | RCTL::BSIZE_8192);
}
void InitializeTX()
{
DebugLog("Initializing TX...");
uintptr_t Ptr = (uintptr_t)AllocateMemory(TO_PAGES(sizeof(TXDescriptor) *
E1000_NUM_RX_DESC +
AdditionalBytes));
for (short i = 0; i < E1000_NUM_TX_DESC; i++)
{
TX[i] = (TXDescriptor *)((uintptr_t)Ptr + i * 16);
TX[i]->Address = 0;
TX[i]->Command = 0;
TX[i]->Status = TSTA::DD;
}
WriteCMD(REG::TXDESCHI, (uint32_t)((uint64_t)Ptr >> 32));
WriteCMD(REG::TXDESCLO, (uint32_t)((uint64_t)Ptr & 0xFFFFFFFF));
WriteCMD(REG::TXDESCLEN, E1000_NUM_TX_DESC * 16);
WriteCMD(REG::TXDESCHEAD, 0);
WriteCMD(REG::TXDESCTAIL, 0);
TXCurrent = 0;
WriteCMD(REG::TCTRL, TCTL::EN_ | TCTL::PSP |
(15 << TCTL::CT_SHIFT) |
(64 << TCTL::COLD_SHIFT) |
TCTL::RTLC);
WriteCMD(REG::TCTRL, 0b0110000000000111111000011111010);
WriteCMD(REG::TIPG, 0x0060200A);
}
public:
dev_t ID;
bool IsInitialized() { return Initialized; }
size_t write(uint8_t *Buffer, size_t Size)
{
TX[TXCurrent]->Address = (uint64_t)Buffer;
TX[TXCurrent]->Length = (uint16_t)Size;
TX[TXCurrent]->Command = CMD::EOP | CMD::IFCS | CMD::RS;
TX[TXCurrent]->Status = 0;
uint16_t OldTXCurrent = TXCurrent;
TXCurrent = (uint16_t)((TXCurrent + 1) % E1000_NUM_TX_DESC);
WriteCMD(REG::TXDESCTAIL, TXCurrent);
while (!(TX[OldTXCurrent]->Status & 0xFF))
Yield();
return Size;
}
MediaAccessControl GetMAC()
{
MediaAccessControl mac;
if (EEPROMAvailable)
{
uint32_t temp;
temp = ReadEEPROM(0);
mac.Address[0] = temp & 0xff;
mac.Address[1] = (uint8_t)(temp >> 8);
temp = ReadEEPROM(1);
mac.Address[2] = temp & 0xff;
mac.Address[3] = (uint8_t)(temp >> 8);
temp = ReadEEPROM(2);
mac.Address[4] = temp & 0xff;
mac.Address[5] = (uint8_t)(temp >> 8);
}
else
{
uint8_t *BaseMac8 = (uint8_t *)(BAR.MemoryBase + 0x5400);
uint32_t *BaseMac32 = (uint32_t *)(BAR.MemoryBase + 0x5400);
if (BaseMac32[0] != 0)
for (int i = 0; i < 6; i++)
mac.Address[i] = BaseMac8[i];
else
{
KernelLog("No MAC address found.");
return MediaAccessControl();
}
}
return mac;
}
int ioctl(NetIoctl req, void *arg)
{
switch (req)
{
case IOCTL_NET_GET_MAC:
{
MediaAccessControl mac = GetMAC();
*((uint48_t *)arg) = mac.ToHex(); /* UNTESTED */
return 0;
}
default:
return -EINVAL;
}
return 0;
}
void OnInterruptReceived(TrapFrame *)
{
WriteCMD(REG::IMASK, 0x1);
uint32_t status = ReadCMD(0xC0);
UNUSED(status);
while ((RX[RXCurrent]->Status & 0x1))
{
uint8_t *data = (uint8_t *)RX[RXCurrent]->Address;
uint16_t dataSz = RX[RXCurrent]->Length;
// ReportNetworkPacket(ID, data, dataSz);
/* FIXME: Implement */
KernelLog("FIXME: Received packet");
(void)data;
(void)dataSz;
RX[RXCurrent]->Status = 0;
uint16_t OldRXCurrent = RXCurrent;
RXCurrent = (uint16_t)((RXCurrent + 1) % E1000_NUM_RX_DESC);
WriteCMD(REG::RXDESCTAIL, OldRXCurrent);
}
}
void Panic()
{
WriteCMD(REG::IMASK, 0x00000000);
WriteCMD(REG::ITR, 0x00000000);
WriteCMD(REG::IAM, 0x00000000);
}
E1000Device(PCIHeader0 *_Header, uint16_t _DeviceID)
: Header(_Header),
DeviceID(_DeviceID)
{
uint32_t PCIBAR0 = Header->BAR0;
uint32_t PCIBAR1 = Header->BAR1;
BAR.Type = PCIBAR0 & 1;
BAR.IOBase = (uint16_t)(PCIBAR0 & (~3));
BAR.MemoryBase = PCIBAR1 & (~15);
switch (DeviceID)
{
case 0x100E:
{
KernelLog("Found Intel 82540EM Gigabit Ethernet Controller.");
/* Detect EEPROM */
WriteCMD(REG::EEPROM, 0x1);
for (int i = 0; i < 1000 && !EEPROMAvailable; i++)
if (ReadCMD(REG::EEPROM) & 0x10)
EEPROMAvailable = true;
else
EEPROMAvailable = false;
if (!GetMAC().Valid())
{
KernelLog("Failed to get MAC");
return;
}
/* Start link */
uint32_t cmdret = ReadCMD(REG::CTRL);
WriteCMD(REG::CTRL, cmdret | ECTRL::SLU);
for (int i = 0; i < 0x80; i++)
WriteCMD((uint16_t)(0x5200 + i * 4), 0);
WriteCMD(REG::IMASK, 0x1F6DC);
WriteCMD(REG::IMASK, 0xFF & ~4);
ReadCMD(0xC0);
InitializeRX();
InitializeTX();
break;
}
default:
{
KernelLog("Unimplemented E1000 device.");
return;
}
}
Initialized = true;
}
~E1000Device()
{
if (!Initialized)
return;
switch (DeviceID)
{
case 0x100E:
{
// Clearing Enable bit in Receive Control Register
uint32_t cmdret = ReadCMD(REG::RCTRL);
WriteCMD(REG::RCTRL, cmdret & ~RCTL::EN);
// Masking Interrupt Mask, Interrupt Throttling Rate & Interrupt Auto-Mask
WriteCMD(REG::IMASK, 0x00000000);
WriteCMD(REG::ITR, 0x00000000);
WriteCMD(REG::IAM, 0x00000000);
// Clearing SLU bit in Device Control Register
cmdret = ReadCMD(REG::CTRL);
WriteCMD(REG::CTRL, cmdret & ~ECTRL::SLU);
// Clear the Interrupt Cause Read register by reading it
ReadCMD(REG::ICR);
// Powering down the device (?)
WriteCMD(REG::CTRL, PCTRL::POWER_DOWN);
/* TODO: Stop link; further testing required */
break;
}
default:
{
KernelLog("Unimplemented E1000 device.");
return;
}
}
}
};
E1000Device *Drivers[4] = {nullptr};
dev_t NetID[4] = {(dev_t)-1};
#define OIR(x) OIR_##x
#define CREATE_OIR(x) \
void OIR_##x(TrapFrame *f) { Drivers[x]->OnInterruptReceived(f); }
CREATE_OIR(0);
CREATE_OIR(1);
CREATE_OIR(2);
CREATE_OIR(3);
int __fs_Open(struct Inode *, int, mode_t) { return 0; }
int __fs_Close(struct Inode *) { return 0; }
ssize_t __fs_Read(struct Inode *, void *, size_t, off_t) { return 0; }
ssize_t __fs_Write(struct Inode *Node, const void *Buffer, size_t Size, off_t)
{
return Drivers[NetID[Node->GetMinor()]]->write((uint8_t *)Buffer, Size);
}
int __fs_Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
{
return Drivers[NetID[Node->GetMinor()]]->ioctl((NetIoctl)Request, Argp);
}
const struct InodeOperations NetOps = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = __fs_Read,
.Write = __fs_Write,
.Truncate = nullptr,
.Open = __fs_Open,
.Close = __fs_Close,
.Ioctl = __fs_Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
PCIArray *Devices;
EXTERNC int cxx_Panic()
{
PCIArray *ctx = Devices;
short Count = 0;
while (ctx != nullptr)
{
if (Drivers[Count] != nullptr)
Drivers[Count]->Panic();
Count++;
ctx = (PCIArray *)ctx->Next;
}
return 0;
}
EXTERNC int cxx_Probe()
{
uint16_t VendorIDs[] = {0x8086, /* Intel */
PCI_END};
uint16_t DeviceIDs[] = {0x100E, /* 82540EM */
0x100F, /* 82545EM */
0x10D3, /* 82574L */
0x10EA, /* I217-LM */
0x153A, /* 82577LM */
PCI_END};
Devices = GetPCIDevices(VendorIDs, DeviceIDs);
if (Devices == nullptr)
{
KernelLog("No E1000 device found.");
return -ENODEV;
}
return 0;
}
EXTERNC int cxx_Initialize()
{
PCIArray *ctx = Devices;
size_t Count = 0;
while (ctx != nullptr)
{
if (Count > sizeof(Drivers) / sizeof(E1000Device *))
break;
InitializePCI(ctx->Device);
Drivers[Count] = new E1000Device((PCIHeader0 *)ctx->Device->Header,
ctx->Device->Header->DeviceID);
if (Drivers[Count]->IsInitialized())
{
dev_t ret = RegisterDevice(NETWORK_TYPE_ETHERNET, &NetOps);
NetID[Count] = ret;
Drivers[Count]->ID = ret;
/* FIXME: bad code */
switch (Count)
{
case 0:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(0));
break;
case 1:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(1));
break;
case 2:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(2));
break;
case 3:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(3));
break;
default:
break;
}
Count++;
}
ctx = (PCIArray *)ctx->Next;
}
if (Count == 0)
{
KernelLog("No valid E1000 device found.");
return -EINVAL;
}
return 0;
}
EXTERNC int cxx_Finalize()
{
PCIArray *ctx = Devices;
size_t Count = 0;
while (ctx != nullptr)
{
if (Count++ > sizeof(Drivers) / sizeof(E1000Device *))
break;
delete Drivers[Count++];
ctx->Device->Header->Command |= PCI_COMMAND_INTX_DISABLE;
ctx = (PCIArray *)ctx->Next;
}
for (size_t i = 0; i < sizeof(NetID) / sizeof(dev_t); i++)
{
if (NetID[i] != (dev_t)-1)
UnregisterDevice(NetID[i]);
}
return 0;
}

View File

@ -1,31 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include <base.h>
#include "e1000.hpp"
int DriverEntry() { return cxx_Initialize(); }
int DriverFinal() { return cxx_Finalize(); }
int DriverPanic() { return cxx_Panic(); }
int DriverProbe() { return cxx_Probe(); }
DriverInfo("e1000",
"Intel(R) PRO/1000 Network Driver",
"EnderIce2",
0, 0, 1,
"GPLv3");

View File

@ -1,35 +0,0 @@
default:
$(error Do not run this Makefile directly!)
S_SOURCES = $(shell find ./ -type f -name '*.S')
C_SOURCES = $(shell find ./ -type f -name '*.c')
CXX_SOURCES = $(shell find ./ -type f -name '*.cpp')
HEADERS = $(sort $(dir $(wildcard ../../include/*)))
OBJ = $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) $(S_SOURCES:.S=.o)
STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CXX_SOURCES:.cpp=.su)
FILENAME = $(notdir $(shell pwd)).drv
build: $(FILENAME)
mv $(FILENAME) $(OUTPUT_DIR)$(FILENAME)
$(FILENAME): $(OBJ)
$(info Linking $@)
$(CC) $(DRIVER_LDFLAGS) $(OBJ) $(OUTPUT_DIR)dcrt0.o -L$(OUTPUT_DIR) -lkernel -o $@
WARNCFLAG = -Wall -Wextra
%.o: %.c $(HEADERS)
$(info Compiling $<)
$(CC) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@
%.o: %.cpp $(HEADERS)
$(info Compiling $<)
$(CXX) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@
%.o: %.S
$(info Compiling $<)
$(AS) -o $@ $<
clean:
rm -f file.map $(OBJ) $(STACK_USAGE_OBJ)

View File

@ -1,31 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include <base.h>
#include "rtl8139.hpp"
int DriverEntry() { return cxx_Initialize(); }
int DriverFinal() { return cxx_Finalize(); }
int DriverPanic() { return cxx_Panic(); }
int DriverProbe() { return cxx_Probe(); }
DriverInfo("rtl8139",
"Realtek RTL8139 Network Driver",
"EnderIce2",
0, 0, 1,
"GPLv3");

View File

@ -1,325 +0,0 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <netools.h>
#include <errno.h>
#include <regs.h>
#include <base.h>
#include <pci.h>
#include <network.h>
#include <io.h>
#include "rtl8139.hpp"
class RTL8139Device
{
private:
PCIHeader0 *Header;
bool Initialized = false;
struct BARData
{
uint8_t Type;
uint16_t IOBase;
uint64_t MemoryBase;
} BAR;
const int BaseBufferSize = 8192;
const int WRAPBytes = 1500;
const int AdditionalBytes = 16;
const int BufferSize = BaseBufferSize +
WRAPBytes +
AdditionalBytes;
uint8_t *RXBuffer = nullptr;
int TXCurrent = 0;
uint16_t CurrentPacket = 0;
uint8_t TSAD[4] = {0x20, 0x24, 0x28, 0x2C};
uint8_t TSD[4] = {0x10, 0x14, 0x18, 0x1C};
public:
dev_t ID;
bool IsInitialized() { return Initialized; }
size_t write(uint8_t *Buffer, size_t Size)
{
outl(TSAD[TXCurrent], (uint32_t)(reinterpret_cast<uint64_t>(Buffer)));
outl(TSD[TXCurrent++], (uint32_t)Size);
if (TXCurrent > 3)
TXCurrent = 0;
return Size;
}
MediaAccessControl GetMAC()
{
return MediaAccessControl();
}
int ioctl(NetIoctl req, void *)
{
switch (req)
{
case IOCTL_NET_GET_MAC:
{
return -ENOSYS;
}
default:
return -EINVAL;
}
return 0;
}
void OnInterruptReceived(TrapFrame *)
{
/* Acknowledge interrupt */
uint16_t status = inw(RegISR);
DebugLog("%#lx", status);
/* Read status */
if (status & RecOK)
{
/* Get the current packet */
uint16_t *data = (uint16_t *)(RXBuffer + CurrentPacket);
uint16_t dataSz = *(data + 1);
data += 2;
// ReportNetworkPacket(ID, data, dataSz);
/* FIXME: Implement */
KernelLog("FIXME: Received packet");
(void)data;
(void)dataSz;
/* Update CAPR */
#define RX_READ_PTR_MASK (~0x3)
CurrentPacket = (uint16_t)((CurrentPacket + dataSz + 4 + 3) & RX_READ_PTR_MASK);
if (CurrentPacket > BufferSize)
CurrentPacket -= uint16_t(BufferSize);
outw(RegCAPR, CurrentPacket - 0x10);
}
/* Clear interrupt */
outw(RegISR, (RecOK | RecBad | SendOK | SendBad));
}
void Panic()
{
}
RTL8139Device(PCIHeader0 *_Header)
: Header(_Header)
{
uint32_t PCIBAR0 = Header->BAR0;
uint32_t PCIBAR1 = Header->BAR1;
BAR.Type = PCIBAR0 & 1;
BAR.IOBase = (uint16_t)(PCIBAR0 & (~3));
BAR.MemoryBase = PCIBAR1 & (~15);
RXBuffer = (uint8_t *)AllocateMemory(TO_PAGES(BufferSize));
/* Power on */
outb(RegCONFIG1, 0x0);
/* Software Reset */
outb(RegCMD, 0x10);
while (inb(RegCMD) & 0x10)
Yield();
/* Initialize receive buffer */
outl(RegRBSTART, (uint32_t)(reinterpret_cast<uintptr_t>(RXBuffer)));
/* Configure interrupt mask register */
outw(RegIMR, (RecOK | RecBad | SendOK | SendBad));
outl(regRCR, (RcAB | RcAM | RcAPM | RcAAP) | RcWRAP);
/* Enable receive and transmit */
outb(RegCMD, 0xC); /* 0xC = RE and TE bit */
uint32_t MAC1 = inl(RegMAC);
uint16_t MAC2 = inw(RegMAR);
MediaAccessControl mac = {
mac.Address[0] = (uint8_t)MAC1,
mac.Address[1] = (uint8_t)(MAC1 >> 8),
mac.Address[2] = (uint8_t)(MAC1 >> 16),
mac.Address[3] = (uint8_t)(MAC1 >> 24),
mac.Address[4] = (uint8_t)MAC2,
mac.Address[5] = (uint8_t)(MAC2 >> 8)};
Initialized = true;
}
~RTL8139Device()
{
if (!Initialized)
return;
/* FIXME: Shutdown code */
}
};
RTL8139Device *Drivers[4] = {nullptr};
dev_t NetID[4] = {(dev_t)-1};
#define OIR(x) OIR_##x
#define CREATE_OIR(x) \
void OIR_##x(TrapFrame *f) { Drivers[x]->OnInterruptReceived(f); }
CREATE_OIR(0);
CREATE_OIR(1);
CREATE_OIR(2);
CREATE_OIR(3);
int __fs_Open(struct Inode *, int, mode_t) { return 0; }
int __fs_Close(struct Inode *) { return 0; }
ssize_t __fs_Read(struct Inode *, void *, size_t, off_t) { return 0; }
ssize_t __fs_Write(struct Inode *Node, const void *Buffer, size_t Size, off_t)
{
return Drivers[NetID[Node->GetMinor()]]->write((uint8_t *)Buffer, Size);
}
int __fs_Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
{
return Drivers[NetID[Node->GetMinor()]]->ioctl((NetIoctl)Request, Argp);
}
const struct InodeOperations NetOps = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = __fs_Read,
.Write = __fs_Write,
.Truncate = nullptr,
.Open = __fs_Open,
.Close = __fs_Close,
.Ioctl = __fs_Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
PCIArray *Devices;
EXTERNC int cxx_Panic()
{
PCIArray *ctx = Devices;
short Count = 0;
while (ctx != nullptr)
{
if (Drivers[Count] != nullptr)
Drivers[Count]->Panic();
Count++;
ctx = (PCIArray *)ctx->Next;
}
return 0;
}
EXTERNC int cxx_Probe()
{
uint16_t VendorIDs[] = {0x10EC, /* Realtek */
PCI_END};
uint16_t DeviceIDs[] = {0x8139, /* RTL8139 */
PCI_END};
Devices = GetPCIDevices(VendorIDs, DeviceIDs);
if (Devices == nullptr)
{
KernelLog("No RTL8139 device found.");
return -ENODEV;
}
return 0;
}
EXTERNC int cxx_Initialize()
{
PCIArray *ctx = Devices;
size_t Count = 0;
while (ctx != nullptr)
{
if (Count > sizeof(Drivers) / sizeof(RTL8139Device *))
break;
InitializePCI(ctx->Device);
Drivers[Count] = new RTL8139Device((PCIHeader0 *)ctx->Device->Header);
if (Drivers[Count]->IsInitialized())
{
dev_t ret = RegisterDevice(NETWORK_TYPE_ETHERNET, &NetOps);
NetID[Count] = ret;
Drivers[Count]->ID = ret;
/* FIXME: bad code */
switch (Count)
{
case 0:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(0));
break;
case 1:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(1));
break;
case 2:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(2));
break;
case 3:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(3));
break;
default:
break;
}
Count++;
}
ctx = (PCIArray *)ctx->Next;
}
if (Count == 0)
{
KernelLog("No valid RTL8139 device found.");
return -EINVAL;
}
return 0;
}
EXTERNC int cxx_Finalize()
{
PCIArray *ctx = Devices;
size_t Count = 0;
while (ctx != nullptr)
{
if (Count++ > sizeof(Drivers) / sizeof(RTL8139Device *))
break;
delete Drivers[Count++];
ctx->Device->Header->Command |= PCI_COMMAND_INTX_DISABLE;
ctx = (PCIArray *)ctx->Next;
}
for (size_t i = 0; i < sizeof(NetID) / sizeof(dev_t); i++)
{
if (NetID[i] != (dev_t)-1)
UnregisterDevice(NetID[i]);
}
return 0;
}

View File

@ -1,7 +1,9 @@
build:
make -C ahci build
make -C ata build
MAKE_TARGETS := build clean
DIRECTORIES := $(sort $(dir $(wildcard ./*/)))
clean:
make -C ahci clean
make -C ata clean
.PHONY: $(MAKE_TARGETS) $(DIRECTORIES)
$(MAKE_TARGETS): $(DIRECTORIES)
$(DIRECTORIES):
$(MAKE) -C $@ $(MAKECMDGOALS)

View File

@ -1,35 +0,0 @@
default:
$(error Do not run this Makefile directly!)
S_SOURCES = $(shell find ./ -type f -name '*.S')
C_SOURCES = $(shell find ./ -type f -name '*.c')
CXX_SOURCES = $(shell find ./ -type f -name '*.cpp')
HEADERS = $(sort $(dir $(wildcard ../../include/*)))
OBJ = $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) $(S_SOURCES:.S=.o)
STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CXX_SOURCES:.cpp=.su)
FILENAME = $(notdir $(shell pwd)).drv
build: $(FILENAME)
mv $(FILENAME) $(OUTPUT_DIR)$(FILENAME)
$(FILENAME): $(OBJ)
$(info Linking $@)
$(CC) $(DRIVER_LDFLAGS) $(OBJ) $(OUTPUT_DIR)dcrt0.o -L$(OUTPUT_DIR) -lkernel -o $@
WARNCFLAG = -Wall -Wextra
%.o: %.c $(HEADERS)
$(info Compiling $<)
$(CC) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@
%.o: %.cpp $(HEADERS)
$(info Compiling $<)
$(CXX) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@
%.o: %.S
$(info Compiling $<)
$(AS) -o $@ $<
clean:
rm -f file.map $(OBJ) $(STACK_USAGE_OBJ)

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <types.h>
EXTERNC int cxx_Panic();
EXTERNC int cxx_Probe();
EXTERNC int cxx_Initialize();
EXTERNC int cxx_Finalize();

View File

@ -1,31 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include <base.h>
#include "ahci.hpp"
int DriverEntry() { return cxx_Initialize(); }
int DriverFinal() { return cxx_Finalize(); }
int DriverPanic() { return cxx_Panic(); }
int DriverProbe() { return cxx_Probe(); }
DriverInfo("ahci",
"Advanced Host Controller Interface Driver",
"EnderIce2",
0, 0, 1,
"GPLv3");

View File

@ -1,35 +0,0 @@
default:
$(error Do not run this Makefile directly!)
S_SOURCES = $(shell find ./ -type f -name '*.S')
C_SOURCES = $(shell find ./ -type f -name '*.c')
CXX_SOURCES = $(shell find ./ -type f -name '*.cpp')
HEADERS = $(sort $(dir $(wildcard ../../include/*)))
OBJ = $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) $(S_SOURCES:.S=.o)
STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CXX_SOURCES:.cpp=.su)
FILENAME = $(notdir $(shell pwd)).drv
build: $(FILENAME)
mv $(FILENAME) $(OUTPUT_DIR)$(FILENAME)
$(FILENAME): $(OBJ)
$(info Linking $@)
$(CC) $(DRIVER_LDFLAGS) $(OBJ) $(OUTPUT_DIR)dcrt0.o -L$(OUTPUT_DIR) -lkernel -o $@
WARNCFLAG = -Wall -Wextra
%.o: %.c $(HEADERS)
$(info Compiling $<)
$(CC) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@
%.o: %.cpp $(HEADERS)
$(info Compiling $<)
$(CXX) $(DRIVER_CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@
%.o: %.S
$(info Compiling $<)
$(AS) -o $@ $<
clean:
rm -f file.map $(OBJ) $(STACK_USAGE_OBJ)

View File

@ -1,76 +0,0 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <regs.h>
#include <base.h>
#include <io.h>
bool IsATAPresent()
{
outb(0x1F0 + 2, 0);
outb(0x1F0 + 3, 0);
outb(0x1F0 + 4, 0);
outb(0x1F0 + 5, 0);
outb(0x1F0 + 7, 0xEC);
if (inb(0x1F0 + 7) == 0 || inb(0x1F0 + 1) != 0)
return false;
return true;
}
void MasterInterruptHandler(TrapFrame *)
{
}
void SlaveInterruptHandler(TrapFrame *)
{
}
int DriverEntry()
{
RegisterInterruptHandler(14, MasterInterruptHandler);
RegisterInterruptHandler(15, SlaveInterruptHandler);
return 0;
}
int DriverFinal()
{
UnregisterInterruptHandler(14, MasterInterruptHandler);
UnregisterInterruptHandler(15, SlaveInterruptHandler);
return 0;
}
int DriverPanic()
{
return 0;
}
int DriverProbe()
{
if (!IsATAPresent())
return -ENODEV;
return 0;
}
DriverInfo("ata",
"Advanced Technology Attachment Driver",
"EnderIce2",
0, 0, 1,
"GPLv3");

View File

@ -26,6 +26,15 @@
"git.alwaysSignOff": true,
"git.defaultBranchName": "master",
"git.openRepositoryInParentFolders": "always",
"C_Cpp.autoAddFileAssociations": false
"C_Cpp.autoAddFileAssociations": false,
"conventionalCommits.scopes": [
"kernel/api",
"kernel/syscalls",
"vscode",
"kernel",
"kernel/pci",
"kernel/driver",
"kernel/drivers"
]
}
}
}

View File

@ -10,6 +10,15 @@
"git.alwaysSignOff": true,
"git.defaultBranchName": "master",
"git.openRepositoryInParentFolders": "always",
"C_Cpp.autoAddFileAssociations": false
"C_Cpp.autoAddFileAssociations": false,
"conventionalCommits.scopes": [
"userspace/libc",
"vscode",
"userspace/apps/sys/init",
"userspace/libs/libm",
"devcontainer",
"userspace/libs",
"userspace/apps/test"
]
}
}
}

View File

@ -9,6 +9,11 @@
"git.alwaysSignOff": true,
"git.defaultBranchName": "master",
"git.openRepositoryInParentFolders": "always",
"C_Cpp.autoAddFileAssociations": false
"C_Cpp.autoAddFileAssociations": false,
"conventionalCommits.scopes": [
"initrd",
"tools",
"devcontainer"
]
}
}
}

16
Kernel/.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
"recommendations": [
"ms-vscode.cpptools",
"maziac.asm-code-lens",
"editorconfig.editorconfig",
"vivaxy.vscode-conventional-commits",
"ms-vscode.hexeditor",
"webfreak.debug",
"ibm.output-colorizer",
"gruntfuggly.todo-tree",
"naumovs.color-highlight",
"seven1bit.vscode-ext-ansi-color-highlight",
"jeff-hykin.better-cpp-syntax",
"aaron-bond.better-comments"
]
}

View File

@ -77,6 +77,14 @@ SECTIONS
*(.rodata .rodata.*)
} :rodata
.builtin_drivers ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.builtin_drivers) - KERNEL_VMA)
{
__kernel_builtin_drivers_start = .;
KEEP(*(SORT(.builtin_drivers.*)))
KEEP(*(.builtin_drivers))
__kernel_builtin_drivers_end = .;
} :rodata
.init_array ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.init_array) - KERNEL_VMA)
{
PROVIDE_HIDDEN(__init_array_start = .);

View File

@ -80,6 +80,14 @@ SECTIONS
*(.rodata .rodata.*)
} :rodata
.builtin_drivers ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.builtin_drivers) - KERNEL_VMA)
{
__kernel_builtin_drivers_start = .;
KEEP(*(SORT(.builtin_drivers.*)))
KEEP(*(.builtin_drivers))
__kernel_builtin_drivers_end = .;
} :rodata
.init_array ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.init_array) - KERNEL_VMA)
{
PROVIDE_HIDDEN(__init_array_start = .);

View File

@ -58,6 +58,14 @@ SECTIONS
}
. = ALIGN(4096);
.builtin_drivers :
{
__kernel_builtin_drivers_start = .;
KEEP(*(SORT(.builtin_drivers.*)))
KEEP(*(.builtin_drivers))
__kernel_builtin_drivers_end = .;
}
.init_array :
{
PROVIDE_HIDDEN(__init_array_start = .);

View File

@ -80,6 +80,14 @@ SECTIONS
*(.rodata .rodata.*)
} :rodata
.builtin_drivers ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.builtin_drivers) - KERNEL_VMA)
{
__kernel_builtin_drivers_start = .;
KEEP(*(SORT(.builtin_drivers.*)))
KEEP(*(.builtin_drivers))
__kernel_builtin_drivers_end = .;
} :rodata
.init_array ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.init_array) - KERNEL_VMA)
{
PROVIDE_HIDDEN(__init_array_start = .);

View File

@ -15,6 +15,7 @@
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#define NO_API_IN_HEADER 1
#include <driver.hpp>
#include <interface/driver.h>
#include <interface/fs.h>
@ -66,12 +67,10 @@ namespace v0
uintptr_t Flags;
#if defined(__x86_64__)
asmv("pushfq");
asmv("popq %0"
: "=r"(Flags));
asmv("popq %0" : "=r"(Flags));
#else
asmv("pushfl");
asmv("popl %0"
: "=r"(Flags));
asmv("popl %0" : "=r"(Flags));
#endif
cs = Flags & (1 << 9);
asmv("cli");
@ -453,29 +452,13 @@ namespace v0
void *AllocateMemory(dev_t DriverID, size_t Pages)
{
dbg_api("%d, %d", DriverID, Pages);
std::unordered_map<dev_t, Driver::DriverObject> &Drivers =
DriverManager->GetDrivers();
auto itr = Drivers.find(DriverID);
assert(itr != Drivers.end());
void *ptr = itr->second.vma->RequestPages(Pages);
memset(ptr, 0, FROM_PAGES(Pages));
return ptr;
return DriverManager->AllocateMemory(DriverID, Pages);
}
void FreeMemory(dev_t DriverID, void *Pointer, size_t Pages)
{
dbg_api("%d, %#lx, %d", DriverID, Pointer, Pages);
std::unordered_map<dev_t, Driver::DriverObject> &Drivers =
DriverManager->GetDrivers();
auto itr = Drivers.find(DriverID);
assert(itr != Drivers.end());
itr->second.vma->FreePages(Pointer, Pages);
DriverManager->FreeMemory(DriverID, Pointer, Pages);
}
void *MemoryCopy(dev_t DriverID, void *Destination, const void *Source, size_t Length)
@ -621,131 +604,7 @@ namespace v0
void InitializePCI(dev_t DriverID, void *_Header)
{
dbg_api("%d, %#lx", DriverID, _Header);
PCI::PCIDevice *__device = (PCI::PCIDevice *)_Header;
PCI::PCIDeviceHeader *Header = (PCI::PCIDeviceHeader *)__device->Header;
debug("Header Type: %d", Header->HeaderType);
switch (Header->HeaderType)
{
case 128:
warn("Unknown header type %d! Guessing PCI Header 0",
Header->HeaderType);
[[fallthrough]];
case 0: /* PCI Header 0 */
{
PCI::PCIHeader0 *hdr0 = (PCI::PCIHeader0 *)Header;
uint32_t BAR[6];
size_t BARsSize[6];
BAR[0] = hdr0->BAR0;
BAR[1] = hdr0->BAR1;
BAR[2] = hdr0->BAR2;
BAR[3] = hdr0->BAR3;
BAR[4] = hdr0->BAR4;
BAR[5] = hdr0->BAR5;
debug("Type: %d; IOBase: %#lx; MemoryBase: %#lx",
BAR[0] & 1, BAR[1] & (~3), BAR[0] & (~15));
/* BARs Size */
for (short i = 0; i < 6; i++)
{
if (BAR[i] == 0)
continue;
size_t size;
if ((BAR[i] & 1) == 0) /* Memory Base */
{
hdr0->BAR0 = 0xFFFFFFFF;
size = hdr0->BAR0;
hdr0->BAR0 = BAR[i];
BARsSize[i] = size & (~15);
BARsSize[i] = ~BARsSize[i] + 1;
BARsSize[i] = BARsSize[i] & 0xFFFFFFFF;
debug("BAR%d %#lx size: %d",
i, BAR[i], BARsSize[i]);
}
else if ((BAR[i] & 1) == 1) /* I/O Base */
{
hdr0->BAR1 = 0xFFFFFFFF;
size = hdr0->BAR1;
hdr0->BAR1 = BAR[i];
BARsSize[i] = size & (~3);
BARsSize[i] = ~BARsSize[i] + 1;
BARsSize[i] = BARsSize[i] & 0xFFFF;
debug("BAR%d %#lx size: %d",
i, BAR[i], BARsSize[i]);
}
}
Memory::Virtual vmm(KernelPageTable);
/* Mapping the BARs */
for (short i = 0; i < 6; i++)
{
if (BAR[i] == 0)
continue;
if ((BAR[i] & 1) == 0) /* Memory Base */
{
uintptr_t BARBase = BAR[i] & (~15);
size_t BARSize = BARsSize[i];
debug("Mapping BAR%d %#lx-%#lx",
i, BARBase, BARBase + BARSize);
if (BARSize == 0)
{
warn("BAR%d size is zero!", i);
BARSize++;
}
vmm.Map((void *)BARBase, (void *)BARBase,
BARSize, Memory::RW | Memory::PWT | Memory::PCD);
}
else if ((BAR[i] & 1) == 1) /* I/O Base */
{
uintptr_t BARBase = BAR[i] & (~3);
size_t BARSize = BARsSize[i];
debug("Mapping BAR%d %#x-%#x",
i, BARBase, BARBase + BARSize);
if (BARSize == 0)
{
warn("BAR%d size is zero!", i);
BARSize++;
}
vmm.Map((void *)BARBase, (void *)BARBase,
BARSize, Memory::RW | Memory::PWT | Memory::PCD);
}
}
break;
}
case 1: /* PCI Header 1 (PCI-to-PCI Bridge) */
{
fixme("PCI Header 1 (PCI-to-PCI Bridge) not implemented yet");
break;
}
case 2: /* PCI Header 2 (PCI-to-CardBus Bridge) */
{
fixme("PCI Header 2 (PCI-to-CardBus Bridge) not implemented yet");
break;
}
default:
{
error("Unknown header type %d", Header->HeaderType);
break;
}
}
Header->Command |= PCI_COMMAND_MASTER |
PCI_COMMAND_IO |
PCI_COMMAND_MEMORY;
Header->Command &= ~PCI_COMMAND_INTX_DISABLE;
PCIManager->InitializeDevice(*(PCI::PCIDevice *)_Header, KernelPageTable);
}
uint32_t GetBAR(dev_t DriverID, uint8_t i, void *_Header)
@ -824,6 +683,13 @@ namespace v0
/* --------- */
dev_t CreateDeviceFile(dev_t DriverID, const char *name, mode_t mode, const InodeOperations *Operations)
{
dbg_api("%d, %s, %#o, %#lx", DriverID, name, mode, Operations);
return DriverManager->CreateDeviceFile(DriverID, name, mode, Operations);
}
dev_t RegisterDevice(dev_t DriverID, DeviceType Type, const InodeOperations *Operations)
{
dbg_api("%d, %d, %#lx", DriverID, Type, Operations);
@ -907,6 +773,7 @@ static struct APISymbols APISymbols_v0[] = {
{"__iLine", (void *)v0::iLine},
{"__iPin", (void *)v0::iPin},
{"__CreateDeviceFile", (void *)v0::CreateDeviceFile},
{"__RegisterDevice", (void *)v0::RegisterDevice},
{"__UnregisterDevice", (void *)v0::UnregisterDevice},
{"__ReportInputEvent", (void *)v0::ReportInputEvent},

View File

@ -40,23 +40,14 @@ namespace Driver
* min:
* 0 - <ROOT>
* 1 - /proc/self
* 2 - /dev/null
* 3 - /dev/zero
* 4 - /dev/random
* 5 - /dev/mem
* 6 - /dev/kcon
* 7 - /dev/tty
* 8 - /dev/ptmx
*
* maj = 1
* min:
* 0 - /dev/input/keyboard
* 1 - /dev/input/mouse
* ..- /dev/input/eventX
* 0 - /dev/input/keyboard
* 1 - /dev/input/mouse
* ..- /dev/input/eventN
*/
TTY::PTMXDevice *ptmx = nullptr;
int __fs_Lookup(struct Inode *_Parent, const char *Name, struct Inode **Result)
{
func("%#lx %s %#lx", _Parent, Name, Result);
@ -99,8 +90,10 @@ namespace Driver
_dev->Parent = nullptr;
_dev->ParentInode = _Parent;
_dev->Name = Name;
_dev->Node.Device = Parent->Node.Device;
_dev->Node.Mode = Mode;
_dev->Node.Index = Parent->Node.Index + Parent->Children.size();
_dev->Node.Offset = Parent->Children.size();
Parent->Children.push_back(_dev);
*Result = &_dev->Node;
@ -112,61 +105,6 @@ namespace Driver
func("%#lx %d %d", Node, Size, Offset);
switch (Node->GetMajor())
{
case 0:
{
switch (Node->GetMinor())
{
case 2: /* /dev/null */
{
return 0;
}
case 3: /* /dev/zero */
{
if (Size <= 0)
return 0;
memset(Buffer, 0, Size);
return Size;
}
case 4: /* /dev/random */
{
if (Size <= 0)
return 0;
if (Size < sizeof(uint64_t))
{
uint8_t *buf = (uint8_t *)Buffer;
for (size_t i = 0; i < Size; i++)
buf[i] = (uint8_t)(Random::rand16() & 0xFF);
return Size;
}
uint64_t *buf = (uint64_t *)Buffer;
for (size_t i = 0; i < Size / sizeof(uint64_t); i++)
buf[i] = Random::rand64();
return Size;
}
case 5: /* /dev/mem */
{
stub;
return 0;
}
case 6: /* /dev/kcon */
return KernelConsole::CurrentTerminal.load()->Read(Buffer, Size, Offset);
case 7: /* /dev/tty */
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Read(Buffer, Size, Offset);
}
case 8: /* /dev/ptmx */
return -ENOSYS;
default:
return -ENOENT;
};
break;
}
case 1:
{
switch (Node->GetMinor())
@ -231,42 +169,6 @@ namespace Driver
switch (Node->GetMajor())
{
case 0:
{
switch (Node->GetMinor())
{
case 2: /* /dev/null */
{
return Size;
}
case 3: /* /dev/zero */
{
return Size;
}
case 4: /* /dev/random */
{
return Size;
}
case 5: /* /dev/mem */
{
stub;
return 0;
}
case 6: /* /dev/kcon */
return KernelConsole::CurrentTerminal.load()->Write(Buffer, Size, Offset);
case 7: /* /dev/tty */
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Write(Buffer, Size, Offset);
}
case 8: /* /dev/ptmx */
return -ENOSYS;
default:
return -ENOENT;
};
}
case 1:
{
switch (Node->GetMinor())
@ -309,30 +211,6 @@ namespace Driver
switch (Node->GetMajor())
{
case 0:
{
switch (Node->GetMinor())
{
case 2: /* /dev/null */
case 3: /* /dev/zero */
case 4: /* /dev/random */
case 5: /* /dev/mem */
return -ENOSYS;
case 6: /* /dev/kcon */
return KernelConsole::CurrentTerminal.load()->Open(Flags, Mode);
case 7: /* /dev/tty */
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Open(Flags, Mode);
}
case 8: /* /dev/ptmx */
return ptmx->Open();
default:
return -ENOENT;
};
}
case 1:
{
switch (Node->GetMinor())
@ -370,30 +248,6 @@ namespace Driver
switch (Node->GetMajor())
{
case 0:
{
switch (Node->GetMinor())
{
case 2: /* /dev/null */
case 3: /* /dev/zero */
case 4: /* /dev/random */
case 5: /* /dev/mem */
return -ENOSYS;
case 6: /* /dev/kcon */
return KernelConsole::CurrentTerminal.load()->Close();
case 7: /* /dev/tty */
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Close();
}
case 8: /* /dev/ptmx */
return ptmx->Close();
default:
return -ENOENT;
};
}
case 1:
{
switch (Node->GetMinor())
@ -431,31 +285,6 @@ namespace Driver
switch (Node->GetMajor())
{
case 0:
{
switch (Node->GetMinor())
{
case 2: /* /dev/null */
case 3: /* /dev/zero */
case 4: /* /dev/random */
case 5: /* /dev/mem */
return -ENOSYS;
case 6: /* /dev/kcon */
return KernelConsole::CurrentTerminal.load()->Ioctl(Request, Argp);
case 7: /* /dev/tty */
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Ioctl(Request, Argp);
}
case 8: /* /dev/ptmx */
return -ENOSYS;
default:
return -ENOENT;
};
break;
}
case 1:
{
switch (Node->GetMinor())
@ -666,8 +495,7 @@ namespace Driver
dev_t Manager::RegisterDevice(dev_t DriverID, DeviceType Type, const InodeOperations *Operations)
{
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
std::unordered_map<dev_t, Driver::DriverObject> &drivers = DriverManager->GetDrivers();
const auto it = drivers.find(DriverID);
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
@ -696,10 +524,46 @@ namespace Driver
ReturnLogError(-1, "No available slots for device %d", DriverID);
}
dev_t Manager::CreateDeviceFile(dev_t DriverID, const char *name, mode_t mode, const InodeOperations *Operations)
{
std::unordered_map<dev_t, Driver::DriverObject> &drivers = DriverManager->GetDrivers();
const auto it = drivers.find(DriverID);
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
const Driver::DriverObject *drv = &it->second;
auto dop = drv->DeviceOperations;
for (size_t i = 0; i < 128; i++)
{
const auto dOps = dop->find(i);
if (dOps != dop->end())
continue;
FileNode *node = fs->GetByPath(name, devNode);
if (node)
ReturnLogError(-EEXIST, "Device file %s already exists", name);
node = fs->Create(devNode, name, mode);
if (node == nullptr)
ReturnLogError(-ENOMEM, "Failed to create device file %s", name);
node->Node->SetDevice(DriverID, i);
DriverHandlers dh{};
dh.Ops = Operations;
dh.Node = node->Node;
dh.InputReports = new RingBuffer<InputReport>(16);
dop->insert({i, std::move(dh)});
return i;
}
ReturnLogError(-1, "No available slots for device %d", DriverID);
return -1; /* -Werror=return-type */
}
int Manager::UnregisterDevice(dev_t DriverID, dev_t Device)
{
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
std::unordered_map<dev_t, Driver::DriverObject> &drivers = DriverManager->GetDrivers();
const auto it = drivers.find(DriverID);
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
@ -715,6 +579,22 @@ namespace Driver
return 0;
}
void *Manager::AllocateMemory(dev_t DriverID, size_t Pages)
{
auto itr = Drivers.find(DriverID);
assert(itr != Drivers.end());
void *ptr = itr->second.vma->RequestPages(Pages);
memset(ptr, 0, FROM_PAGES(Pages));
return ptr;
}
void Manager::FreeMemory(dev_t DriverID, void *Pointer, size_t Pages)
{
auto itr = Drivers.find(DriverID);
assert(itr != Drivers.end());
itr->second.vma->FreePages(Pointer, Pages);
}
int Manager::ReportInputEvent(dev_t DriverID, InputReport *Report)
{
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
@ -753,8 +633,6 @@ namespace Driver
void Manager::InitializeDaemonFS()
{
ptmx = new TTY::PTMXDevice;
dev_t MinorID = 0;
DeviceInode *_dev = new DeviceInode;
_dev->Name = "dev";
@ -775,12 +653,21 @@ namespace Driver
fsi->SuperOps = {};
fsi->Ops.Lookup = __fs_Lookup;
fsi->Ops.Create = __fs_Create;
fsi->Ops.Remove = nullptr;
fsi->Ops.Rename = nullptr;
fsi->Ops.Read = __fs_Read;
fsi->Ops.Write = __fs_Write;
fsi->Ops.Truncate = nullptr;
fsi->Ops.Open = __fs_Open;
fsi->Ops.Close = __fs_Close;
fsi->Ops.Ioctl = __fs_Ioctl;
fsi->Ops.ReadDir = __fs_Readdir;
fsi->Ops.MkDir = nullptr;
fsi->Ops.RmDir = nullptr;
fsi->Ops.SymLink = nullptr;
fsi->Ops.ReadLink = nullptr;
fsi->Ops.Seek = nullptr;
fsi->Ops.Stat = nullptr;
dev->Device = fs->RegisterFileSystem(fsi, dev);
dev->SetDevice(0, MinorID++);
@ -820,57 +707,6 @@ namespace Driver
p1->Children.push_back(device);
};
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH |
S_IFCHR;
createDevice(_dev, devNode, "null", 0, MinorID++, mode);
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH |
S_IFCHR;
createDevice(_dev, devNode, "zero", 0, MinorID++, mode);
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH |
S_IFCHR;
createDevice(_dev, devNode, "random", 0, MinorID++, mode);
/* c rw- r-- --- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP |
S_IFCHR;
createDevice(_dev, devNode, "mem", 0, MinorID++, mode);
/* c rw- r-- --- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP |
S_IFCHR;
createDevice(_dev, devNode, "kcon", 0, MinorID++, mode);
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IRUSR | S_IWUSR |
S_IFCHR;
createDevice(_dev, devNode, "tty", 0, MinorID++, mode);
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IRUSR | S_IWUSR |
S_IFCHR;
createDevice(_dev, devNode, "ptmx", 0, MinorID++, mode);
/* ------------------------------------------------------ */
MinorID = 0;
/* c rw- r-- --- */

View File

@ -37,6 +37,36 @@ namespace Driver
void Manager::PreloadDrivers()
{
debug("Initializing driver manager");
auto &bs = __kernel_builtin_drivers_start;
auto &be = __kernel_builtin_drivers_end;
for (const BuiltInDriver *drv = bs; drv < be; ++drv)
{
debug("Loading built-in driver %s", drv->Name);
DriverObject drvObj = {};
strncpy(drvObj.Name, drv->Name, sizeof(drvObj.Name));
strncpy(drvObj.Description, drv->Description, sizeof(drvObj.Description));
strncpy(drvObj.Author, drv->Author, sizeof(drvObj.Author));
drvObj.Version.Major = drv->Version.Major;
drvObj.Version.Minor = drv->Version.Minor;
drvObj.Version.Patch = drv->Version.Patch;
drvObj.Entry = drv->Entry;
drvObj.Final = drv->Final;
drvObj.Panic = drv->Panic;
drvObj.Probe = drv->Probe;
drvObj.IsBuiltIn = true;
drvObj.BaseAddress = 0;
drvObj.EntryPoint = drv->EntryPoint;
drvObj.vma = new Memory::VirtualMemoryArea(thisProcess->PageTable);
drvObj.Path = "<builtin>";
drvObj.InterruptHandlers = new std::unordered_map<uint8_t, void *>();
drvObj.DeviceOperations = new std::unordered_map<dev_t, DriverHandlers>();
drvObj.ID = DriverIDCounter;
Drivers.insert({DriverIDCounter++, drvObj});
}
const char *DriverDirectory = Config.DriverDirectory;
FileNode *drvDirNode = fs->GetByPath(DriverDirectory, nullptr);
if (!drvDirNode)
@ -58,13 +88,15 @@ namespace Driver
continue;
}
DriverObject drvObj = {.BaseAddress = 0,
.EntryPoint = 0,
.vma = new Memory::VirtualMemoryArea(thisProcess->PageTable),
.Path = drvNode->Path,
.InterruptHandlers = new std::unordered_map<uint8_t, void *>(),
.DeviceOperations = new std::unordered_map<dev_t, DriverHandlers>(),
.ID = DriverIDCounter};
DriverObject drvObj = {};
drvObj.IsBuiltIn = false;
drvObj.BaseAddress = 0;
drvObj.EntryPoint = 0;
drvObj.vma = new Memory::VirtualMemoryArea(thisProcess->PageTable);
drvObj.Path = drvNode->Path;
drvObj.InterruptHandlers = new std::unordered_map<uint8_t, void *>();
drvObj.DeviceOperations = new std::unordered_map<dev_t, DriverHandlers>();
drvObj.ID = DriverIDCounter;
int err = this->LoadDriverFile(drvObj, drvNode);
debug("err = %d (%s)", err, strerror(err));
@ -111,7 +143,7 @@ namespace Driver
continue;
}
KPrint("Loading driver %s", Drv.Name);
KPrint("Loading driver %s (%d)", Drv.Name, Drv.ID);
debug("Calling Probe()=%#lx on driver %s",
Drv.Probe, Drv.Path.c_str());
@ -514,6 +546,102 @@ namespace Driver
return 0;
}
void Manager::ReloadDriver(dev_t driverID)
{
auto it = Drivers.find(driverID);
if (it == Drivers.end())
{
error("Driver with ID %d not found", driverID);
return;
}
DriverObject &Drv = it->second;
if (!Drv.Initialized)
{
error("Driver %s is not initialized", Drv.Name);
return;
}
debug("Reloading driver %s", Drv.Name);
/* Unload the driver */
int err = Drv.Final();
if (err < 0)
{
warn("Failed to unload driver %s: %s", Drv.Name, strerror(err));
}
/* Free resources */
Drv.vma->FreeAllPages();
delete Drv.vma;
delete Drv.InterruptHandlers;
delete Drv.DeviceOperations;
/* Reload the driver */
FileNode *drvNode = fs->GetByPath(Drv.Path.c_str(), nullptr);
if (!drvNode)
{
error("Failed to open driver file %s", Drv.Path.c_str());
return;
}
DriverObject newDrvObj = {};
newDrvObj.IsBuiltIn = false;
newDrvObj.BaseAddress = 0;
newDrvObj.EntryPoint = 0;
newDrvObj.vma = new Memory::VirtualMemoryArea(thisProcess->PageTable);
newDrvObj.Path = drvNode->Path;
newDrvObj.InterruptHandlers = new std::unordered_map<uint8_t, void *>();
newDrvObj.DeviceOperations = new std::unordered_map<dev_t, DriverHandlers>();
newDrvObj.ID = driverID;
err = this->LoadDriverFile(newDrvObj, drvNode);
if (err != 0)
{
error("Failed to load driver %s: %s", drvNode->Path.c_str(), strerror(err));
delete newDrvObj.vma;
delete newDrvObj.InterruptHandlers;
delete newDrvObj.DeviceOperations;
return;
}
Drivers[driverID] = newDrvObj;
/* Initialize the driver */
int (*DrvInit)(dev_t) = (int (*)(dev_t))newDrvObj.EntryPoint;
newDrvObj.ErrorCode = DrvInit(newDrvObj.ID);
if (newDrvObj.ErrorCode < 0)
{
KPrint("FATAL: _start() failed for %s: %s", newDrvObj.Name, strerror(newDrvObj.ErrorCode));
error("Failed to load driver %s: %s", newDrvObj.Path.c_str(), strerror(newDrvObj.ErrorCode));
newDrvObj.vma->FreeAllPages();
return;
}
KPrint("Loading driver %s", newDrvObj.Name);
newDrvObj.ErrorCode = newDrvObj.Probe();
if (newDrvObj.ErrorCode < 0)
{
KPrint("Probe() failed for %s: %s", newDrvObj.Name, strerror(newDrvObj.ErrorCode));
error("Failed to probe driver %s: %s", newDrvObj.Path.c_str(), strerror(newDrvObj.ErrorCode));
newDrvObj.vma->FreeAllPages();
return;
}
newDrvObj.ErrorCode = newDrvObj.Entry();
if (newDrvObj.ErrorCode < 0)
{
KPrint("Entry() failed for %s: %s", newDrvObj.Name, strerror(newDrvObj.ErrorCode));
error("Failed to initialize driver %s: %s", newDrvObj.Path.c_str(), strerror(newDrvObj.ErrorCode));
newDrvObj.vma->FreeAllPages();
return;
}
debug("Reloaded driver %s", newDrvObj.Path.c_str());
newDrvObj.Initialized = true;
}
Manager::Manager() { this->InitializeDaemonFS(); }
Manager::~Manager()

View File

@ -887,8 +887,8 @@ namespace PCI
{
PCIHeader0 *hdr0 = (PCIHeader0 *)Device.Header;
uint32_t BAR[6];
size_t BARsSize[6];
uint32_t BAR[6] = {};
size_t BARsSize[6] = {};
BAR[0] = hdr0->BAR0;
BAR[1] = hdr0->BAR1;
@ -909,26 +909,46 @@ namespace PCI
size_t size;
if ((BAR[i] & 1) == 0) /* Memory Base */
{
hdr0->BAR0 = 0xFFFFFFFF;
hdr0->BAR0 = UINT32_MAX;
size = hdr0->BAR0;
hdr0->BAR0 = BAR[i];
BARsSize[i] = size & (~15);
BARsSize[i] = ~BARsSize[i] + 1;
BARsSize[i] = BARsSize[i] & 0xFFFFFFFF;
debug("BAR%d %#lx size: %d",
i, BAR[i], BARsSize[i]);
/* FIXME: further testing required */
// if ((BAR[i] & 0x6) == 0x4) /* 64-bit address */
// {
// hdr0->BAR1 = UINT32_MAX;
// size |= ((size_t)hdr0->BAR1 << 32);
// hdr0->BAR1 = BAR[i + 1];
// i++; /* Skip the next BAR since it's part of the 64-bit address */
// }
size = size & (~15);
size = ~size + 1;
size = size & UINT32_MAX;
if (size == 0)
{
warn("BAR%d size is zero! Device: %#x:%#x", i, Device.Header->VendorID, Device.Header->DeviceID);
size++;
}
BARsSize[i] = size;
}
else if ((BAR[i] & 1) == 1) /* I/O Base */
{
hdr0->BAR1 = 0xFFFFFFFF;
hdr0->BAR1 = UINT32_MAX;
size = hdr0->BAR1;
hdr0->BAR1 = BAR[i];
BARsSize[i] = size & (~3);
BARsSize[i] = ~BARsSize[i] + 1;
BARsSize[i] = BARsSize[i] & 0xFFFF;
debug("BAR%d %#lx size: %d",
i, BAR[i], BARsSize[i]);
size = size & (~3);
size = ~size + 1;
size = size & UINT16_MAX;
if (size == 0)
{
warn("BAR%d size is zero! Device: %#x:%#x", i, Device.Header->VendorID, Device.Header->DeviceID);
size++;
}
BARsSize[i] = size;
}
debug("BAR%d %#lx size: %zu", i, BAR[i], BARsSize[i]);
}
/* Mapping the BARs */
@ -942,24 +962,20 @@ namespace PCI
uintptr_t BARBase = BAR[i] & (~15);
size_t BARSize = BARsSize[i];
debug("Mapping BAR%d %#lx-%#lx",
i, BARBase, BARBase + BARSize);
debug("Mapping BAR%d %#lx-%#lx", i, BARBase, BARBase + BARSize);
if (BARSize > 0)
Memory::Virtual(Table).Map((void *)BARBase, (void *)BARBase,
BARSize, Memory::RW | Memory::PWT);
Memory::Virtual(Table).Map((void *)BARBase, (void *)BARBase,
BARSize, Memory::RW | Memory::PWT | Memory::PCD);
}
else if ((BAR[i] & 1) == 1) /* I/O Base */
{
uintptr_t BARBase = BAR[i] & (~3);
size_t BARSize = BARsSize[i];
debug("Mapping BAR%d %#x-%#x",
i, BARBase, BARBase + BARSize);
debug("Mapping BAR%d %#x-%#x", i, BARBase, BARBase + BARSize);
if (BARSize > 0)
Memory::Virtual(Table).Map((void *)BARBase, (void *)BARBase,
BARSize, Memory::RW | Memory::PWT);
Memory::Virtual(Table).Map((void *)BARBase, (void *)BARBase,
BARSize, Memory::RW | Memory::PWT | Memory::PCD);
}
}
break;
@ -982,6 +998,18 @@ namespace PCI
}
}
void Manager::InitializeDevice(PCIDevice Device, Memory::PageTable *Table)
{
this->MapPCIAddresses(Device, Table);
PCI::PCIDeviceHeader *Header = Device.Header;
Header->Command |= PCI_COMMAND_MASTER |
PCI_COMMAND_IO |
PCI_COMMAND_MEMORY;
Header->Command &= ~PCI_COMMAND_INTX_DISABLE;
}
void Manager::EnumerateFunction(uint64_t DeviceAddress, uint32_t Function, PCIDevice dev)
{
dev.Function = Function;

View File

@ -0,0 +1,820 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <cpu.hpp>
#include <pci.hpp>
#include <io.h>
extern Driver::Manager *DriverManager;
extern PCI::Manager *PCIManager;
namespace Driver::AC97
{
dev_t DriverID;
#define DescriptorListLength 0x20
enum AudioVolumeValues
{
AV_Maximum = 0x0,
AV_Minimum = 0x3F,
};
enum AudioEncodingValues
{
AE_PCMs8,
AE_PCMu8,
AE_PCMs16le,
AE_PCMs20le,
AE_PCMs24le,
AE_PCMs32le,
AE_PCMu16le,
AE_PCMu20le,
AE_PCMu24le,
AE_PCMu32le,
AE_PCMs16be,
AE_PCMs20be,
AE_PCMs24be,
AE_PCMs32be,
AE_PCMu16be,
AE_PCMu20be,
AE_PCMu24be,
AE_PCMu32be,
};
enum NativeAudioMixerRegisters
{
/**
* @brief Reset Register
* @note Length: word
*/
NAM_Reset = 0x00,
/**
* @brief Master Volume Register
* @note Length: word
*/
NAM_MasterVolume = 0x02,
/**
* @brief Microphone Volume Register
* @note Length: word
*/
NAM_MicrophoneVolume = 0x0E,
/**
* @brief PCM Out Volume Register
* @note Length: word
*/
NAM_PCMOutVolume = 0x18,
/**
* @brief Select Record Input Register
* @note Length: word
*/
NAM_SelectRecordInput = 0x1A,
/**
* @brief Record Gain Register
* @note Length: word
*/
NAM_RecordGain = 0x1C,
/**
* @brief Record Gain Microphone Register
* @note Length: word
*/
NAM_RecordGainMicrophone = 0x1E,
};
enum NativeAudioBusMasterRegisters
{
/**
* @brief Register box for PCM IN
* @note Length: below
*/
NABM_PCMInBox = 0x00,
/**
* @brief Register box for PCM OUT
* @note Length: below
*/
NABM_PCMOutBox = 0x10,
/**
* @brief Register box for Microphone
* @note Length: below
*/
NABM_MicrophoneBox = 0x20,
/**
* @brief Global Control Register
* @note Length: dword
*/
NABM_GlobalControl = 0x2C, /* 0x30 */
/**
* @brief Global Status Register
* @note Length: dword
*/
NABM_GlobalStatus = 0x30, /* 0x34 */
};
enum NativeAudioBusMasterBoxOffsets
{
/**
* @brief Physical Address of Buffer Descriptor List
* @note Length: dword
*/
NABMBOFF_BufferDescriptorList = 0x00,
/**
* @brief Number of Actual Processed Buffer Descriptor Entry
* @note Length: byte
*/
NABMBOFF_BufferDescriptorEntry = 0x04,
/**
* @brief Number of all Descriptor Entries
* @note Length: byte
*/
NABMBOFF_DescriptorEntries = 0x05,
/**
* @brief Status of transferring Data
* @note Length: word
*/
NABMBOFF_Status = 0x06,
/**
* @brief Number of transferred Samples in Actual Processed Entry
* @note Length: word
*/
NABMBOFF_TransferredSamples = 0x08,
/**
* @brief Number of next processed Buffer Entry
* @note Length: byte
*/
NABMBOFF_NextProcessedBufferEntry = 0x0A,
/**
* @brief Transfer Control
* @note Length: byte
*/
NABMBOFF_TransferControl = 0x0B,
};
enum OutputPulseCodeModulationRegisters
{
/**
* @brief Physical Address of Buffer Descriptor List
* @note Length: dword
*/
PCMOUT_BufferDescriptorList = (int)NABM_PCMOutBox + (int)NABMBOFF_BufferDescriptorList,
/**
* @brief Number of Actual Processed Buffer Descriptor Entry
* @note Length: byte
*/
PCMOUT_BufferDescriptorEntry = (int)NABM_PCMOutBox + (int)NABMBOFF_BufferDescriptorEntry,
/**
* @brief Number of all Descriptor Entries
* @note Length: byte
*/
PCMOUT_DescriptorEntries = (int)NABM_PCMOutBox + (int)NABMBOFF_DescriptorEntries,
/**
* @brief Status of transferring Data
* @note Length: word
*/
PCMOUT_Status = (int)NABM_PCMOutBox + (int)NABMBOFF_Status,
/**
* @brief Number of transferred Samples in Actual Processed Entry
* @note Length: word
*/
PCMOUT_TransferredSamples = (int)NABM_PCMOutBox + (int)NABMBOFF_TransferredSamples,
/**
* @brief Number of next processed Buffer Entry
* @note Length: byte
*/
PCMOUT_NextProcessedBufferEntry = (int)NABM_PCMOutBox + (int)NABMBOFF_NextProcessedBufferEntry,
/**
* @brief Transfer Control
* @note Length: byte
*/
PCMOUT_TransferControl = (int)NABM_PCMOutBox + (int)NABMBOFF_TransferControl,
};
enum TransferControlRegisters
{
/**
* @brief DMA controller control
*
* 0 = Pause transfer
* 1 = Transfer sound data
*/
TC_DMAControllerControl = 0x01,
/**
* @brief Reset
*
* 0 = Remove reset condition
* 1 = Reset this NABM register box, this bit is cleared by card when is reset complete
*/
TC_TransferReset = 0x02,
/**
* @brief Last Buffer Entry Interrupt enable
*
* 0 = Disable interrupt
* 1 = Enable interrupt
*/
TC_LastBufferEntryInterruptEnable = 0x04,
/**
* @brief IOC Interrupt enable
*
* 0 = Disable interrupt
* 1 = Enable interrupt
*/
TC_IOCInterruptEnable = 0x08,
/**
* @brief Fifo ERROR Interrupt enable
*
* 0 = Disable interrupt
* 1 = Enable interrupt
*/
TC_FifoERRORInterruptEnable = 0x10,
};
enum GlobalControlRegisters
{
/**
* @brief Global Interrupt Enable
*
* 0 = Disable Interrupts
* 1 = Enable Interrupts
*/
GC_GlobalInterruptEnable = 0x01,
/**
* @brief Cold reset
*
* 0 = Device is in reset and can not be used
* 1 = Resume to operational state
*/
GC_ColdReset = 0x02,
/**
* @brief Warm reset
*/
GC_WarmReset = 0x04,
/**
* @brief Shut down
*
* 0 = Device is powered
* 1 = Shut down
*/
GC_ShutDown = 0x08,
/**
* @brief Channels for PCM Output
*
* 00 = 2 channels
* 01 = 4 channels
* 10 = 6 channels
* 11 = Reserved
*/
GC_ChannelsForPCMOutput = 0x30,
/**
* @brief PCM Output mode
*
* 00 = 16 bit samples
* 01 = 20 bit samples
*/
GC_PCMOutputMode = 0xC0,
};
struct BufferDescriptorList
{
/**
* @brief Physical Address to sound data in memory
* @note Length: dword
*/
uint32_t Address;
/**
* @brief Number of samples in this buffer
* @note Length: word
*/
uint16_t SampleCount;
/**
* @brief Flags
* @note Length: word
*
* Bit 15 = Interrupt fired when data from this entry is transferred
* Bit 14 = Last entry of buffer, stop playing
* Other bits = Reserved
*/
uint16_t Flags;
} __attribute__((packed));
uint16_t MixerVolume(uint8_t Left, uint8_t Right, bool Mute)
{
return ((uint16_t)((Right & 0x3F) |
((Left & 0x3F) << 0x8) |
(Mute & 1 << 0xF)));
}
class AC97Device
{
private:
PCI::PCIHeader0 *Header;
BufferDescriptorList *DescriptorList = nullptr;
uint16_t MixerAddress;
uint16_t BusMasterAddress;
AudioEncodingValues Encoding = AE_PCMs16le;
char Channels = 2;
uint8_t Volume = AV_Maximum;
bool Mute = false;
int SampleRate = 48000;
char SampleSize = 2;
public:
size_t write(uint8_t *Buffer, size_t Size)
{
if (Buffer == nullptr)
{
info("Invalid buffer.");
return -EINVAL;
}
if ((Size == 0) || (Size % (SampleSize * Channels)))
{
info("Invalid buffer length.");
return -EINVAL;
}
int TotalBDLToFill = (int)((Size + PAGE_SIZE - 1) >> 12);
while (Size > 0)
{
bool ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl);
if (ActiveDMA)
{
int RemainingBDL = 0;
do
{
int CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry);
int LastBDL = inb(BusMasterAddress + PCMOUT_DescriptorEntries);
RemainingBDL = LastBDL - CurrentBDL;
if (RemainingBDL < 0)
RemainingBDL += DescriptorListLength;
RemainingBDL += 1;
if (RemainingBDL >= DescriptorListLength - 1)
{
long SampleCount = DescriptorList[(CurrentBDL + 1) % DescriptorListLength].SampleCount / Channels;
if (SampleCount > 0)
v0::Sleep(DriverID, SampleCount * 1000 / SampleRate);
}
} while (RemainingBDL >= DescriptorListLength - 1 &&
!(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl));
}
uint8_t CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry);
uint8_t LastBDL = inb(BusMasterAddress + PCMOUT_DescriptorEntries);
uint8_t NextBDL = LastBDL % DescriptorListLength;
ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl);
if (ActiveDMA)
{
NextBDL = (uint8_t)((LastBDL + 1) % DescriptorListLength);
if (NextBDL == CurrentBDL)
continue;
}
do
{
size_t Wrote = (PAGE_SIZE > Size) ? size_t(Size)
: size_t(PAGE_SIZE);
if (Wrote == 0)
{
info("Wrote 0 bytes.");
break;
}
memcpy((void *)((uint64_t)DescriptorList[NextBDL].Address), Buffer, Wrote);
DescriptorList[NextBDL].Flags = 0;
Buffer += Wrote;
Size -= (unsigned int)Wrote;
DescriptorList[NextBDL].SampleCount = uint16_t(Wrote / SampleSize);
TotalBDLToFill--;
NextBDL = (uint8_t)((NextBDL + 1) % DescriptorListLength);
} while (TotalBDLToFill-- && NextBDL != CurrentBDL);
outb(BusMasterAddress + PCMOUT_DescriptorEntries, NextBDL - 1);
ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl);
if (!ActiveDMA)
{
// Start DMA
outb(BusMasterAddress + PCMOUT_TransferControl,
inb(BusMasterAddress + PCMOUT_TransferControl) | TC_DMAControllerControl);
}
}
return Size;
}
int ioctl(AudioIoctl, void *)
{
// if (Data->AudioCallback.Adjust._Volume)
// {
// Volume = (uint8_t)(0x3F - (0x3F * Data->AudioCallback.Adjust.Volume / 100));
// outw(BAR.MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute));
// // outw(BAR.MixerAddress + NAM_PCMOutVolume, MixerVolume(Volume, Volume, Mute));
// }
// else if (Data->AudioCallback.Adjust._Encoding)
// {
// fixme("Encoding changing not supported yet.");
// }
// else if (Data->AudioCallback.Adjust._SampleRate)
// {
// switch (Data->AudioCallback.Adjust.SampleRate)
// {
// case 0:
// {
// SampleRate = 8000;
// break;
// }
// case 1:
// {
// SampleRate = 11025;
// break;
// }
// case 2:
// {
// SampleRate = 16000;
// break;
// }
// case 3:
// {
// SampleRate = 22050;
// break;
// }
// case 4:
// {
// SampleRate = 32000;
// break;
// }
// case 5:
// {
// SampleRate = 44100;
// break;
// }
// case 6:
// {
// SampleRate = 48000;
// break;
// }
// case 7:
// {
// SampleRate = 88200;
// break;
// }
// case 8:
// {
// SampleRate = 96000;
// break;
// }
// default:
// {
// SampleRate = 16000;
// error("Invalid sample rate. Defaulting to 16000.");
// break;
// }
// }
// }
// else if (Data->AudioCallback.Adjust._Channels)
// {
// switch (Data->AudioCallback.Adjust.Channels)
// {
// case 0:
// {
// Channels = 1; // Mono
// break;
// }
// case 1:
// {
// Channels = 2; // Stereo
// break;
// }
// default:
// {
// Channels = 2;
// error("Invalid channel count. Defaulting to 2.");
// break;
// }
// }
// }
return 0;
}
void OnInterruptReceived(CPU::TrapFrame *)
{
uint16_t Status = inw(MixerAddress + PCMOUT_Status);
if (Status & TC_IOCInterruptEnable)
{
debug("IOC");
outw(MixerAddress + PCMOUT_Status, TC_IOCInterruptEnable);
uint16_t CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry);
uint16_t LastBDL = (CurrentBDL + 2) & (DescriptorListLength - 1);
outb(BusMasterAddress + PCMOUT_DescriptorEntries, LastBDL);
info("FIXME: CurrentBDL: %d, LastBDL: %d", CurrentBDL, LastBDL);
}
else if (Status & TC_LastBufferEntryInterruptEnable)
{
debug("Last buffer entry");
// Stop DMA
uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl));
TransferControl &= ~TC_DMAControllerControl;
outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl);
outw(MixerAddress + PCMOUT_Status, TC_LastBufferEntryInterruptEnable);
}
else if (Status & TC_FifoERRORInterruptEnable)
{
info("FIFO error");
outw(MixerAddress + PCMOUT_Status, TC_FifoERRORInterruptEnable);
}
else
{
debug("Unknown interrupt status %#x", Status);
outw(MixerAddress + PCMOUT_Status, 0xFFFF);
}
}
void Panic()
{
uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl));
TransferControl &= ~(TC_LastBufferEntryInterruptEnable |
TC_IOCInterruptEnable |
TC_FifoERRORInterruptEnable);
outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl);
uint32_t GlobalControl = inl((uint16_t)(BusMasterAddress + NABM_GlobalControl));
GlobalControl &= ~GC_GlobalInterruptEnable;
GlobalControl |= GC_ShutDown;
outl((uint16_t)(BusMasterAddress + NABM_GlobalControl), GlobalControl);
}
AC97Device(PCI::PCIHeader0 *_Header)
: Header(_Header)
{
/* Native Audio Mixer Base Address */
uint32_t PCIBAR0 = Header->BAR0;
/* Native Audio Bus Master Base Address */
uint32_t PCIBAR1 = Header->BAR1;
// uint8_t Type = PCIBAR0 & 1;
MixerAddress = (uint16_t)(PCIBAR0 & (~3));
BusMasterAddress = PCIBAR1 & (~15);
uint16_t OutputPCMTransferControl = BusMasterAddress + PCMOUT_TransferControl;
/* DescriptorList address MUST be physical. */
DescriptorList = (BufferDescriptorList *)v0::AllocateMemory(DriverID, TO_PAGES(sizeof(BufferDescriptorList) * DescriptorListLength));
memset(DescriptorList, 0, sizeof(BufferDescriptorList) * DescriptorListLength);
uint16_t DLSampleCount = (uint16_t)(PAGE_SIZE / SampleSize);
for (int i = 0; i < DescriptorListLength; i++)
{
DescriptorList[i].Address = (uint32_t)(uintptr_t)v0::AllocateMemory(DriverID, TO_PAGES(sizeof(uint16_t *)));
DescriptorList[i].SampleCount = DLSampleCount;
DescriptorList[i].Flags = 0;
debug("DescriptorList[%d] = { Address: %#lx, SampleCount: %d, Flags: %#lx }",
i,
DescriptorList[i].Address,
DescriptorList[i].SampleCount,
DescriptorList[i].Flags);
}
outw(MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute));
outw(MixerAddress + NAM_PCMOutVolume, MixerVolume(Volume, Volume, Mute));
Volume = 0x3F - (0x3F * /* VOL 50% */ 50 / 100);
outw(MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute));
outb(OutputPCMTransferControl, inb(OutputPCMTransferControl) | TC_TransferReset);
while (inb(OutputPCMTransferControl) & TC_TransferReset)
;
uint32_t GlobalControl = inl(BusMasterAddress + NABM_GlobalControl);
GlobalControl = (GlobalControl & ~((0x3U) << 0x16)); /* PCM 16-bit mode */
GlobalControl = (GlobalControl & ~((0x3U) << 20)); /* 2 channels */
GlobalControl |= GC_GlobalInterruptEnable;
GlobalControl &= ~GC_ShutDown;
outl(BusMasterAddress + PCMOUT_BufferDescriptorList,
(uint32_t)(uint64_t)DescriptorList);
outl(BusMasterAddress + NABM_GlobalControl, GlobalControl);
uint8_t TransferControl = inb(OutputPCMTransferControl);
TransferControl |= TC_IOCInterruptEnable |
TC_FifoERRORInterruptEnable;
outb(OutputPCMTransferControl, TransferControl);
// Stop DMA
outb(OutputPCMTransferControl, inb(OutputPCMTransferControl) & ~TC_DMAControllerControl);
}
~AC97Device()
{
outw(MixerAddress + NAM_MasterVolume, MixerVolume(AV_Maximum, AV_Maximum, true));
outw(MixerAddress + NAM_PCMOutVolume, MixerVolume(AV_Maximum, AV_Maximum, true));
// Stop DMA
outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl),
inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl)) & ~TC_DMAControllerControl);
// Disable interrupts
uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl));
TransferControl &= ~(TC_LastBufferEntryInterruptEnable |
TC_IOCInterruptEnable |
TC_FifoERRORInterruptEnable);
outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl);
// Disable global control
uint32_t GlobalControl = inl((uint16_t)(BusMasterAddress + NABM_GlobalControl));
GlobalControl &= ~GC_GlobalInterruptEnable;
GlobalControl |= GC_ShutDown;
outl((uint16_t)(BusMasterAddress + NABM_GlobalControl), GlobalControl);
}
};
std::unordered_map<dev_t, AC97Device *> Drivers;
int Open(struct Inode *, int, mode_t)
{
return 0;
}
int Close(struct Inode *)
{
return 0;
}
ssize_t Read(struct Inode *, void *, size_t, off_t)
{
return 0;
}
ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t)
{
return Drivers[Node->GetMinor()]->write((uint8_t *)Buffer, Size);
}
int Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
{
return Drivers[Node->GetMinor()]->ioctl((AudioIoctl)Request, Argp);
}
const struct InodeOperations ops = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = Read,
.Write = Write,
.Truncate = nullptr,
.Open = Open,
.Close = Close,
.Ioctl = Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
std::list<PCI::PCIDevice> Devices;
int Entry()
{
for (auto &&dev : Devices)
{
PCIManager->InitializeDevice(dev, KernelPageTable);
AC97Device *ac97 = new AC97Device((PCI::PCIHeader0 *)dev.Header);
dev_t ret = v0::RegisterDevice(DriverID, NETWORK_TYPE_ETHERNET, &ops);
Drivers[ret] = ac97;
}
if (Drivers.empty())
{
info("No valid AC'97 device found.");
return -EINVAL;
}
return 0;
}
int Final()
{
for (auto &&dev : Drivers)
{
dev_t ret = dev.first;
v0::UnregisterDevice(DriverID, ret);
delete dev.second;
}
return 0;
}
int Panic()
{
for (auto &&i : Drivers)
i.second->Panic();
return 0;
}
int Probe()
{
Devices = PCIManager->FindPCIDevice(
{
0x8086, /* Intel */
},
{
0x2415, /* AC'97 */
});
for (auto &&i : Devices)
{
PCI::PCIHeader0 *hdr0 = (PCI::PCIHeader0 *)i.Header;
uint8_t type = hdr0->BAR0 & 1;
if (type != 1)
{
warn("Device %x:%x.%d BAR0 is not I/O.",
hdr0->Header.VendorID,
hdr0->Header.DeviceID,
hdr0->Header.ProgIF);
continue;
}
}
if (Devices.empty())
{
info("No AC'97 device found.");
return -ENODEV;
}
return 0;
}
REGISTER_BUILTIN_DRIVER(ac97,
"Audio Codec '97 Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}

View File

@ -0,0 +1,210 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <cpu.hpp>
#include <pci.hpp>
#include "hda.hpp"
extern Driver::Manager *DriverManager;
extern PCI::Manager *PCIManager;
namespace Driver::HighDefinitionAudio
{
dev_t DriverID;
class HDADevice
{
private:
PCI::PCIHeader0 *Header;
bool Initialized = false;
ControllerRegisters *CTL;
uint32_t *CORB;
uint64_t *RIRB;
public:
bool IsInitialized() { return Initialized; }
size_t write(uint8_t *, size_t Size)
{
return Size;
}
int ioctl(AudioIoctl, void *)
{
return 0;
}
void OnInterruptReceived(CPU::TrapFrame *)
{
}
void Panic()
{
}
HDADevice(PCI::PCIHeader0 *_Header)
: Header(_Header),
CORB((uint32_t *)(uintptr_t)DriverManager->AllocateMemory(DriverID, 1)),
RIRB((uint64_t *)DriverManager->AllocateMemory(DriverID, 1))
{
CTL = (ControllerRegisters *)(uintptr_t)Header->BAR0;
fixme("Unimplemented HDA driver");
return;
Initialized = true;
}
~HDADevice()
{
if (!Initialized)
return;
}
};
std::unordered_map<dev_t, HDADevice *> Drivers;
int Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
{
return Drivers[Node->GetMinor()]->ioctl((AudioIoctl)Request, Argp);
}
ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
{
return Drivers[Node->GetMinor()]->write((uint8_t *)Buffer, Size);
}
const struct InodeOperations ops = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = nullptr,
.Write = Write,
.Truncate = nullptr,
.Open = nullptr,
.Close = nullptr,
.Ioctl = Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
std::list<PCI::PCIDevice> Devices;
int Entry()
{
for (auto &&dev : Devices)
{
PCI::PCIHeader0 *hdr0 = (PCI::PCIHeader0 *)dev.Header;
uint8_t type = hdr0->BAR0 & 1;
if (type == 1)
{
debug("Device %x:%x.%d BAR0 is I/O.",
hdr0->Header.VendorID,
hdr0->Header.DeviceID,
hdr0->Header.ProgIF);
continue;
}
PCIManager->InitializeDevice(dev, KernelPageTable);
HDADevice *driver = new HDADevice((PCI::PCIHeader0 *)dev.Header);
if (driver->IsInitialized())
{
dev_t ret = v0::RegisterDevice(DriverID, AUDIO_TYPE_PCM, &ops);
Drivers[ret] = driver;
}
}
if (Drivers.empty())
{
debug("No valid HDA device found.");
return -EINVAL;
}
return 0;
}
int Final()
{
for (auto &&dev : Drivers)
{
dev_t ret = dev.first;
v0::UnregisterDevice(DriverID, ret);
delete dev.second;
}
return 0;
}
int Panic()
{
for (auto &&i : Drivers)
i.second->Panic();
return 0;
}
int Probe()
{
Devices = PCIManager->FindPCIDevice(
{
0x8086, /* Intel */
0x15AD, /* VMware */
},
{
0x9D71, /* Sunrise Point-LP HD Audio */
0x2668, /* ICH6 */
0x293E, /* ICH9 */
});
if (Devices.empty())
{
trace("No HDA device found.");
return -ENODEV;
}
for (auto &&dev : Devices)
{
PCI::PCIHeader0 *PCIBaseAddress = (PCI::PCIHeader0 *)dev.Header;
uint32_t PCIBAR0 = PCIBaseAddress->BAR0;
uint8_t Type = PCIBAR0 & 1;
if (Type == 1)
{
debug("Device %x:%x.%d BAR0 is I/O.",
PCIBaseAddress->Header.VendorID,
PCIBaseAddress->Header.DeviceID,
PCIBaseAddress->Header.ProgIF);
continue;
}
}
return 0;
}
REGISTER_BUILTIN_DRIVER(hda,
"Intel High Definition Audio Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}

View File

@ -1,26 +1,24 @@
/*
This file is part of Fennix Drivers.
This file is part of Fennix Kernel.
Fennix Drivers is free software: you can redistribute it and/or
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <types.h>
#ifdef __cplusplus
struct StreamDescriptor
{
/** Control */
@ -627,9 +625,3 @@ struct __ControllerRegisters
StreamDescriptor SD[];
} __attribute__((packed));
#endif
EXTERNC int cxx_Panic();
EXTERNC int cxx_Probe();
EXTERNC int cxx_Initialize();
EXTERNC int cxx_Finalize();

View File

@ -0,0 +1,37 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
namespace Driver::FileAllocationTable
{
dev_t DriverID;
int Entry() { return 0; }
int Final() { return 0; }
int Panic() { return 0; }
int Probe() { return 0; }
REGISTER_BUILTIN_DRIVER(fat,
"File Allocation Table Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}

View File

@ -1,22 +1,21 @@
/*
This file is part of Fennix Drivers.
This file is part of Fennix Kernel.
Fennix Drivers is free software: you can redistribute it and/or
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __FENNIX_DRIVER_FAT_H__
#define __FENNIX_DRIVER_FAT_H__
#pragma once
#include <types.h>
@ -286,5 +285,3 @@ struct exFATBootRecord
/** Reserved (set to 0). */
uint8_t Reserved2[7];
} __packed;
#endif // !__FENNIX_DRIVER_FAT_H__

View File

@ -0,0 +1,43 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <types.h>
#include <cpu.hpp>
namespace Driver::AdvancedIntegratedPeripheral
{
extern uint8_t Device1ID[];
extern uint8_t Device2ID[];
void PS2KbdInterruptHandler(CPU::TrapFrame *);
int InitializeKeyboard();
int FinalizeKeyboard();
int DetectPS2Keyboard();
void PS2MouseInterruptHandler(CPU::TrapFrame *);
int InitializeMouse();
int FinalizeMouse();
int DetectPS2Mouse();
int DetectUART();
void MasterInterruptHandler(CPU::TrapFrame *);
void SlaveInterruptHandler(CPU::TrapFrame *);
int InitializeATA();
int FinalizeATA();
}

View File

@ -0,0 +1,70 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <cpu.hpp>
#include <pci.hpp>
#include <io.h>
extern Driver::Manager *DriverManager;
extern PCI::Manager *PCIManager;
namespace Driver::AdvancedIntegratedPeripheral
{
extern dev_t DriverID;
const struct InodeOperations opsATA = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = nullptr,
.Write = nullptr,
.Truncate = nullptr,
.Open = nullptr,
.Close = nullptr,
.Ioctl = nullptr,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
void MasterInterruptHandler(CPU::TrapFrame *)
{
}
void SlaveInterruptHandler(CPU::TrapFrame *)
{
}
int InitializeATA()
{
v0::RegisterInterruptHandler(DriverID, 14, (void *)MasterInterruptHandler);
v0::RegisterInterruptHandler(DriverID, 15, (void *)SlaveInterruptHandler);
return 0;
}
int FinalizeATA()
{
v0::UnregisterInterruptHandler(DriverID, 14, (void *)MasterInterruptHandler);
v0::UnregisterInterruptHandler(DriverID, 15, (void *)SlaveInterruptHandler);
return 0;
}
}

View File

@ -0,0 +1,224 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include "aip.hpp"
#include <driver.hpp>
#include <interface/input.h>
#include <interface/aip.h>
#include <cpu.hpp>
#include <io.h>
extern const unsigned short ScanCodeSet1[];
extern const unsigned short ScanCodeSet1mm[];
extern const unsigned short ScanCodeSet3[];
namespace Driver::AdvancedIntegratedPeripheral
{
extern dev_t DriverID;
uint8_t KeyboardScanCodeSet = 0;
dev_t KeyboardDevID = -1;
InputReport kir = {};
int ReportKeyboardEvent(dev_t Device, const unsigned short ScanCode, uint8_t Pressed)
{
kir.Type = INPUT_TYPE_KEYBOARD;
kir.Device = Device;
kir.Keyboard.Key = (KeyScanCodes)(ScanCode);
kir.Keyboard.Key = (KeyScanCodes)((int)kir.Keyboard.Key | (Pressed ? KEY_PRESSED : 0));
// kir.Keyboard.Key |= Pressed ? KEY_PRESSED : 0;
v0::ReportInputEvent(DriverID, &kir);
return 0;
}
bool IsE0 = false;
bool IsE1 = false;
void PS2KbdInterruptHandler(CPU::TrapFrame *)
{
uint8_t sc = inb(PS2_DATA);
if (sc == PS2_KBD_RESP_ACK ||
sc == PS2_KBD_RESP_ECHO ||
sc == PS2_KBD_RESP_RESEND)
return;
if (sc == 0xE0)
{
IsE0 = true;
return;
}
if (sc == 0xE1)
{
IsE1 = true;
return;
}
switch (KeyboardScanCodeSet)
{
case PS2_KBD_SC_SET_1:
case PS2_KBD_SC_SET_2:
{
if (IsE0)
{
IsE0 = false;
ReportKeyboardEvent(KeyboardDevID, ScanCodeSet1mm[sc], sc < 0x90);
return;
}
else
{
bool released = sc & 0x80;
uint8_t scFinal = released ? sc & 0x7F : sc;
ReportKeyboardEvent(KeyboardDevID, ScanCodeSet1[scFinal], !released);
return;
}
}
/* FIXME: https://wiki.osdev.org/PS/2_Keyboard */
// case PS2_KBD_SC_SET_2:
// {
// break;
// }
case PS2_KBD_SC_SET_3:
{
ReportKeyboardEvent(KeyboardDevID, ScanCodeSet3[sc], true);
ReportKeyboardEvent(KeyboardDevID, ScanCodeSet3[sc], false);
break;
}
default:
{
if (IsE0)
IsE0 = false;
trace("Unknown PS/2 Keyboard Scan Code Set: %#x", KeyboardScanCodeSet);
break;
}
}
}
int __fs_kb_Ioctl(struct Inode *, unsigned long, void *)
{
return 0;
}
const struct InodeOperations KbdOps = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = nullptr,
.Write = nullptr,
.Truncate = nullptr,
.Open = nullptr,
.Close = nullptr,
.Ioctl = __fs_kb_Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
int InitializeKeyboard()
{
// v0::PS2WriteData(DriverID, PS2_KBD_CMD_RESET);
// uint8_t test = v0::PS2ReadData(DriverID);
// if (test != PS2_KBD_RESP_TEST_PASSED &&
// test != PS2_KBD_RESP_ACK)
// {
// trace("PS/2 keyboard reset failed (%#x)", test);
// return -EFAULT;
// }
v0::PS2WriteData(DriverID, PS2_KBD_CMD_DEFAULTS);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to set defaults");
v0::PS2WriteData(DriverID, PS2_KBD_CMD_SCAN_CODE_SET);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to set scan code set");
/* We want Scan Code Set 1 */
v0::PS2WriteData(DriverID, PS2_KBD_SCAN_CODE_SET_2); /* It will set to 1 but with translation? */
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to set scan code set 2");
v0::PS2WriteData(DriverID, PS2_KBD_CMD_SCAN_CODE_SET);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to set scan code set");
v0::PS2WriteData(DriverID, PS2_KBD_SCAN_CODE_GET_CURRENT);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to get current scan code set");
KeyboardScanCodeSet = v0::PS2ReadAfterACK(DriverID);
trace("PS/2 Keyboard Scan Code Set: 0x%X", KeyboardScanCodeSet);
v0::PS2ClearOutputBuffer(DriverID);
v0::PS2WriteData(DriverID, PS2_KBD_CMD_ENABLE_SCANNING);
v0::RegisterInterruptHandler(DriverID, 1, (void *)PS2KbdInterruptHandler);
KeyboardDevID = v0::RegisterDevice(DriverID, INPUT_TYPE_KEYBOARD, &KbdOps);
return 0;
}
int FinalizeKeyboard()
{
v0::PS2WriteData(DriverID, PS2_KBD_CMD_DISABLE_SCANNING);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to disable scanning");
v0::UnregisterDevice(DriverID, KeyboardDevID);
return 0;
}
int DetectPS2Keyboard()
{
v0::PS2WriteData(DriverID, PS2_KBD_CMD_DISABLE_SCANNING);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to disable scanning");
v0::PS2WriteData(DriverID, PS2_KBD_CMD_IDENTIFY);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to identify");
uint8_t recByte;
int timeout = 1000000;
while (timeout--)
{
recByte = v0::PS2ReadData(DriverID);
if (recByte != PS2_ACK)
break;
}
Device1ID[0] = recByte;
timeout = 1000000;
while (timeout--)
{
recByte = v0::PS2ReadData(DriverID);
if (recByte != PS2_ACK)
break;
}
if (timeout == 0)
trace("PS/2 keyboard second byte timed out");
else
Device1ID[1] = recByte;
trace("PS2 Keyboard Device: 0x%X 0x%X", Device1ID[0], Device1ID[1]);
return 0;
}
}

View File

@ -0,0 +1,260 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <interface/aip.h>
#include <cpu.hpp>
#include <pci.hpp>
#include "aip.hpp"
extern Driver::Manager *DriverManager;
extern PCI::Manager *PCIManager;
EXTERNC void KPrint(const char *Format, ...);
namespace Driver::AdvancedIntegratedPeripheral
{
dev_t DriverID;
bool IsATAPresent()
{
outb(0x1F0 + 2, 0);
outb(0x1F0 + 3, 0);
outb(0x1F0 + 4, 0);
outb(0x1F0 + 5, 0);
outb(0x1F0 + 7, 0xEC);
if (inb(0x1F0 + 7) == 0 || inb(0x1F0 + 1) != 0)
return false;
return true;
}
bool IsKeyboard(uint8_t ID)
{
/* Common keyboard IDs */
return ID == 0xAB || ID == 0xAC || ID == 0x5D ||
ID == 0x2B || ID == 0x47 || ID == 0x60;
}
bool IsMouse(uint8_t ID)
{
/* Common mouse IDs */
return ID == 0x00 || ID == 0x03 || ID == 0x04;
}
const char *GetPS2DeviceName(uint8_t ID, uint8_t SubID)
{
switch (ID)
{
case 0x00:
return "Standard PS/2 Mouse";
case 0x03:
return "Mouse with scroll wheel";
case 0x04:
return "Mouse 5 buttons";
case 0xAB:
{
switch (SubID)
{
case 0x83: /* Normal */
case 0x41: /* Translated */
case 0xC1: /* Normal + Translated */
return "Standard PS/2 Keyboard";
case 0x84:
case 0x54:
return "IBM Thinkpad/Spacesaver Keyboard";
case 0x85:
return "NCD N-97/122-Key Host Connect(ed) Keyboard";
case 0x86:
return "122-Key Keyboard";
case 0x90:
return "Japanese \"G\" Keyboard";
case 0x91:
return "Japanese \"P\" Keyboard";
case 0x92:
return "Japanese \"A\" Keyboard";
default:
return "Unknown PS/2 Keyboard";
}
}
case 0xAC:
{
switch (SubID)
{
case 0xA1:
return "NCD Sun Keyboard";
default:
return "Unknown NCD Sun Keyboard";
}
}
case 0x5D:
case 0x2B:
return "Trust Keyboard";
case 0x47:
case 0x60:
return "NMB SGI Keyboard";
default:
return "Unknown PS/2 Device";
}
}
uint8_t Device1ID[2] = {0x00, 0x00};
uint8_t Device2ID[2] = {0x00, 0x00};
bool DualChannel = false;
bool ATAPresent = false;
int Entry()
{
v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_1);
v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_2);
v0::PS2ClearOutputBuffer(DriverID);
v0::PS2WriteCommand(DriverID, PS2_CMD_READ_CONFIG);
PS2_CONFIGURATION cfg = {.Raw = v0::PS2ReadData(DriverID)};
DualChannel = cfg.Port2Clock;
if (DualChannel)
trace("Dual channel PS/2 controller detected");
cfg.Port1Interrupt = 1;
cfg.Port2Interrupt = 1;
cfg.Port1Translation = 1;
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_CONFIG);
v0::PS2WriteData(DriverID, cfg.Raw);
v0::PS2WriteCommand(DriverID, PS2_CMD_TEST_CONTROLLER);
uint8_t test = v0::PS2ReadData(DriverID);
if (test != PS2_TEST_PASSED)
{
trace("PS/2 controller self test failed (%#x)", test);
return -EFAULT;
}
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_CONFIG);
v0::PS2WriteData(DriverID, cfg.Raw);
// bool port2avail = false;
// if (DualChannel)
// {
// v0::PS2WriteCommand(DriverID, PS2_CMD_ENABLE_PORT_1);
// v0::PS2WriteCommand(DriverID, PS2_CMD_READ_CONFIG);
// cfg.Raw = v0::PS2ReadData(DriverID);
// port2avail = cfg.Port2Clock;
// v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_1);
// }
v0::PS2WriteCommand(DriverID, PS2_CMD_TEST_PORT_1);
test = v0::PS2ReadData(DriverID);
if (test != 0x00)
{
trace("PS/2 Port 1 self test failed (%#x)", test);
return -EFAULT;
}
if (DualChannel)
{
v0::PS2WriteCommand(DriverID, PS2_CMD_TEST_PORT_2);
test = v0::PS2ReadData(DriverID);
if (test != 0x00)
{
trace("PS/2 Port 2 self test failed (%#x)", test);
return -EFAULT;
}
}
v0::PS2WriteCommand(DriverID, PS2_CMD_ENABLE_PORT_1);
if (DualChannel)
v0::PS2WriteCommand(DriverID, PS2_CMD_ENABLE_PORT_2);
int errK = InitializeKeyboard();
int errM = 0;
if (DualChannel)
errM = InitializeMouse();
ATAPresent = IsATAPresent();
if (errK != 0 && errM != 0 && ATAPresent == false)
return -ENODEV;
return 0;
}
int Final()
{
FinalizeKeyboard();
FinalizeMouse();
v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_1);
v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_2);
return 0;
}
int Panic()
{
v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_1);
v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_2);
return 0;
}
void __intStub() {}
int Probe()
{
v0::RegisterInterruptHandler(DriverID, 1, (void *)__intStub);
v0::RegisterInterruptHandler(DriverID, 12, (void *)__intStub);
int kbd = DetectPS2Keyboard();
int mouse = DetectPS2Mouse();
int uart = DetectUART();
v0::UnregisterAllInterruptHandlers(DriverID, (void *)__intStub);
if (kbd != 0 && mouse != 0 && uart != 0)
return -ENODEV;
if (kbd == 0)
{
if (!IsKeyboard(Device1ID[0]))
{
trace("PS/2 Port 1 is not a keyboard");
// return -EINVAL;
}
}
if (mouse == 0)
{
if (!IsMouse(Device2ID[0]))
{
trace("PS/2 Port 2 is not a mouse");
// return -EINVAL;
}
}
KPrint("PS/2 Port 1: %s (0x%X 0x%X)",
GetPS2DeviceName(Device1ID[0], Device1ID[1]),
Device1ID[0], Device1ID[1]);
KPrint("PS/2 Port 2: %s (0x%X 0x%X)",
GetPS2DeviceName(Device2ID[0], Device2ID[1]),
Device2ID[0], Device2ID[1]);
return 0;
}
REGISTER_BUILTIN_DRIVER(aip,
"Advanced Integrated Peripheral Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}

View File

@ -0,0 +1,263 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include "aip.hpp"
#include <driver.hpp>
#include <interface/aip.h>
#include <interface/input.h>
namespace Driver::AdvancedIntegratedPeripheral
{
extern dev_t DriverID;
dev_t MouseDevID = -1;
bool PacketReady = false;
bool FourPackets = false;
bool MouseButton45 = false;
uint8_t Cycle = 0;
PS2_MOUSE_PACKET Packet = {};
InputReport mir = {};
void PS2MouseInterruptHandler(CPU::TrapFrame *)
{
uint8_t data = v0::PS2ReadData(DriverID);
if (data == PS2_MOUSE_RESP_ACK ||
data == PS2_MOUSE_RESP_RESEND)
return;
if (!PacketReady)
{
switch (Cycle)
{
case 0:
{
if ((data & 0b00001000 /* Always 1 */) == 0)
return;
Packet.Base.Raw = data;
Cycle++;
break;
}
case 1:
{
Packet.XMovement = data;
Cycle++;
break;
}
case 2:
{
Packet.YMovement = data;
if (FourPackets)
Cycle++;
else
{
Cycle = 0;
PacketReady = true;
}
break;
}
case 3:
{
Packet.ZMovement.Raw = data;
Cycle = 0;
PacketReady = true;
break;
}
default:
break;
}
return;
}
/* https://stackoverflow.com/a/3208376/9352057 */
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte) \
((byte) & 0x80 ? '1' : '0'), \
((byte) & 0x40 ? '1' : '0'), \
((byte) & 0x20 ? '1' : '0'), \
((byte) & 0x10 ? '1' : '0'), \
((byte) & 0x08 ? '1' : '0'), \
((byte) & 0x04 ? '1' : '0'), \
((byte) & 0x02 ? '1' : '0'), \
((byte) & 0x01 ? '1' : '0')
debug("PS/2 Mouse Packet: [" BYTE_TO_BINARY_PATTERN ":" BYTE_TO_BINARY_PATTERN ":" BYTE_TO_BINARY_PATTERN ":" BYTE_TO_BINARY_PATTERN "] LB:%d RB:%d MB:%d A1:%d XS:%d YS:%d XO:%d YO:%d | X:%03d Y:%03d | Z:%d B4:%d B5:%d A0:%d A0:%d",
BYTE_TO_BINARY(Packet.Base.Raw),
BYTE_TO_BINARY(Packet.XMovement),
BYTE_TO_BINARY(Packet.YMovement),
BYTE_TO_BINARY(Packet.ZMovement.Raw),
Packet.Base.LeftButton, Packet.Base.RightButton, Packet.Base.MiddleButton,
Packet.Base.Always1,
Packet.Base.XSign, Packet.Base.YSign,
Packet.Base.XOverflow, Packet.Base.YOverflow,
Packet.XMovement, Packet.YMovement,
Packet.ZMovement.Z, Packet.ZMovement.Button4, Packet.ZMovement.Button5,
Packet.ZMovement.Always0, Packet.ZMovement.Always0_2);
int X, Y;
X = Packet.XMovement - (Packet.Base.XSign ? 256 : 0);
Y = Packet.YMovement - (Packet.Base.YSign ? 256 : 0);
if (Packet.Base.XOverflow)
X = 0;
if (Packet.Base.YOverflow)
Y = 0;
mir.Type = INPUT_TYPE_MOUSE;
mir.Device = MouseDevID;
mir.Mouse.LeftButton = Packet.Base.LeftButton;
mir.Mouse.RightButton = Packet.Base.RightButton;
mir.Mouse.MiddleButton = Packet.Base.MiddleButton;
mir.Mouse.Button4 = Packet.ZMovement.Button4;
mir.Mouse.Button5 = Packet.ZMovement.Button5;
mir.Mouse.X = X;
mir.Mouse.Y = -Y;
mir.Mouse.Z = Packet.ZMovement.Z;
v0::ReportInputEvent(DriverID, &mir);
PacketReady = false;
}
void MouseSampleRate(uint8_t SampleRate)
{
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_SET_SAMPLE_RATE);
v0::PS2ReadData(DriverID);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, SampleRate);
v0::PS2ReadData(DriverID);
}
int __fs_ms_Ioctl(struct Inode *, unsigned long, void *)
{
return 0;
}
const struct InodeOperations MouseOps = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = nullptr,
.Write = nullptr,
.Truncate = nullptr,
.Open = nullptr,
.Close = nullptr,
.Ioctl = __fs_ms_Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
int InitializeMouse()
{
v0::PS2WriteData(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_RESET);
uint8_t test = v0::PS2ReadData(DriverID);
if (test != PS2_MOUSE_RESP_TEST_PASSED &&
test != PS2_MOUSE_RESP_ACK)
{
trace("PS/2 mouse reset failed! (%#x)", test);
return -EFAULT;
}
v0::RegisterInterruptHandler(DriverID, 12, (void *)PS2MouseInterruptHandler);
MouseDevID = v0::RegisterDevice(DriverID, INPUT_TYPE_MOUSE, &MouseOps);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_SET_DEFAULTS);
v0::PS2ReadData(DriverID);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_ENABLE_DATA_REPORTING);
MouseSampleRate(200);
MouseSampleRate(100);
MouseSampleRate(80);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_READ_ID);
uint8_t Device2ID = v0::PS2ReadData(DriverID);
trace("PS/2 Mouse ID: %#x", Device2ID);
MouseSampleRate(200);
MouseSampleRate(200);
MouseSampleRate(80);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_READ_ID);
Device2ID = v0::PS2ReadData(DriverID);
trace("PS/2 Mouse ID: %#x", Device2ID);
if (Device2ID >= 3 && Device2ID <= 4)
FourPackets = true;
if (Device2ID == 4)
MouseButton45 = true;
return 0;
}
int FinalizeMouse()
{
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
v0::UnregisterDevice(DriverID, MouseDevID);
return 0;
}
int DetectPS2Mouse()
{
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 mouse failed to disable data reporting!");
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_READ_ID);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 mouse failed to read ID!");
uint8_t recByte;
int timeout = 1000000;
while (timeout--)
{
recByte = v0::PS2ReadData(DriverID);
if (recByte != PS2_ACK)
break;
}
Device2ID[0] = recByte;
timeout = 1000000;
while (timeout--)
{
recByte = v0::PS2ReadData(DriverID);
if (recByte != PS2_ACK)
break;
}
Device2ID[1] = recByte;
trace("PS2 Mouse Device: 0x%X 0x%X", Device2ID[0], Device2ID[1]);
return 0;
}
}

View File

@ -1,31 +1,22 @@
/*
This file is part of Fennix Drivers.
This file is part of Fennix Kernel.
Fennix Drivers is free software: you can redistribute it and/or
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "aip.h"
#include <driver.h>
#include <errno.h>
#include <fs.h>
#include <input.h>
#include <base.h>
#include <io.h>
uint8_t KeyboardScanCodeSet = 0;
dev_t KeyboardDevID = -1;
#include <interface/driver.h>
#include <interface/input.h>
const unsigned short ScanCodeSet1[] =
{KEY_NULL, KEY_ESCAPE,
@ -161,175 +152,3 @@ const unsigned short ScanCodeSet3[] = {
[0x44] = KEY_O,
[0x4B] = KEY_L,
[0x4D] = KEY_P};
InputReport kir = {0};
int ReportKeyboardEvent(dev_t Device, KeyScanCodes ScanCode, uint8_t Pressed)
{
kir.Type = INPUT_TYPE_KEYBOARD;
kir.Device = Device;
kir.Keyboard.Key = ScanCode;
kir.Keyboard.Key |= Pressed ? KEY_PRESSED : 0;
ReportInputEvent(&kir);
return 0;
}
bool IsE0 = false;
bool IsE1 = false;
void PS2KbdInterruptHandler(TrapFrame *)
{
uint8_t sc = inb(PS2_DATA);
if (sc == PS2_KBD_RESP_ACK ||
sc == PS2_KBD_RESP_ECHO ||
sc == PS2_KBD_RESP_RESEND)
return;
if (sc == 0xE0)
{
IsE0 = true;
return;
}
if (sc == 0xE1)
{
IsE1 = true;
return;
}
switch (KeyboardScanCodeSet)
{
case PS2_KBD_SC_SET_1:
case PS2_KBD_SC_SET_2:
{
if (IsE0)
{
IsE0 = false;
ReportKeyboardEvent(KeyboardDevID, ScanCodeSet1mm[sc], sc < 0x90);
return;
}
else
{
bool released = sc & 0x80;
uint8_t scFinal = released ? sc & 0x7F : sc;
ReportKeyboardEvent(KeyboardDevID, ScanCodeSet1[scFinal], !released);
return;
}
}
/* FIXME: https://wiki.osdev.org/PS/2_Keyboard */
// case PS2_KBD_SC_SET_2:
// {
// break;
// }
case PS2_KBD_SC_SET_3:
{
ReportKeyboardEvent(KeyboardDevID, ScanCodeSet3[sc], true);
ReportKeyboardEvent(KeyboardDevID, ScanCodeSet3[sc], false);
break;
}
default:
{
if (IsE0)
IsE0 = false;
KernelLog("Unknown PS/2 Keyboard Scan Code Set: %#x", KeyboardScanCodeSet);
break;
}
}
}
int __fs_kb_Ioctl(struct Inode *, unsigned long, void *)
{
return 0;
}
const struct InodeOperations KbdOps = {
.Ioctl = __fs_kb_Ioctl,
};
int InitializeKeyboard()
{
// PS2WriteData(PS2_KBD_CMD_RESET);
// uint8_t test = PS2ReadData();
// if (test != PS2_KBD_RESP_TEST_PASSED &&
// test != PS2_KBD_RESP_ACK)
// {
// KernelLog("PS/2 keyboard reset failed (%#x)", test);
// return -EFAULT;
// }
PS2WriteData(PS2_KBD_CMD_DEFAULTS);
if (PS2ACKTimeout() != 0)
KernelLog("PS/2 keyboard failed to set defaults");
PS2WriteData(PS2_KBD_CMD_SCAN_CODE_SET);
if (PS2ACKTimeout() != 0)
KernelLog("PS/2 keyboard failed to set scan code set");
/* We want Scan Code Set 1 */
PS2WriteData(PS2_KBD_SCAN_CODE_SET_2); /* It will set to 1 but with translation? */
if (PS2ACKTimeout() != 0)
KernelLog("PS/2 keyboard failed to set scan code set 2");
PS2WriteData(PS2_KBD_CMD_SCAN_CODE_SET);
if (PS2ACKTimeout() != 0)
KernelLog("PS/2 keyboard failed to set scan code set");
PS2WriteData(PS2_KBD_SCAN_CODE_GET_CURRENT);
if (PS2ACKTimeout() != 0)
KernelLog("PS/2 keyboard failed to get current scan code set");
KeyboardScanCodeSet = PS2ReadAfterACK();
KernelLog("PS/2 Keyboard Scan Code Set: 0x%X", KeyboardScanCodeSet);
PS2ClearOutputBuffer();
PS2WriteData(PS2_KBD_CMD_ENABLE_SCANNING);
RegisterInterruptHandler(1, PS2KbdInterruptHandler);
KeyboardDevID = RegisterDevice(INPUT_TYPE_KEYBOARD, &KbdOps);
return 0;
}
int FinalizeKeyboard()
{
PS2WriteData(PS2_KBD_CMD_DISABLE_SCANNING);
if (PS2ACKTimeout() != 0)
KernelLog("PS/2 keyboard failed to disable scanning");
UnregisterDevice(KeyboardDevID);
return 0;
}
int DetectPS2Keyboard()
{
PS2WriteData(PS2_KBD_CMD_DISABLE_SCANNING);
if (PS2ACKTimeout() != 0)
KernelLog("PS/2 keyboard failed to disable scanning");
PS2WriteData(PS2_KBD_CMD_IDENTIFY);
if (PS2ACKTimeout() != 0)
KernelLog("PS/2 keyboard failed to identify");
uint8_t recByte;
int timeout = 1000000;
while (timeout--)
{
recByte = PS2ReadData();
if (recByte != PS2_ACK)
break;
}
Device1ID[0] = recByte;
timeout = 1000000;
while (timeout--)
{
recByte = PS2ReadData();
if (recByte != PS2_ACK)
break;
}
if (timeout == 0)
KernelLog("PS/2 keyboard second byte timed out");
else
Device1ID[1] = recByte;
KernelLog("PS2 Keyboard Device: 0x%X 0x%X", Device1ID[0], Device1ID[1]);
return 0;
}

View File

@ -0,0 +1,647 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <io.h>
#include "aip.hpp"
namespace Driver::AdvancedIntegratedPeripheral
{
extern dev_t DriverID;
#define SERIAL_ENABLE_DLAB 0x80
#define SERIAL_BUFFER_EMPTY 0x20
enum Ports
{
COM1 = 0x3F8,
COM2 = 0x2F8,
COM3 = 0x3E8,
COM4 = 0x2E8,
COM5 = 0x5F8,
COM6 = 0x4F8,
COM7 = 0x5E8,
COM8 = 0x4E8,
LPT1 = 0x378,
LPT2 = 0x278,
LPT3 = 0x3BC
};
enum SerialSpeed
{
RATE_50_HI = 0x09,
RATE_50_LO = 0x00,
RATE_300_HI = 0x01,
RATE_300_LO = 0x80,
RATE_600_HI = 0x00,
RATE_600_LO = 0xC0,
RATE_2400_HI = 0x00,
RATE_2400_LO = 0x30,
RATE_4800_HI = 0x00,
RATE_4800_LO = 0x18,
RATE_9600_HI = 0x00,
RATE_9600_LO = 0x0C,
RATE_19200_HI = 0x00,
RATE_19200_LO = 0x06,
RATE_38400_HI = 0x00,
RATE_38400_LO = 0x03,
RATE_57600_HI = 0x00,
RATE_57600_LO = 0x02,
RATE_115200_HI = 0x00,
RATE_115200_LO = 0x01
};
/*
. Table of Registers .
/---------------------------------------------------------------------\
| Base Address | DLAB | R/W | Abr | Register Name |
|---------------------------------------------------------------------|
| +0 | =0 | W | - | Transmitter Holding Buffer |
| | =0 | R | - | Receiver Buffer |
| | =1 | R/W | - | Divisor Latch Low Byte |
| +1 | =0 | R/W | IER | Interrupt Enable Register |
| | =1 | R/W | - | Divisor Latch High Byte |
| +2 | - | R | IIR | Interrupt Identification Register |
| | - | W | FCR | FIFO Control Register |
| +3 | - | R/W | LCR | Line Control Register |
| +4 | - | R/W | MCR | Modem Control Register |
| +5 | - | R | LSR | Line Status Register |
| +6 | - | R | MSR | Modem Status Register |
| +7 | - | R/W | - | Scratch Register |
\---------------------------------------------------------------------/
Source:
Interfacing the Serial / RS232 Port V5.0
Table 5 : Table of Registers
*/
/** Interrupt Enable Register */
typedef union
{
struct
{
/* Enable Received Data Available Interrupt */
uint8_t InterruptOnReceive : 1;
/* Enable Transmitter Holding Register Empty Interrupt */
uint8_t InterruptOnTransmitter : 1;
/* Enable Receiver Line Status Interrupt */
uint8_t LineStatusInterrupt : 1;
/* Enable Modem Status Interrupt */
uint8_t ModemStatusInterrupt : 1;
/* Enables Sleep Mode (16750) */
uint8_t SleepMode : 1;
/* Enables Low Power Mode (16750) */
uint8_t LowPowerMode : 1;
/* Reserved */
uint8_t __reserved : 2;
};
uint8_t raw;
} IER;
/** Interrupt Identification Register */
typedef union
{
struct
{
/* Interrupt pending */
uint8_t InterruptPending : 1;
/**
* Interrupt Status
*
* 00b = Modem Status Interrupt
* 01b = Transmitter Holding Register Empty Interrupt
* 10b = Received Data Available Interrupt
* 11b = Receiver Line Status Interrupt
*/
uint8_t InterruptStatus : 2;
/**
* 16550 Time-out Interrupt Pending
*
* @note Reserved on 8250, 16450
*/
uint8_t TimeOutIP : 1;
/** Reserved */
uint8_t __reserved : 1;
/** 64 Byte Fifo Enabled (16750 only) */
uint8_t FIFO64 : 1;
/**
* Enable FIFO
*
* 00b = No FIFO
* 01b = FIFO Enabled but Unusable
* 11b = FIFO Enabled
*/
uint8_t FIFO : 2;
};
uint8_t raw;
} IIR;
/** First In / First Out Control Register */
typedef union
{
struct
{
/** Enable FIFO's */
uint8_t FIFO : 1;
/** Clear Receive FIFO */
uint8_t ClearRX : 1;
/** Clear Transmit FIFO */
uint8_t ClearTX : 1;
/** DMA Mode Select.
*
* Change status of RXRDY & TXRDY pins from mode 1 to mode 2.
*/
uint8_t DMAMode : 1;
/** Reserved */
uint8_t __reserved : 1;
/** Enable 64 Byte FIFO (16750 only) */
uint8_t FIFO64 : 1;
/** Interrupt Trigger Level
*
* 00b = 1 Byte
* 01b = 4 Bytes
* 10b = 8 Bytes
* 11b = 14 Bytes
*/
uint8_t TriggerLevel : 2;
};
uint8_t raw;
} FCR;
/** Line Control Register */
typedef union
{
struct
{
/** Word Length
*
* 00b = 5 bits
* 01b = 6 bits
* 10b = 7 bits
* 11b = 8 bits
*/
uint8_t WordLength : 2;
/** Length of Stop Bit
*
* 0b = One Stop Bit
* 1b = 2 Stop bits for words of length 6,7 or 8 bits or 1.5 Stop Bits for Word lengths of 5 bits.
*/
uint8_t StopBit : 1;
/** Parity Select
*
* 0b = No Parity
* 001b = Odd Parity
* 011b = Even Parity
* 101b = High Parity (Sticky)
* 111b = Low Parity (Sticky)
*/
uint8_t Parity : 3;
/** Set Break Enable */
uint8_t SetBreak : 1;
/**
* Divisor Latch Access
*
* 0b = Access to Receiver buffer, Transmitter buffer & Interrupt Enable Register
* 1b = Divisor Latch Access Bit
*/
uint8_t DLAB : 1;
};
uint8_t raw;
} LCR;
/** Modem Control Register */
typedef union
{
struct
{
/** Force Data Terminal Ready */
uint8_t DataTerminalReady : 1;
/** Force Request to Send */
uint8_t RequestToSend : 1;
/** Auxiliary Output 1 */
uint8_t Out1 : 1;
/** Auxiliary Output 2 */
uint8_t Out2 : 1;
/** Loopback Mode */
uint8_t Loopback : 1;
/** Autoflow Control Enabled (16750 only) */
uint8_t Autoflow : 1;
/** Reserved */
uint8_t __reserved : 2;
};
uint8_t raw;
} MCR;
/** Line Status Register */
typedef union
{
struct
{
/** Data Ready */
uint8_t DataReady : 1;
/** Overrun Error */
uint8_t OverrunError : 1;
/** Parity Error */
uint8_t ParityError : 1;
/** Framing Error */
uint8_t FramingError : 1;
/** Break Interrupt */
uint8_t BreakInterrupt : 1;
/** Empty Transmitter Holding Register */
uint8_t EmptyTransmitterHolding : 1;
/** Empty Data Holding Registers */
uint8_t EmptyDataHolding : 1;
/** Error in Received FIFO */
uint8_t ErrorReceivedFIFO : 1;
};
uint8_t raw;
} LSR;
/** Modem Status Register */
typedef union
{
struct
{
/** Delta Clear to Send */
uint8_t DeltaClearToSend : 1;
/** Delta Data Set Ready */
uint8_t DeltaDataSetReady : 1;
/** Trailing Edge Ring Indicator */
uint8_t TrailingEdgeRingIndicator : 1;
/** Delta Data Carrier Detect */
uint8_t DeltaDataCarrierDetect : 1;
/** Clear To Send */
uint8_t ClearToSend : 1;
/** Data Set Ready */
uint8_t DataSetReady : 1;
/** Ring Indicator */
uint8_t RingIndicator : 1;
/** Carrier Detect */
uint8_t CarrierDetect : 1;
};
uint8_t raw;
} MSR;
union UARTs
{
struct
{
uint8_t com1 : 1;
uint8_t com2 : 1;
uint8_t com3 : 1;
uint8_t com4 : 1;
uint8_t com5 : 1;
uint8_t com6 : 1;
uint8_t com7 : 1;
uint8_t com8 : 1;
uint8_t lpt1 : 1;
uint8_t lpt2 : 1;
uint8_t lpt3 : 1;
uint8_t __reserved : 5;
};
uint16_t raw;
} uart;
bool IsDataReady(uint16_t Port)
{
LSR lsr;
lsr.raw = inb(Port + 5);
return lsr.DataReady;
}
bool IsTransmitEmpty(uint16_t Port)
{
LSR lsr;
lsr.raw = inb(Port + 5);
return lsr.EmptyTransmitterHolding;
}
char ReadSerial(uint16_t Port)
{
while (!IsDataReady(Port))
v0::Yield(DriverID);
return inb(Port);
}
void WriteSerial(uint16_t Port, char Character)
{
while (!IsTransmitEmpty(Port))
v0::Yield(DriverID);
outb(Port, Character);
}
void ReportSerialReceived(uint8_t Data)
{
debug("%c", Data);
}
void UartCOM24(CPU::TrapFrame *)
{
LSR lsr2, lsr4;
do
{
lsr2.raw = inb(COM2 + 5);
if (lsr2.DataReady)
ReportSerialReceived(inb(COM2));
lsr4.raw = inb(COM4 + 5);
if (lsr4.DataReady)
ReportSerialReceived(inb(COM4));
} while (lsr2.DataReady || lsr4.DataReady);
}
void UartCOM13(CPU::TrapFrame *)
{
LSR lsr1, lsr3;
do
{
lsr1.raw = inb(COM1 + 5);
if (lsr1.DataReady)
ReportSerialReceived(inb(COM1));
lsr3.raw = inb(COM3 + 5);
if (lsr3.DataReady)
ReportSerialReceived(inb(COM3));
} while (lsr1.DataReady || lsr3.DataReady);
}
bool InitializePort(uint16_t Port)
{
v0::CriticalState cs = v0::EnterCriticalSection(DriverID);
LCR lcr = {};
IER ier = {};
FCR fcr = {};
MCR mcr = {};
outb(Port + 3, lcr.raw);
outb(Port + 1, ier.raw);
lcr.DLAB = 1;
outb(Port + 3, lcr.raw);
outb(Port + 0, RATE_115200_LO);
outb(Port + 1, RATE_115200_HI);
lcr.DLAB = 0;
lcr.WordLength = 0b11;
outb(Port + 3, lcr.raw);
fcr.FIFO = 1;
fcr.ClearRX = 1;
fcr.ClearTX = 1;
fcr.TriggerLevel = 0b11;
outb(Port + 2, fcr.raw);
mcr.DataTerminalReady = 1;
mcr.RequestToSend = 1;
mcr.Out2 = 1;
mcr.Loopback = 1;
outb(Port + 4, mcr.raw);
/* Test the serial port */
outb(Port + 0, 0x48);
uint8_t result = inb(Port + 0);
if (result != 0x48)
{
/* FIXME: DETECT BAUD RATE
Do multiple test to check if the output is garbage.
If so, reduce the baud rate until it works. */
v0::LeaveCriticalSection(DriverID, cs);
trace("Port %#X test failed!", Port);
return false;
}
/* Set normal operation mode */
mcr.DataTerminalReady = 1;
mcr.RequestToSend = 1;
mcr.Out1 = 1;
mcr.Out2 = 1;
mcr.Loopback = 0;
outb(Port + 4, mcr.raw);
/* Enable interrupts on receive */
ier.InterruptOnReceive = 1;
outb(Port + 1, ier.raw);
v0::RegisterInterruptHandler(DriverID, 3, (void *)UartCOM24);
v0::RegisterInterruptHandler(DriverID, 4, (void *)UartCOM13);
v0::LeaveCriticalSection(DriverID, cs);
trace("Port %#X initialized", Port);
return true;
}
int DetectUART()
{
uart.com1 = inb(COM1) != 0xFF ? true : false;
uart.com2 = inb(COM2) != 0xFF ? true : false;
uart.com3 = inb(COM3) != 0xFF ? true : false;
uart.com4 = inb(COM4) != 0xFF ? true : false;
uart.com5 = inb(COM5) != 0xFF ? true : false;
uart.com6 = inb(COM6) != 0xFF ? true : false;
uart.com7 = inb(COM7) != 0xFF ? true : false;
uart.com8 = inb(COM8) != 0xFF ? true : false;
uart.lpt1 = inb(LPT1) != 0xFF ? true : false;
uart.lpt2 = inb(LPT2) != 0xFF ? true : false;
uart.lpt3 = inb(LPT3) != 0xFF ? true : false;
if (uart.com1 == true)
if (InitializePort(COM1) == false)
uart.com1 = false;
if (uart.com2 == true)
if (InitializePort(COM2) == false)
uart.com1 = false;
if (uart.com3 == true)
if (InitializePort(COM3) == false)
uart.com1 = false;
if (uart.com4 == true)
if (InitializePort(COM4) == false)
uart.com1 = false;
if (uart.com5 == true)
if (InitializePort(COM5) == false)
uart.com1 = false;
if (uart.com6 == true)
if (InitializePort(COM6) == false)
uart.com1 = false;
if (uart.com7 == true)
if (InitializePort(COM7) == false)
uart.com1 = false;
if (uart.com8 == true)
if (InitializePort(COM8) == false)
uart.com1 = false;
if (uart.lpt1 == true)
trace("LPT1 is present");
if (uart.lpt2 == true)
trace("LPT2 is present");
if (uart.lpt3 == true)
trace("LPT3 is present");
return 0;
}
// static int once = 0;
// static uint8_t com4 = 0xFF;
// if (!once++)
// com4 = inb(0x2E8);
// if (com4 == 0xFF)
// CPU::Halt(true);
// char UserInputBuffer[256]{'\0'};
// int BackSpaceLimit = 0;
// while (true)
// {
// while ((inb(0x2E8 + 5) & 1) == 0)
// CPU::Pause();
// char key = inb(0x2E8);
// // debug("key: %d", key);
// if (key == '\x7f') /* Backspace (DEL) */
// {
// if (BackSpaceLimit <= 0)
// continue;
// char keyBuf[5] = {'\b', '\x1b', '[', 'K', '\0'};
// ExPrint(keyBuf);
// backspace(UserInputBuffer);
// BackSpaceLimit--;
// continue;
// }
// else if (key == '\x0d') /* Enter (CR) */
// {
// UserInput(UserInputBuffer);
// BackSpaceLimit = 0;
// UserInputBuffer[0] = '\0';
// continue;
// }
// else if (key == '\x1b') /* Escape */
// {
// char tmp[16]{'\0'};
// append(tmp, key);
// while ((inb(0x2E8 + 5) & 1) == 0)
// CPU::Pause();
// char key = inb(0x2E8);
// append(tmp, key);
// if (key == '[')
// {
// // 27 91
// // < 68
// // > 67
// // down 66
// // up 65
// while ((inb(0x2E8 + 5) & 1) == 0)
// CPU::Pause();
// key = inb(0x2E8);
// append(tmp, key);
// switch (key)
// {
// case 'A':
// key = KEY_D_UP;
// break;
// case 'B':
// key = KEY_D_DOWN;
// break;
// case 'C':
// key = KEY_D_RIGHT;
// break;
// case 'D':
// key = KEY_D_LEFT;
// break;
// default:
// {
// for (size_t i = 0; i < strlen(tmp); i++)
// {
// if ((int)sizeof(UserInputBuffer) <= BackSpaceLimit)
// continue;
// append(UserInputBuffer, tmp[i]);
// BackSpaceLimit++;
// char keyBuf[2] = {(char)tmp[i], '\0'};
// ExPrint(keyBuf);
// }
// continue;
// }
// }
// ArrowInput(key);
// continue;
// }
// }
// if ((int)sizeof(UserInputBuffer) <= BackSpaceLimit)
// continue;
// append(UserInputBuffer, key);
// BackSpaceLimit++;
// char keyBuf[2] = {(char)key, '\0'};
// ExPrint(keyBuf);
// }
}

View File

@ -0,0 +1,164 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <rand.hpp>
extern Driver::Manager *DriverManager;
namespace Driver::MemoryDevices
{
dev_t DriverID;
struct
{
dev_t null;
dev_t zero;
dev_t random;
dev_t urandom;
dev_t mem;
} ids;
int Open(struct Inode *Node, int Flags, mode_t Mode) { return -ENOENT; }
int Close(struct Inode *Node) { return -ENOSYS; }
int Ioctl(struct Inode *Node, unsigned long Request, void *Argp) { return -ENOSYS; }
ssize_t Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
{
dev_t min = Node->GetMinor();
if (min == ids.null)
return 0;
else if (min == ids.zero)
{
if (Size <= 0)
return 0;
memset(Buffer, 0, Size);
return Size;
}
else if (min == ids.random || min == ids.urandom)
{
if (Size <= 0)
return 0;
if (Size < sizeof(uint64_t))
{
uint8_t *buf = (uint8_t *)Buffer;
for (size_t i = 0; i < Size; i++)
buf[i] = (uint8_t)(Random::rand16() & 0xFF);
return Size;
}
uint64_t *buf = (uint64_t *)Buffer;
for (size_t i = 0; i < Size / sizeof(uint64_t); i++)
buf[i] = Random::rand64();
return Size;
}
else if (min == ids.mem)
{
stub;
return 0;
}
return -ENODEV;
}
ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
{
dev_t min = Node->GetMinor();
if (min == ids.null)
return Size;
else if (min == ids.zero)
return Size;
else if (min == ids.random || min == ids.urandom)
return Size;
else if (min == ids.mem)
return Size;
return -ENODEV;
}
off_t Seek(struct Inode *Node, off_t Offset) { return -ENOSYS; }
int Stat(struct Inode *Node, struct kstat *Stat) { return -ENOSYS; }
const struct InodeOperations ops = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = Read,
.Write = Write,
.Truncate = nullptr,
.Open = Open,
.Close = Close,
.Ioctl = Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = Seek,
.Stat = Stat,
};
int Entry()
{
mode_t mode = 0;
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH |
S_IFCHR;
ids.null = DriverManager->CreateDeviceFile(DriverID, "null", mode, &ops);
ids.zero = DriverManager->CreateDeviceFile(DriverID, "zero", mode, &ops);
ids.random = DriverManager->CreateDeviceFile(DriverID, "random", mode, &ops);
ids.urandom = DriverManager->CreateDeviceFile(DriverID, "urandom", mode, &ops);
/* c rw- r-- --- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP |
S_IFCHR;
ids.mem = DriverManager->CreateDeviceFile(DriverID, "mem", mode, &ops);
return 0;
}
int Final()
{
DriverManager->UnregisterDevice(DriverID, ids.null);
DriverManager->UnregisterDevice(DriverID, ids.zero);
DriverManager->UnregisterDevice(DriverID, ids.random);
DriverManager->UnregisterDevice(DriverID, ids.urandom);
DriverManager->UnregisterDevice(DriverID, ids.mem);
return 0;
}
int Panic() { return 0; }
int Probe() { return 0; }
REGISTER_BUILTIN_DRIVER(mem,
"Memory Devices Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}

View File

@ -0,0 +1,192 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <kcon.hpp>
#include <task.hpp>
#include <smp.hpp>
extern Driver::Manager *DriverManager;
namespace Driver::TeleTypeDevices
{
dev_t DriverID;
TTY::PTMXDevice *ptmx = nullptr;
struct
{
dev_t kcon;
dev_t tty;
dev_t ptmx;
} ids;
int Open(struct Inode *Node, int Flags, mode_t Mode)
{
dev_t min = Node->GetMinor();
if (min == ids.kcon)
return KernelConsole::CurrentTerminal.load()->Open(Flags, Mode);
else if (min == ids.tty)
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Open(Flags, Mode);
}
else if (min == ids.ptmx)
return ptmx->Open();
return -ENODEV;
}
int Close(struct Inode *Node)
{
dev_t min = Node->GetMinor();
if (min == ids.kcon)
return KernelConsole::CurrentTerminal.load()->Close();
else if (min == ids.tty)
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Close();
}
else if (min == ids.ptmx)
return ptmx->Close();
return -ENODEV;
}
int Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
{
dev_t min = Node->GetMinor();
if (min == ids.kcon)
return KernelConsole::CurrentTerminal.load()->Ioctl(Request, Argp);
else if (min == ids.tty)
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Ioctl(Request, Argp);
}
else if (min == ids.ptmx)
return -ENOSYS;
return -ENODEV;
}
ssize_t Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
{
dev_t min = Node->GetMinor();
if (min == ids.kcon)
return KernelConsole::CurrentTerminal.load()->Read(Buffer, Size, Offset);
else if (min == ids.tty)
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Read(Buffer, Size, Offset);
}
else if (min == ids.ptmx)
return -ENOSYS;
return -ENODEV;
}
ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
{
dev_t min = Node->GetMinor();
if (min == ids.kcon)
return KernelConsole::CurrentTerminal.load()->Write(Buffer, Size, Offset);
else if (min == ids.tty)
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Write(Buffer, Size, Offset);
}
else if (min == ids.ptmx)
return -ENOSYS;
return -ENODEV;
}
off_t Seek(struct Inode *Node, off_t Offset) { return -ENOSYS; }
int Stat(struct Inode *Node, struct kstat *Stat) { return -ENOSYS; }
const struct InodeOperations ops = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = Read,
.Write = Write,
.Truncate = nullptr,
.Open = Open,
.Close = Close,
.Ioctl = Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = Seek,
.Stat = Stat,
};
int Entry()
{
ptmx = new TTY::PTMXDevice;
mode_t mode = 0;
/* c rw- r-- --- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP |
S_IFCHR;
ids.kcon = DriverManager->CreateDeviceFile(DriverID, "kcon", mode, &ops);
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IRUSR | S_IWUSR |
S_IFCHR;
ids.tty = DriverManager->CreateDeviceFile(DriverID, "tty", mode, &ops);
ids.ptmx = DriverManager->CreateDeviceFile(DriverID, "ptmx", mode, &ops);
return 0;
}
int Final()
{
DriverManager->UnregisterDevice(DriverID, ids.kcon);
DriverManager->UnregisterDevice(DriverID, ids.tty);
DriverManager->UnregisterDevice(DriverID, ids.ptmx);
delete ptmx;
return 0;
}
int Panic() { return 0; }
int Probe() { return 0; }
REGISTER_BUILTIN_DRIVER(pty,
"Pseudo Terminal Devices Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}

View File

@ -0,0 +1,917 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <cpu.hpp>
#include <pci.hpp>
#include <io.h>
#include <interface/aip.h>
extern Driver::Manager *DriverManager;
extern PCI::Manager *PCIManager;
namespace Driver::VMwareToolBox
{
dev_t DriverID;
enum RPCMessages
{
MSG_OPEN,
MSG_SENDSIZE,
MSG_SENDPAYLOAD,
MSG_RECVSIZE,
MSG_RECVPAYLOAD,
MSG_RECVSTATUS,
MSG_CLOSE,
};
enum RPCStatus
{
STATUS_SUCCESS = 0x1,
STATUS_DORECV = 0x2,
STATUS_CPT = 0x10,
STATUS_HB = 0x80,
};
typedef struct
{
union
{
uint32_t ax;
uint32_t magic;
};
union
{
uint32_t bx;
size_t size;
};
union
{
uint32_t cx;
uint16_t command;
};
union
{
uint32_t dx;
uint16_t port;
};
uint32_t si;
uint32_t di;
} VMwareCommand;
#define VMWARE_MAGIC 0x564D5868
#define VMWARE_PORT 0x5658
#define VMWARE_PORTHB 0x5659
#define VMWARE_HYPERVISOR_HB 0x00000000
#define VMWARE_HYPERVISOR_OUT 0x00000001
#define CMD_GETVERSION 0xA
#define CMD_MESSAGE 0x1E
#define CMD_ABSPOINTER_DATA 0x27
#define CMD_ABSPOINTER_STATUS 0x28
#define CMD_ABSPOINTER_COMMAND 0x29
#define ABSPOINTER_ENABLE 0x45414552
#define ABSPOINTER_RELATIVE 0xF5
#define ABSPOINTER_ABSOLUTE 0x53424152
#define MESSAGE_RPCI 0x49435052
#define MESSAGE_TCLO 0x4f4c4354
#define FLAG_COOKIE 0x80000000
#define ToMsg(x) ((x) << 16 | CMD_MESSAGE)
#define HighWord(x) ((x & 0xFFFF0000) >> 16)
#define MESSAGE_HB_MSG 0
#define MESSAGE_OPEN_CHANNEL ToMsg(MSG_OPEN)
#define MESSAGE_CLOSE_CHANNEL ToMsg(MSG_CLOSE)
#define MESSAGE_SEND_SIZE ToMsg(MSG_SENDSIZE)
#define MESSAGE_SEND_PAYLOAD ToMsg(MSG_SENDPAYLOAD)
#define MESSAGE_RECV_SIZE ToMsg(MSG_RECVSIZE)
#define MESSAGE_RECV_PAYLOAD ToMsg(MSG_RECVPAYLOAD)
#define MESSAGE_RECV_STATUS ToMsg(MSG_RECVSTATUS)
#if defined(__amd64__)
#define VM_PORT(cmd, in_ebx, isi, idi, \
flags, magic, \
ax, bx, cx, dx, si, di) \
__asm__ __volatile__("movw $0x5658, %%dx\n" \
"inl %%dx, %%eax\n" \
: "=a"(ax), \
"=b"(bx), \
"=c"(cx), \
"=d"(dx), \
"=S"(si), \
"=D"(di) \
: "a"(magic), \
"b"(in_ebx), \
"c"(cmd), \
"d"(flags), \
"S"(isi), \
"D"(idi) : "memory")
#define VM_PORT_HB_OUT(cmd, in_ecx, isi, idi, \
flags, magic, bp, \
ax, bx, cx, dx, si, di) \
__asm__ __volatile__("push %%rbp\n" \
"mov %12, %%rbp\n" \
"movw $0x5659, %%dx\n" \
"rep outsb\n" \
"pop %%rbp\n" \
: "=a"(ax), \
"=b"(bx), \
"=c"(cx), \
"=d"(dx), \
"=S"(si), \
"=D"(di) \
: "a"(magic), \
"b"(cmd), \
"c"(in_ecx), \
"d"(flags), \
"S"(isi), \
"D"(idi), \
"r"(bp) : "memory", "cc")
#define VM_PORT_HB_IN(cmd, in_ecx, isi, idi, \
flags, magic, bp, \
ax, bx, cx, dx, si, di) \
__asm__ __volatile__("push %%rbp\n" \
"mov %12, %%rbp\n" \
"movw $0x5659, %%dx\n" \
"rep insb\n" \
"pop %%rbp\n" \
: "=a"(ax), \
"=b"(bx), \
"=c"(cx), \
"=d"(dx), \
"=S"(si), \
"=D"(di) \
: "a"(magic), \
"b"(cmd), \
"c"(in_ecx), \
"d"(flags), \
"S"(isi), \
"D"(idi), \
"r"(bp) : "memory", "cc")
#elif defined(__i386__)
#define VM_PORT(cmd, in_ebx, isi, idi, \
flags, magic, \
ax, bx, cx, dx, si, di)
#define VM_PORT_HB_OUT(cmd, in_ecx, isi, idi, \
flags, \
magic, bp, ax, \
bx, cx, dx, si, di)
#define VM_PORT_HB_IN(cmd, in_ecx, isi, idi, \
flags, magic, bp, \
ax, bx, cx, dx, si, di)
#endif
/* TODO:
- use vmcall or vmmcall instead of "out" and "in" if available
*/
typedef struct
{
int TCLOChannel;
uint16_t ChannelID;
uint32_t CookieHigh;
uint32_t CookieLow;
} ToolboxContext;
dev_t MouseDevID = -1;
int __strcmp(const char *l, const char *r)
{
for (; *l == *r && *l; l++, r++)
;
return *(unsigned char *)l - *(unsigned char *)r;
}
void __cpuid(uint32_t Function,
uint32_t *eax, uint32_t *ebx,
uint32_t *ecx, uint32_t *edx)
{
asmv("cpuid" : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) : "a"(Function));
}
bool __CheckHypervisorBit()
{
uint32_t eax, ebx, ecx, edx;
__cpuid(0x1, &eax, &ebx, &ecx, &edx);
if (!(ecx & (1 << 31)))
return false; /* Hypervisor not detected */
return true;
}
bool __VMwareBackdoorHypervisors()
{
const char hv[13] = {0};
uint32_t eax, ebx, ecx, edx;
__cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
*(uint32_t *)hv = ebx;
*(uint32_t *)(hv + 4) = ecx;
*(uint32_t *)(hv + 8) = edx;
if (__strcmp(hv, "VMwareVMware") != 0 &&
__strcmp(hv, "KVMKVMKVM") != 0 &&
__strcmp(hv, "TCGTCGTCGTCG") != 0)
{
return false;
}
return true;
}
bool IsVMwareBackdoorAvailable()
{
if (!__CheckHypervisorBit())
return false;
if (!__VMwareBackdoorHypervisors())
return false;
struct
{
union
{
uint32_t ax;
uint32_t magic;
};
union
{
uint32_t bx;
size_t size;
};
union
{
uint32_t cx;
uint16_t command;
};
union
{
uint32_t dx;
uint16_t port;
};
uint32_t si;
uint32_t di;
} cmd;
cmd.si = cmd.di = 0;
cmd.bx = ~0x564D5868;
cmd.command = 0xA;
cmd.magic = 0x564D5868;
cmd.port = 0x5658;
asmv("in %%dx, %0" : "+a"(cmd.ax), "+b"(cmd.bx),
"+c"(cmd.cx), "+d"(cmd.dx),
"+S"(cmd.si), "+D"(cmd.di));
if (cmd.bx != 0x564D5868 ||
cmd.ax == 0xFFFFFFFF)
return false;
return true;
}
static int OpenMessageChannel(ToolboxContext *ctx, uint32_t Protocol)
{
uintptr_t ax, bx, cx, dx, si = 0, di = 0;
VM_PORT(MESSAGE_OPEN_CHANNEL,
(Protocol | FLAG_COOKIE), si, di,
0, VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
{
trace("Failed to open message channel %#lx", Protocol);
return -EINVAL;
}
debug("Opened message channel %d (Protocol: %#lx)",
HighWord(dx), Protocol);
ctx->ChannelID = (uint16_t)HighWord(dx);
ctx->CookieHigh = si;
ctx->CookieLow = di;
return 0;
}
static void MessageClose(ToolboxContext *ctx)
{
uintptr_t ax, bx, cx, dx,
si = ctx->CookieHigh,
di = ctx->CookieLow;
VM_PORT(MESSAGE_CLOSE_CHANNEL,
0, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
debug("Closed message channel %d", ctx->ChannelID);
}
static uintptr_t MessageSendHB(ToolboxContext *ctx,
const char *Message)
{
uintptr_t ax, bx, cx, dx,
si = (uintptr_t)Message,
di = ctx->CookieLow,
bp = ctx->CookieHigh;
uint32_t ChannelID = ctx->ChannelID << 16;
size_t Size = strlen(Message);
VM_PORT_HB_OUT((STATUS_SUCCESS << 16) | MESSAGE_HB_MSG,
Size, si, di,
VMWARE_HYPERVISOR_HB | ChannelID | VMWARE_HYPERVISOR_OUT,
VMWARE_MAGIC, bp,
ax, bx, cx, dx, si, di);
return bx;
}
static uintptr_t MessageSendLB(ToolboxContext *ctx,
const char *Message)
{
uintptr_t ax, bx,
cx = STATUS_SUCCESS << 16,
dx, si, di;
size_t Size = strlen(Message);
while (Size &&
(HighWord(cx) & STATUS_SUCCESS))
{
uint32_t TotalBytes = MIN((uint32_t)Size, (uint32_t)4);
uint32_t Word = 0;
memcpy(&Word, Message, TotalBytes);
Message += TotalBytes;
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_SEND_PAYLOAD,
Word, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
}
return cx;
}
static uintptr_t MessageReceiveHB(ToolboxContext *ctx,
char *Buffer,
size_t BufferSize)
{
uintptr_t ax, bx, cx, dx,
si = ctx->CookieHigh,
di = (uintptr_t)Buffer,
bp = ctx->CookieLow;
uint32_t ChannelID = ctx->ChannelID << 16;
VM_PORT_HB_IN((STATUS_SUCCESS << 16) | MESSAGE_HB_MSG,
BufferSize, si, di,
VMWARE_HYPERVISOR_HB | ChannelID | VMWARE_HYPERVISOR_OUT,
VMWARE_MAGIC, bp,
ax, bx, cx, dx, si, di);
return bx;
}
static uintptr_t MessageReceiveLB(ToolboxContext *ctx,
char *Buffer,
size_t BufferSize)
{
uintptr_t ax, bx,
cx = STATUS_SUCCESS << 16,
dx, si, di;
while (BufferSize)
{
uint32_t TotalBytes = MIN((uint32_t)BufferSize, (uint32_t)4);
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_RECV_PAYLOAD,
STATUS_SUCCESS, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
break;
memcpy(Buffer, &bx, TotalBytes);
Buffer += TotalBytes;
BufferSize -= TotalBytes;
}
return cx;
}
static int MessageSend(ToolboxContext *ctx,
const char *Message)
{
uintptr_t ax, bx, cx, dx, si, di;
size_t Size = strlen(Message);
int Retries = 0;
while (Retries < 2)
{
Retries++;
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_SEND_SIZE,
Size, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
{
trace("Failed to send message size for \"%s\": %d",
Message, cx);
return -EINVAL;
}
bool HighBand = (HighWord(cx) & STATUS_HB) != 0;
if (HighBand)
bx = MessageSendHB(ctx, Message);
else
bx = MessageSendLB(ctx, Message);
int status = HighWord(bx);
if ((status & STATUS_SUCCESS) != 0)
{
debug("Message \"%s\" sent", Message);
return 0;
}
else if ((status & STATUS_CPT) == 0)
{
trace("Checkpoint occurred for message \"%s\"", Message);
continue;
}
else
break;
}
trace("Failed to send message \"%s\": %#lx", Message, bx);
return -EINVAL;
}
static int MessageReceive(ToolboxContext *ctx,
char **Buffer,
size_t *BufferSize)
{
uintptr_t ax, bx, cx, dx, si, di;
int Retries = 0;
*Buffer = NULL;
*BufferSize = 0;
char *ReplyBuf = NULL;
size_t ReplyBufPages = 0;
size_t ReplySize = 0;
while (Retries < 2)
{
Retries++;
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_RECV_SIZE,
0, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
{
trace("Failed to receive message size: %d", cx);
return -EINVAL;
}
else if ((HighWord(cx) & STATUS_DORECV) == 0)
{
debug("No message to receive");
return -EAGAIN;
}
ReplySize = bx;
if (ReplyBuf != NULL)
DriverManager->FreeMemory(DriverID, ReplyBuf, ReplyBufPages);
ReplyBufPages = ReplySize / 0x1000 + 1;
ReplyBuf = (char *)DriverManager->AllocateMemory(DriverID, ReplyBufPages);
bool HighBand = (HighWord(cx) & STATUS_HB) != 0;
if (HighBand)
bx = MessageReceiveHB(ctx, ReplyBuf, ReplySize);
else
bx = MessageReceiveLB(ctx, ReplyBuf, ReplySize);
if ((HighWord(bx) & STATUS_SUCCESS) == 0)
{
if ((HighWord(bx) & STATUS_CPT) == 0)
{
trace("Checkpoint occurred for message payload");
continue;
}
trace("Failed to receive message payload: %d", HighWord(bx));
DriverManager->FreeMemory(DriverID, ReplyBuf, ReplyBufPages);
return -EINVAL;
}
ReplyBuf[ReplySize] = '\0';
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_RECV_STATUS,
STATUS_SUCCESS, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
{
if ((HighWord(cx) & STATUS_CPT) == 0)
{
trace("Retrying message receive");
continue;
}
trace("Failed to receive message status: %d", HighWord(cx));
DriverManager->FreeMemory(DriverID, ReplyBuf, ReplyBufPages);
return -EINVAL;
}
break;
}
if (ReplyBuf == NULL)
{
trace("Failed to receive message");
return -EINVAL;
}
*Buffer = ReplyBuf;
*BufferSize = ReplySize;
debug("Received message \"%s\"", ReplyBuf);
return 0;
}
static int SendRPCI(ToolboxContext *, const char *Request)
{
ToolboxContext rpci_ctx = {};
int status = OpenMessageChannel(&rpci_ctx, MESSAGE_RPCI);
if (status < 0)
{
trace("Failed to open RPCI channel: %d", status);
return status;
}
status = MessageSend(&rpci_ctx, Request);
if (status < 0)
{
trace("Failed to send RPCI request: %d", status);
return status;
}
MessageClose(&rpci_ctx);
return 0;
}
int MsgEqual(const char *haystack, const char *needle)
{
return strstr(haystack, needle) == haystack;
}
static int DisplayGetSize(ToolboxContext *ctx)
{
if (ctx->TCLOChannel != -1)
MessageClose(ctx);
OpenMessageChannel(ctx, MESSAGE_TCLO);
char EmptyBuffer[256] = {'\0'};
MessageSend(ctx, EmptyBuffer);
while (true)
{
/* FIXME: buf memory leak */
char *buf;
size_t len;
int status = MessageReceive(ctx, &buf, &len);
if (status == -EAGAIN)
{
v0::Sleep(DriverID, 1000);
continue;
}
else if (status < 0)
{
trace("Failed to receive message");
return 1;
}
buf[strlen(buf)] = '\0';
if (MsgEqual(buf, "reset"))
{
if (MessageSend(ctx, "OK ATR toolbox") < 0)
return 1;
}
else if (MsgEqual(buf, "ping"))
{
if (MessageSend(ctx, "OK ") < 0)
return 1;
}
else if (MsgEqual(buf, "Capabilities_Register"))
{
SendRPCI(ctx, "tools.capability.resolution_set 1");
SendRPCI(ctx, "tools.capability.resolution_server toolbox 1");
SendRPCI(ctx, "tools.capability.display_topology_set 1");
SendRPCI(ctx, "tools.capability.color_depth_set 1");
SendRPCI(ctx, "tools.capability.resolution_min 0 0");
SendRPCI(ctx, "tools.capability.unity 1");
if (MessageSend(ctx, "OK ") < 0)
return 1;
}
else if (MsgEqual(buf, "Resolution_Set"))
{
debug("%s", buf);
if (MessageSend(ctx, "OK ") < 0)
return 1;
MessageClose(ctx);
return 0;
}
else
{
if (MessageSend(ctx, "ERROR Unknown command") < 0)
return 1;
}
}
}
pid_t dst_id = -1;
pid_t dst_pid = -1;
ToolboxContext *tb_ctx = NULL;
void DisplayScaleThread()
{
/* sizeof ToolboxContext */
tb_ctx = (ToolboxContext *)DriverManager->AllocateMemory(DriverID, 1);
v0::Sleep(DriverID, 2000);
while (true)
{
if (DisplayGetSize(tb_ctx) != 0)
trace("Failed to scale display");
v0::Sleep(DriverID, 1000);
}
}
void CommandSend(VMwareCommand *cmd)
{
cmd->magic = VMWARE_MAGIC;
cmd->port = VMWARE_PORT;
asm volatile("in %%dx, %0"
: "+a"(cmd->ax), "+b"(cmd->bx),
"+c"(cmd->cx), "+d"(cmd->dx),
"+S"(cmd->si), "+D"(cmd->di));
}
void Absolute()
{
VMwareCommand cmd = {};
/* Enable */
cmd.bx = ABSPOINTER_ENABLE;
cmd.command = CMD_ABSPOINTER_COMMAND;
CommandSend(&cmd);
/* Status */
cmd.bx = 0;
cmd.command = CMD_ABSPOINTER_STATUS;
CommandSend(&cmd);
/* Read data (1) */
cmd.bx = 1;
cmd.command = CMD_ABSPOINTER_DATA;
CommandSend(&cmd);
/* Enable absolute */
cmd.bx = ABSPOINTER_ABSOLUTE;
cmd.command = CMD_ABSPOINTER_COMMAND;
CommandSend(&cmd);
}
void Relative()
{
VMwareCommand cmd = {};
cmd.bx = ABSPOINTER_RELATIVE;
cmd.command = CMD_ABSPOINTER_COMMAND;
CommandSend(&cmd);
}
InputReport ir = {};
void InterruptHandler(CPU::TrapFrame *)
{
uint8_t Data = inb(0x60);
(void)Data;
VMwareCommand cmd = {};
cmd.bx = 0;
cmd.command = CMD_ABSPOINTER_STATUS;
CommandSend(&cmd);
if (cmd.ax == 0xFFFF0000)
{
trace("VMware mouse is not connected?");
Relative();
Absolute();
return;
}
if ((cmd.ax & 0xFFFF) < 4)
return;
cmd.bx = 4;
cmd.command = CMD_ABSPOINTER_DATA;
CommandSend(&cmd);
int Buttons = (cmd.ax & 0xFFFF);
/**
* How should I handle this?
* (cmd.[bx,cx] * Width) / 0xFFFF
* Maybe TODO: Width and Height API?
*/
uintptr_t AbsoluteX = cmd.bx;
uintptr_t AbsoluteY = cmd.cx;
ir.Type = INPUT_TYPE_MOUSE;
ir.Device = MouseDevID;
ir.Mouse.X = AbsoluteX;
ir.Mouse.Y = AbsoluteY;
ir.Mouse.Z = (int8_t)cmd.dx;
ir.Mouse.Absolute = 1;
ir.Mouse.LeftButton = Buttons & 0x20;
ir.Mouse.RightButton = Buttons & 0x10;
ir.Mouse.MiddleButton = Buttons & 0x08;
// ir.Mouse.Button4 = 0x0;
// ir.Mouse.Button5 = 0x0;
// ir.Mouse.Button6 = 0x0;
// ir.Mouse.Button7 = 0x0;
// ir.Mouse.Button8 = 0x0;
v0::ReportInputEvent(DriverID, &ir);
}
int Ioctl(struct Inode *, unsigned long Request, void *)
{
switch (Request)
{
case 0x1:
Relative();
break;
case 0x2:
Absolute();
break;
default:
return -EINVAL;
}
return 0;
}
const struct InodeOperations ops = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = nullptr,
.Write = nullptr,
.Truncate = nullptr,
.Open = nullptr,
.Close = nullptr,
.Ioctl = Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
bool ToolboxSupported = false;
int Entry()
{
ToolboxContext tb_ctx = {};
/* Test if it's supported */
int status = OpenMessageChannel(&tb_ctx, MESSAGE_TCLO);
if (status == 0)
{
ToolboxSupported = true;
MessageClose(&tb_ctx);
dst_id = v0::CreateKernelThread(DriverID, 0, "VMware Display Scale",
(void *)DisplayScaleThread, NULL);
dst_pid = v0::GetCurrentProcess(DriverID);
}
v0::PS2WriteCommand(DriverID, PS2_CMD_ENABLE_PORT_2);
v0::PS2WriteCommand(DriverID, PS2_CMD_READ_CONFIG);
PS2_CONFIGURATION config = {.Raw = v0::PS2ReadData(DriverID)};
config.Port2Interrupt = 1;
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_CONFIG);
v0::PS2WriteData(DriverID, config.Raw);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_SET_DEFAULTS);
v0::PS2ReadData(DriverID);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_ENABLE_DATA_REPORTING);
v0::PS2ReadData(DriverID);
Absolute();
/**
* If we have another driver using the PS/2 mouse, we need to
* override its interrupt handler.
*/
v0::OverrideInterruptHandler(DriverID, 12, (void *)InterruptHandler);
MouseDevID = v0::RegisterDevice(DriverID, INPUT_TYPE_MOUSE, &ops);
return 0;
}
int Final()
{
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
Relative();
v0::UnregisterDevice(DriverID, MouseDevID);
if (ToolboxSupported)
{
v0::KillThread(DriverID, dst_id, dst_pid, 0);
if (tb_ctx->TCLOChannel != -1)
MessageClose(tb_ctx);
DriverManager->FreeMemory(DriverID, tb_ctx, 1);
}
return 0;
}
int Panic()
{
Relative();
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
return 0;
}
int Probe()
{
if (!IsVMwareBackdoorAvailable())
return -ENODEV;
return 0;
}
REGISTER_BUILTIN_DRIVER(vmware,
"VMware Tools Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}

View File

@ -0,0 +1,485 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <cpu.hpp>
#include <pci.hpp>
#include <net/net.hpp>
#include "e1000.hpp"
extern Driver::Manager *DriverManager;
extern PCI::Manager *PCIManager;
namespace Driver::E1000
{
dev_t DriverID;
class E1000Device
{
private:
PCI::PCIHeader0 *Header;
uint16_t DeviceID;
bool Initialized = false;
bool EEPROMAvailable = false;
struct BARData
{
uint8_t Type;
uint16_t IOBase;
uint64_t MemoryBase;
} BAR;
#define E1000_NUM_RX_DESC 32
#define E1000_NUM_TX_DESC 8
RXDescriptor *RX[E1000_NUM_RX_DESC];
TXDescriptor *TX[E1000_NUM_TX_DESC];
uint16_t RXCurrent;
uint16_t TXCurrent;
const int BaseBufferSize = 8192;
const int AdditionalBytes = 16;
uint32_t CurrentPacket;
void WriteCMD(uint16_t Address, uint32_t Value)
{
if (BAR.Type == 0)
mmoutl((void *)(BAR.MemoryBase + Address), Value);
else
{
outl(BAR.IOBase, Address);
outl(BAR.IOBase + 4, Value);
}
}
uint32_t ReadCMD(uint16_t Address)
{
if (BAR.Type == 0)
return mminl((void *)(BAR.MemoryBase + Address));
else
{
outl(BAR.IOBase, Address);
return inl(BAR.IOBase + 0x4);
}
}
uint32_t ReadEEPROM(uint8_t Address)
{
uint16_t Data = 0;
uint32_t temp = 0;
if (EEPROMAvailable)
{
WriteCMD(REG::EEPROM, (1) | ((uint32_t)(Address) << 8));
while (!((temp = ReadCMD(REG::EEPROM)) & (1 << 4)))
;
}
else
{
WriteCMD(REG::EEPROM, (1) | ((uint32_t)(Address) << 2));
while (!((temp = ReadCMD(REG::EEPROM)) & (1 << 1)))
;
}
Data = (uint16_t)((temp >> 16) & 0xFFFF);
return Data;
}
void InitializeRX()
{
debug("Initializing RX...");
uintptr_t Ptr = (uintptr_t)v0::AllocateMemory(DriverID,
TO_PAGES(sizeof(RXDescriptor) *
E1000_NUM_RX_DESC +
AdditionalBytes));
for (int i = 0; i < E1000_NUM_RX_DESC; i++)
{
RX[i] = (RXDescriptor *)(Ptr + i * 16);
RX[i]->Address = (uint64_t)v0::AllocateMemory(DriverID,
TO_PAGES(BaseBufferSize + AdditionalBytes));
RX[i]->Status = 0;
}
#pragma GCC diagnostic ignored "-Wshift-count-overflow"
WriteCMD(REG::TXDESCLO, (uint32_t)(Ptr >> 32));
WriteCMD(REG::TXDESCHI, (uint32_t)(Ptr & 0xFFFFFFFF));
WriteCMD(REG::RXDESCLO, (uint32_t)Ptr);
WriteCMD(REG::RXDESCHI, 0);
WriteCMD(REG::RXDESCLEN, E1000_NUM_RX_DESC * 16);
WriteCMD(REG::RXDESCHEAD, 0);
WriteCMD(REG::RXDESCTAIL, E1000_NUM_RX_DESC - 1);
RXCurrent = 0;
WriteCMD(REG::RCTRL, RCTL::EN | RCTL::SBP | RCTL::UPE |
RCTL::MPE | RCTL::LBM_NONE |
RTCL::RDMTS_HALF | RCTL::BAM |
RCTL::SECRC | RCTL::BSIZE_8192);
}
void InitializeTX()
{
debug("Initializing TX...");
uintptr_t Ptr = (uintptr_t)v0::AllocateMemory(DriverID,
TO_PAGES(sizeof(TXDescriptor) *
E1000_NUM_RX_DESC +
AdditionalBytes));
for (short i = 0; i < E1000_NUM_TX_DESC; i++)
{
TX[i] = (TXDescriptor *)((uintptr_t)Ptr + i * 16);
TX[i]->Address = 0;
TX[i]->Command = 0;
TX[i]->Status = TSTA::DD;
}
WriteCMD(REG::TXDESCHI, (uint32_t)((uint64_t)Ptr >> 32));
WriteCMD(REG::TXDESCLO, (uint32_t)((uint64_t)Ptr & 0xFFFFFFFF));
WriteCMD(REG::TXDESCLEN, E1000_NUM_TX_DESC * 16);
WriteCMD(REG::TXDESCHEAD, 0);
WriteCMD(REG::TXDESCTAIL, 0);
TXCurrent = 0;
WriteCMD(REG::TCTRL, TCTL::EN_ | TCTL::PSP |
(15 << TCTL::CT_SHIFT) |
(64 << TCTL::COLD_SHIFT) |
TCTL::RTLC);
WriteCMD(REG::TCTRL, 0b0110000000000111111000011111010);
WriteCMD(REG::TIPG, 0x0060200A);
}
public:
dev_t ID;
bool IsInitialized() { return Initialized; }
size_t write(uint8_t *Buffer, size_t Size)
{
TX[TXCurrent]->Address = (uint64_t)Buffer;
TX[TXCurrent]->Length = (uint16_t)Size;
TX[TXCurrent]->Command = CMD::EOP | CMD::IFCS | CMD::RS;
TX[TXCurrent]->Status = 0;
uint16_t OldTXCurrent = TXCurrent;
TXCurrent = (uint16_t)((TXCurrent + 1) % E1000_NUM_TX_DESC);
WriteCMD(REG::TXDESCTAIL, TXCurrent);
while (!(TX[OldTXCurrent]->Status & 0xFF))
v0::Yield(DriverID);
return Size;
}
MediaAccessControl GetMAC()
{
MediaAccessControl mac;
if (EEPROMAvailable)
{
uint32_t temp;
temp = ReadEEPROM(0);
mac.Address[0] = temp & 0xff;
mac.Address[1] = (uint8_t)(temp >> 8);
temp = ReadEEPROM(1);
mac.Address[2] = temp & 0xff;
mac.Address[3] = (uint8_t)(temp >> 8);
temp = ReadEEPROM(2);
mac.Address[4] = temp & 0xff;
mac.Address[5] = (uint8_t)(temp >> 8);
}
else
{
uint8_t *BaseMac8 = (uint8_t *)(BAR.MemoryBase + 0x5400);
uint32_t *BaseMac32 = (uint32_t *)(BAR.MemoryBase + 0x5400);
if (BaseMac32[0] != 0)
for (int i = 0; i < 6; i++)
mac.Address[i] = BaseMac8[i];
else
{
trace("No MAC address found.");
return MediaAccessControl();
}
}
return mac;
}
int ioctl(NetIoctl req, void *arg)
{
switch (req)
{
case IOCTL_NET_GET_MAC:
{
MediaAccessControl mac = GetMAC();
*((uint48_t *)arg) = mac.ToHex(); /* UNTESTED */
return 0;
}
default:
return -EINVAL;
}
return 0;
}
void OnInterruptReceived(CPU::TrapFrame *)
{
WriteCMD(REG::IMASK, 0x1);
uint32_t status = ReadCMD(0xC0);
UNUSED(status);
while ((RX[RXCurrent]->Status & 0x1))
{
uint8_t *data = (uint8_t *)RX[RXCurrent]->Address;
uint16_t dataSz = RX[RXCurrent]->Length;
// ReportNetworkPacket(ID, data, dataSz);
/* FIXME: Implement */
trace("FIXME: Received packet");
(void)data;
(void)dataSz;
RX[RXCurrent]->Status = 0;
uint16_t OldRXCurrent = RXCurrent;
RXCurrent = (uint16_t)((RXCurrent + 1) % E1000_NUM_RX_DESC);
WriteCMD(REG::RXDESCTAIL, OldRXCurrent);
}
}
void Panic()
{
WriteCMD(REG::IMASK, 0x00000000);
WriteCMD(REG::ITR, 0x00000000);
WriteCMD(REG::IAM, 0x00000000);
}
E1000Device(PCI::PCIHeader0 *_Header, uint16_t _DeviceID)
: Header(_Header),
DeviceID(_DeviceID)
{
uint32_t PCIBAR0 = Header->BAR0;
uint32_t PCIBAR1 = Header->BAR1;
BAR.Type = PCIBAR0 & 1;
BAR.IOBase = (uint16_t)(PCIBAR0 & (~3));
BAR.MemoryBase = PCIBAR1 & (~15);
switch (DeviceID)
{
case 0x100E:
{
trace("Found Intel 82540EM Gigabit Ethernet Controller.");
/* Detect EEPROM */
WriteCMD(REG::EEPROM, 0x1);
for (int i = 0; i < 1000 && !EEPROMAvailable; i++)
if (ReadCMD(REG::EEPROM) & 0x10)
EEPROMAvailable = true;
else
EEPROMAvailable = false;
if (!GetMAC().Valid())
{
trace("Failed to get MAC");
return;
}
/* Start link */
uint32_t cmdret = ReadCMD(REG::CTRL);
WriteCMD(REG::CTRL, cmdret | ECTRL::SLU);
for (int i = 0; i < 0x80; i++)
WriteCMD((uint16_t)(0x5200 + i * 4), 0);
WriteCMD(REG::IMASK, 0x1F6DC);
WriteCMD(REG::IMASK, 0xFF & ~4);
ReadCMD(0xC0);
InitializeRX();
InitializeTX();
break;
}
default:
{
trace("Unimplemented E1000 device.");
return;
}
}
Initialized = true;
}
~E1000Device()
{
if (!Initialized)
return;
switch (DeviceID)
{
case 0x100E:
{
// Clearing Enable bit in Receive Control Register
uint32_t cmdret = ReadCMD(REG::RCTRL);
WriteCMD(REG::RCTRL, cmdret & ~RCTL::EN);
// Masking Interrupt Mask, Interrupt Throttling Rate & Interrupt Auto-Mask
WriteCMD(REG::IMASK, 0x00000000);
WriteCMD(REG::ITR, 0x00000000);
WriteCMD(REG::IAM, 0x00000000);
// Clearing SLU bit in Device Control Register
cmdret = ReadCMD(REG::CTRL);
WriteCMD(REG::CTRL, cmdret & ~ECTRL::SLU);
// Clear the Interrupt Cause Read register by reading it
ReadCMD(REG::ICR);
// Powering down the device (?)
WriteCMD(REG::CTRL, PCTRL::POWER_DOWN);
/* TODO: Stop link; further testing required */
break;
}
default:
{
trace("Unimplemented E1000 device.");
return;
}
}
((PCI::PCIDeviceHeader *)Header)->Command |= PCI::PCI_COMMAND_INTX_DISABLE;
}
};
std::unordered_map<dev_t, E1000Device *> Drivers;
int Open(struct Inode *, int, mode_t)
{
return 0;
}
int Close(struct Inode *)
{
return 0;
}
ssize_t Read(struct Inode *, void *, size_t, off_t)
{
return 0;
}
ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t)
{
return Drivers[Node->GetMinor()]->write((uint8_t *)Buffer, Size);
}
int Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
{
return Drivers[Node->GetMinor()]->ioctl((NetIoctl)Request, Argp);
}
const struct InodeOperations ops = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = Read,
.Write = Write,
.Truncate = nullptr,
.Open = Open,
.Close = Close,
.Ioctl = Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
std::list<PCI::PCIDevice> Devices;
int Entry()
{
for (auto &&dev : Devices)
{
PCIManager->InitializeDevice(dev, KernelPageTable);
E1000Device *e1000 = new E1000Device((PCI::PCIHeader0 *)dev.Header,
dev.Header->DeviceID);
if (e1000->IsInitialized())
{
dev_t ret = v0::RegisterDevice(DriverID, NETWORK_TYPE_ETHERNET, &ops);
Drivers[ret] = e1000;
}
}
if (Drivers.empty())
{
trace("No valid E1000 device found.");
return -EINVAL;
}
return 0;
}
int Final()
{
for (auto &&dev : Drivers)
{
dev_t ret = dev.first;
v0::UnregisterDevice(DriverID, ret);
delete dev.second;
}
return 0;
}
int Panic()
{
for (auto &&i : Drivers)
i.second->Panic();
return 0;
}
int Probe()
{
Devices = PCIManager->FindPCIDevice(
{
0x8086, /* Intel */
},
{
0x100E, /* 82540EM */
0x100F, /* 82545EM */
0x10D3, /* 82574L */
0x10EA, /* I217-LM */
0x153A, /* 82577LM */
});
if (Devices.empty())
{
trace("No E1000 device found.");
return -ENODEV;
}
return 0;
}
REGISTER_BUILTIN_DRIVER(e1000,
"Intel(R) PRO/1000 Network Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}

View File

@ -1,25 +1,24 @@
/*
This file is part of Fennix Drivers.
This file is part of Fennix Kernel.
Fennix Drivers is free software: you can redistribute it and/or
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <types.h>
#ifdef __cplusplus
enum REG
{
CTRL = 0x0000,
@ -155,9 +154,3 @@ struct TXDescriptor
volatile uint8_t css;
volatile uint16_t Special;
} __attribute__((packed));
#endif
EXTERNC int cxx_Panic();
EXTERNC int cxx_Probe();
EXTERNC int cxx_Initialize();
EXTERNC int cxx_Finalize();

View File

@ -0,0 +1,294 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <cpu.hpp>
#include <pci.hpp>
#include <net/net.hpp>
#include <io.h>
#include "rtl8139.hpp"
extern Driver::Manager *DriverManager;
extern PCI::Manager *PCIManager;
namespace Driver::RTL8139
{
dev_t DriverID;
class RTL8139Device
{
private:
PCI::PCIHeader0 *Header;
bool Initialized = false;
struct BARData
{
uint8_t Type;
uint16_t IOBase;
uint64_t MemoryBase;
} BAR;
const int BaseBufferSize = 8192;
const int WRAPBytes = 1500;
const int AdditionalBytes = 16;
const int BufferSize = BaseBufferSize +
WRAPBytes +
AdditionalBytes;
uint8_t *RXBuffer = nullptr;
int TXCurrent = 0;
uint16_t CurrentPacket = 0;
uint8_t TSAD[4] = {0x20, 0x24, 0x28, 0x2C};
uint8_t TSD[4] = {0x10, 0x14, 0x18, 0x1C};
public:
dev_t ID;
bool IsInitialized() { return Initialized; }
size_t write(uint8_t *Buffer, size_t Size)
{
outl(TSAD[TXCurrent], (uint32_t)(reinterpret_cast<uint64_t>(Buffer)));
outl(TSD[TXCurrent++], (uint32_t)Size);
if (TXCurrent > 3)
TXCurrent = 0;
return Size;
}
MediaAccessControl GetMAC()
{
return MediaAccessControl();
}
int ioctl(NetIoctl req, void *)
{
switch (req)
{
case IOCTL_NET_GET_MAC:
{
return -ENOSYS;
}
default:
return -EINVAL;
}
return 0;
}
void OnInterruptReceived(CPU::TrapFrame *)
{
/* Acknowledge interrupt */
uint16_t status = inw(RegISR);
debug("%#lx", status);
/* Read status */
if (status & RecOK)
{
/* Get the current packet */
uint16_t *data = (uint16_t *)(RXBuffer + CurrentPacket);
uint16_t dataSz = *(data + 1);
data += 2;
// ReportNetworkPacket(ID, data, dataSz);
/* FIXME: Implement */
fixme("Received packet");
(void)data;
(void)dataSz;
/* Update CAPR */
#define RX_READ_PTR_MASK (~0x3)
CurrentPacket = (uint16_t)((CurrentPacket + dataSz + 4 + 3) & RX_READ_PTR_MASK);
if (CurrentPacket > BufferSize)
CurrentPacket -= uint16_t(BufferSize);
outw(RegCAPR, CurrentPacket - 0x10);
}
/* Clear interrupt */
outw(RegISR, (RecOK | RecBad | SendOK | SendBad));
}
void Panic()
{
}
RTL8139Device(PCI::PCIHeader0 *_Header)
: Header(_Header)
{
uint32_t PCIBAR0 = Header->BAR0;
uint32_t PCIBAR1 = Header->BAR1;
BAR.Type = PCIBAR0 & 1;
BAR.IOBase = (uint16_t)(PCIBAR0 & (~3));
BAR.MemoryBase = PCIBAR1 & (~15);
RXBuffer = (uint8_t *)v0::AllocateMemory(DriverID, TO_PAGES(BufferSize));
/* Power on */
outb(RegCONFIG1, 0x0);
/* Software Reset */
outb(RegCMD, 0x10);
while (inb(RegCMD) & 0x10)
v0::Yield(DriverID);
/* Initialize receive buffer */
outl(RegRBSTART, (uint32_t)(reinterpret_cast<uintptr_t>(RXBuffer)));
/* Configure interrupt mask register */
outw(RegIMR, (RecOK | RecBad | SendOK | SendBad));
outl(regRCR, (RcAB | RcAM | RcAPM | RcAAP) | RcWRAP);
/* Enable receive and transmit */
outb(RegCMD, 0xC); /* 0xC = RE and TE bit */
uint32_t MAC1 = inl(RegMAC);
uint16_t MAC2 = inw(RegMAR);
MediaAccessControl mac = {
mac.Address[0] = (uint8_t)MAC1,
mac.Address[1] = (uint8_t)(MAC1 >> 8),
mac.Address[2] = (uint8_t)(MAC1 >> 16),
mac.Address[3] = (uint8_t)(MAC1 >> 24),
mac.Address[4] = (uint8_t)MAC2,
mac.Address[5] = (uint8_t)(MAC2 >> 8)};
Initialized = true;
}
~RTL8139Device()
{
if (!Initialized)
return;
((PCI::PCIDeviceHeader *)Header)->Command |= PCI::PCI_COMMAND_INTX_DISABLE;
/* FIXME: Shutdown code */
}
};
std::unordered_map<dev_t, RTL8139Device *> Drivers;
int Open(struct Inode *, int, mode_t)
{
return 0;
}
int Close(struct Inode *)
{
return 0;
}
ssize_t Read(struct Inode *, void *, size_t, off_t)
{
return 0;
}
ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t)
{
return Drivers[Node->GetMinor()]->write((uint8_t *)Buffer, Size);
}
int Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
{
return Drivers[Node->GetMinor()]->ioctl((NetIoctl)Request, Argp);
}
const struct InodeOperations ops = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = Read,
.Write = Write,
.Truncate = nullptr,
.Open = Open,
.Close = Close,
.Ioctl = Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
std::list<PCI::PCIDevice> Devices;
int Entry()
{
for (auto &&dev : Devices)
{
PCIManager->InitializeDevice(dev, KernelPageTable);
RTL8139Device *rtl8139 = new RTL8139Device((PCI::PCIHeader0 *)dev.Header);
if (rtl8139->IsInitialized())
{
dev_t ret = v0::RegisterDevice(DriverID, NETWORK_TYPE_ETHERNET, &ops);
Drivers[ret] = rtl8139;
}
}
if (Drivers.empty())
{
trace("No valid RTL8139 device found.");
return -EINVAL;
}
return 0;
}
int Final()
{
for (auto &&dev : Drivers)
{
dev_t ret = dev.first;
v0::UnregisterDevice(DriverID, ret);
delete dev.second;
}
return 0;
}
int Panic()
{
for (auto &&i : Drivers)
i.second->Panic();
return 0;
}
int Probe()
{
Devices = PCIManager->FindPCIDevice(
{
0x10EC, /* Realtek */
},
{
0x8139, /* RTL8139 */
});
if (Devices.empty())
{
trace("No RTL8139 device found.");
return -ENODEV;
}
return 0;
}
REGISTER_BUILTIN_DRIVER(rtl8139,
"Realtek RTL8139 Network Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}

View File

@ -1,18 +1,18 @@
/*
This file is part of Fennix Drivers.
This file is part of Fennix Kernel.
Fennix Drivers is free software: you can redistribute it and/or
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
@ -86,8 +86,3 @@ enum Registers
RegIMR = 0x3C,
RegISR = 0x3E,
};
EXTERNC int cxx_Panic();
EXTERNC int cxx_Probe();
EXTERNC int cxx_Initialize();
EXTERNC int cxx_Finalize();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,128 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
extern Driver::Manager *DriverManager;
namespace Driver::KernelDisplayManager
{
dev_t DriverID;
struct
{
dev_t kdm;
} ids;
int Open(struct Inode *Node, int Flags, mode_t Mode)
{
dev_t min = Node->GetMinor();
if (min == ids.kdm)
return -ENOSYS;
return -ENODEV;
}
int Close(struct Inode *Node)
{
dev_t min = Node->GetMinor();
if (min == ids.kdm)
return -ENOSYS;
return -ENODEV;
}
int Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
{
dev_t min = Node->GetMinor();
if (min == ids.kdm)
return -ENOSYS;
return -ENODEV;
}
ssize_t Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
{
dev_t min = Node->GetMinor();
if (min == ids.kdm)
return -ENOSYS;
return -ENODEV;
}
ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
{
dev_t min = Node->GetMinor();
if (min == ids.kdm)
return -ENOSYS;
return -ENODEV;
}
off_t Seek(struct Inode *Node, off_t Offset) { return -ENOSYS; }
int Stat(struct Inode *Node, struct kstat *Stat) { return -ENOSYS; }
const struct InodeOperations ops = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = Read,
.Write = Write,
.Truncate = nullptr,
.Open = Open,
.Close = Close,
.Ioctl = Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = Seek,
.Stat = Stat,
};
int Entry()
{
mode_t mode = 0;
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH |
S_IFCHR;
ids.kdm = DriverManager->CreateDeviceFile(DriverID, "kdm", mode, &ops);
return 0;
}
int Final()
{
DriverManager->UnregisterDevice(DriverID, ids.kdm);
return 0;
}
int Panic() { return 0; }
int Probe() { return 0; }
REGISTER_BUILTIN_DRIVER(kdm,
"Kernel Display Manager Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}

View File

@ -38,6 +38,51 @@
#include <io.h>
#include <list>
struct BuiltInDriver
{
char Name[32] = {'\0'};
char Description[64] = {'\0'};
char Author[32] = {'\0'};
struct
{
int Major, Minor, Patch;
} Version = {0, 0, 0};
char License[32] = {'\0'};
bool Initialized = false;
int ErrorCode = 0;
int (*Entry)() = nullptr;
int (*Final)() = nullptr;
int (*Panic)() = nullptr;
int (*Probe)() = nullptr;
uintptr_t EntryPoint = 0;
};
extern const BuiltInDriver __kernel_builtin_drivers_start[];
extern const BuiltInDriver __kernel_builtin_drivers_end[];
#define REGISTER_BUILTIN_DRIVER(driverName, desc, auth, maj, min, patch, \
entryFunc, finalFunc, panicFunc, initFunc) \
int __builtin_driver_start_##driverName(dev_t id) \
{ \
DriverID = id; \
return 0; \
} \
static const BuiltInDriver __builtin_driver_##driverName \
__attribute__((section(".builtin_drivers"), used)) = { \
#driverName, \
desc, \
auth, \
{maj, min, patch}, \
"", \
false, \
0, \
entryFunc, \
finalFunc, \
panicFunc, \
initFunc, \
(uintptr_t)__builtin_driver_start_##driverName}
namespace Driver
{
char GetScanCode(uint8_t ScanCode, bool Upper);
@ -50,10 +95,10 @@ namespace Driver
RingBuffer<InputReport> *InputReports;
};
struct DriverObject
struct DriverObject : BuiltInDriver
{
bool IsBuiltIn = false;
uintptr_t BaseAddress = 0;
uintptr_t EntryPoint = 0;
Memory::VirtualMemoryArea *vma;
/* Path has the same pointer as in the Node */
@ -61,22 +106,6 @@ namespace Driver
std::unordered_map<uint8_t, void *> *InterruptHandlers;
std::unordered_map<dev_t, DriverHandlers> *DeviceOperations;
dev_t ID = 0;
char Name[32] = {'\0'};
char Description[64] = {'\0'};
char Author[32] = {'\0'};
struct
{
int Major, Minor, Patch;
} Version = {0, 0, 0};
char License[32] = {'\0'};
bool Initialized = false;
int ErrorCode = 0;
int (*Entry)() = nullptr;
int (*Final)() = nullptr;
int (*Panic)() = nullptr;
int (*Probe)() = nullptr;
};
class Manager
@ -93,8 +122,8 @@ namespace Driver
FileNode *devNode = nullptr;
FileNode *devInputNode = nullptr;
int LoadDriverFile(DriverObject &Drv, FileNode *File);
void ReloadDriver(dev_t driverID);
void InitializeDaemonFS();
@ -148,11 +177,15 @@ namespace Driver
*/
dev_t CreateIncrementalDevice(dev_t DriverID, const std::string &Prefix, mode_t Mode, InodeOperations *Ops);
dev_t CreateDeviceFile(dev_t DriverID, const char *name, mode_t mode, const InodeOperations *Operations);
dev_t RegisterDevice(dev_t DriverID, DeviceType Type, const InodeOperations *Operations);
int ReportInputEvent(dev_t DriverID, InputReport *Report);
int UnregisterDevice(dev_t DriverID, dev_t Device);
void *AllocateMemory(dev_t DriverID, size_t Pages);
void FreeMemory(dev_t DriverID, void *Pointer, size_t Pages);
Manager();
~Manager();
};
@ -162,4 +195,73 @@ namespace Driver
void *GetSymbolByName(const char *Name, int Version);
#ifndef NO_API_IN_HEADER
namespace v0
{
typedef int CriticalState;
void KernelPrint(dev_t DriverID, const char *Format, va_list args);
void KernelLog(dev_t DriverID, const char *Format, va_list args);
CriticalState EnterCriticalSection(dev_t DriverID);
void LeaveCriticalSection(dev_t DriverID, CriticalState PreviousState);
int RegisterInterruptHandler(dev_t DriverID, uint8_t IRQ, void *Handler);
int OverrideInterruptHandler(dev_t DriverID, uint8_t IRQ, void *Handler);
int UnregisterInterruptHandler(dev_t DriverID, uint8_t IRQ, void *Handler);
int UnregisterAllInterruptHandlers(dev_t DriverID, void *Handler);
dev_t RegisterFileSystem(dev_t DriverID, FileSystemInfo *Info, struct Inode *Root);
int UnregisterFileSystem(dev_t DriverID, dev_t Device);
pid_t CreateKernelProcess(dev_t DriverID, const char *Name);
pid_t CreateKernelThread(dev_t DriverID, pid_t pId, const char *Name, void *EntryPoint, void *Argument);
pid_t GetCurrentProcess(dev_t DriverID);
int KillProcess(dev_t DriverID, pid_t pId, int ExitCode);
int KillThread(dev_t DriverID, pid_t tId, pid_t pId, int ExitCode);
void Yield(dev_t DriverID);
void Sleep(dev_t DriverID, uint64_t Milliseconds);
void PIC_EOI(dev_t DriverID, uint8_t IRQ);
void IRQ_MASK(dev_t DriverID, uint8_t IRQ);
void IRQ_UNMASK(dev_t DriverID, uint8_t IRQ);
void PS2Wait(dev_t DriverID, const bool Output);
void PS2WriteCommand(dev_t DriverID, uint8_t Command);
void PS2WriteData(dev_t DriverID, uint8_t Data);
uint8_t PS2ReadData(dev_t DriverID);
uint8_t PS2ReadStatus(dev_t DriverID);
uint8_t PS2ReadAfterACK(dev_t DriverID);
void PS2ClearOutputBuffer(dev_t DriverID);
int PS2ACKTimeout(dev_t DriverID);
void *AllocateMemory(dev_t DriverID, size_t Pages);
void FreeMemory(dev_t DriverID, void *Pointer, size_t Pages);
void *MemoryCopy(dev_t DriverID, void *Destination, const void *Source, size_t Length);
void *MemorySet(dev_t DriverID, void *Destination, int Value, size_t Length);
void *MemoryMove(dev_t DriverID, void *Destination, const void *Source, size_t Length);
size_t StringLength(dev_t DriverID, const char String[]);
char *_strstr(dev_t DriverID, const char *Haystack, const char *Needle);
void MapPages(dev_t MajorID, void *PhysicalAddress, void *VirtualAddress, size_t Pages, uint32_t Flags);
void UnmapPages(dev_t MajorID, void *VirtualAddress, size_t Pages);
void AppendMapFlag(dev_t MajorID, void *Address, PageMapFlags Flag);
void RemoveMapFlag(dev_t MajorID, void *Address, PageMapFlags Flag);
void *Znwm(size_t Size);
void ZdlPvm(void *Pointer, size_t Size);
__PCIArray *GetPCIDevices(dev_t DriverID, uint16_t _Vendors[], uint16_t _Devices[]);
void InitializePCI(dev_t DriverID, void *_Header);
uint32_t GetBAR(dev_t DriverID, uint8_t i, void *_Header);
uint8_t iLine(dev_t DriverID, PCI::PCIDevice *Device);
uint8_t iPin(dev_t DriverID, PCI::PCIDevice *Device);
dev_t CreateDeviceFile(dev_t DriverID, const char *name, mode_t mode, const InodeOperations *Operations);
dev_t RegisterDevice(dev_t DriverID, DeviceType Type, const InodeOperations *Operations);
int UnregisterDevice(dev_t DriverID, dev_t Device);
int ReportInputEvent(dev_t DriverID, InputReport *Report);
}
#endif // !NO_API_IN_HEADER
#endif // !__FENNIX_KERNEL_DRIVER_H__

View File

@ -31,6 +31,24 @@
static_assert(DTTOIF(DT_FIFO) == S_IFIFO);
static_assert(IFTODT(S_IFCHR) == DT_CHR);
/**
* This macro is used to check if a filesystem operation is available.
*
* TL;DR
*
* @code
* if FileSystemInfo.Ops.op == nullptr
* return -err
* else
* return FileSystemInfo.Ops.op(this->Node, ...);
* @endcode
*
* @param op The operation to check.
* @param err The error to return if the operation is not available.
* @param ... The arguments to pass to the operation.
*
* @return The result of the operation.
*/
#define __check_op(op, err, ...) \
if (fsi->Ops.op == nullptr) \
return -err; \

View File

@ -103,6 +103,7 @@ typedef union
uint8_t Raw;
} PS2_OUTPUT_PORT;
#ifndef __kernel__
void PIC_EOI(uint8_t IRQ);
void IRQ_MASK(uint8_t IRQ);
void IRQ_UNMASK(uint8_t IRQ);
@ -114,11 +115,11 @@ uint8_t PS2ReadStatus();
uint8_t PS2ReadAfterACK();
void PS2ClearOutputBuffer();
int PS2ACKTimeout();
#endif // !__kernel__
#define WaitOutput PS2Wait(DriverID, true)
#define WaitInput PS2Wait(DriverID, false)
#define PS2_KBD_CMD_SET_LEDS 0xED
#define PS2_KBD_CMD_ECHO 0xEE
#define PS2_KBD_CMD_SCAN_CODE_SET 0xF0
@ -189,7 +190,6 @@ typedef union
uint8_t Raw;
} PS2_KBD_TYPEMATIC;
#define PS2_MOUSE_CMD_SET_SCALING_1_1 0xE6
#define PS2_MOUSE_CMD_SET_SCALING_2_1 0xE7
#define PS2_MOUSE_CMD_SET_RESOLUTION 0xE8

View File

@ -69,7 +69,10 @@ typedef enum
BLOCK_TYPE_FLOPPY = DEVICE_TYPE_BLOCK + 128,
} DeviceType;
#ifndef __kernel__
EXTERNC dev_t CreateDeviceFile(const char *name, mode_t mode, const struct InodeOperations *Operations);
EXTERNC dev_t RegisterDevice(DeviceType Type, const struct InodeOperations *Operations);
EXTERNC int UnregisterDevice(dev_t Device);
#endif // !__kernel__
#endif // !__FENNIX_API_DEVICE_H__

View File

@ -377,7 +377,9 @@ struct FileSystemInfo
void *PrivateData;
} __attribute__((packed));
#ifndef __kernel__
dev_t RegisterFileSystem(struct FileSystemInfo *Info, struct Inode *Root);
int UnregisterFileSystem(dev_t Device);
#endif // !__kernel__
#endif // !__FENNIX_API_FILESYSTEM_H__

View File

@ -239,6 +239,8 @@ typedef struct
};
} InputReport;
#ifndef __kernel__
EXTERNC int ReportInputEvent(InputReport *Report);
#endif // !__kernel__
#endif // !__FENNIX_API_INPUT_H__

View File

@ -171,11 +171,13 @@ extern "C"
{
#endif
#ifndef __kernel__
PCIArray *GetPCIDevices(uint16_t Vendors[], uint16_t Devices[]);
void InitializePCI(PCIDevice *Device);
uint32_t GetBAR(uint8_t Index, PCIDevice *Device);
uint8_t iLine(PCIDevice *Device);
uint8_t iPin(PCIDevice *Device);
#endif // !__kernel__
#ifdef __cplusplus
}

View File

@ -531,6 +531,28 @@ static_assert((int)__SYS_SIG_IGN == (int)___SYS_SIG_IGN, "SIG_IGN values do not
typedef int __SYS_clockid_t;
typedef unsigned int __SYS_socklen_t;
typedef struct FramebufferScreenInfo
{
__UINT32_TYPE__ Width;
__UINT32_TYPE__ Height;
__UINT32_TYPE__ Pitch;
__UINT32_TYPE__ Bpp;
__UINT32_TYPE__ Size;
} FramebufferScreenInfo;
/**
* @brief Get framebuffer screen info
*
* @code
* struct FramebufferScreenInfo info;
* int ioctl(fd, FBIOGET_SCREEN_INFO, &info);
* @endcode
*
* @param fd File descriptor of the framebuffer device
* @param info Pointer to the framebuffer screen info structure
*/
#define FBIOGET_SCREEN_INFO 0xf0
/**
* @brief List of syscalls
*

View File

@ -232,6 +232,7 @@ namespace PCI
public:
std::list<PCIDevice> GetDevices() { return Devices; }
void MapPCIAddresses(PCIDevice Device, Memory::PageTable *Table);
void InitializeDevice(PCIDevice Device, Memory::PageTable *Table);
void EnumerateFunction(uint64_t DeviceAddress, uint32_t Function, PCIDevice dev);
void EnumerateDevice(uint64_t BusAddress, uint32_t Device, PCIDevice dev);
void EnumerateBus(uint64_t BaseAddress, uint32_t Bus, PCIDevice dev);

View File

@ -1013,7 +1013,7 @@ namespace std
_size = Traits::length(s);
_capacity = _size + 1;
_data = _alloc.allocate(_capacity);
memcpy(_data, s, _size);
memcpy(_data, s, _size > 0 ? _size : 1);
_data[_size] = '\0';
return *this;
}

View File

@ -33,11 +33,12 @@
int SpawnInit()
{
const char *envp[5] = {
const char *envp[6] = {
"PATH=/bin:/usr/bin",
"TERM=tty",
"HOME=/root",
"USER=root",
"TZ=UTC",
nullptr};
const char *argv[4] = {

View File

@ -398,10 +398,7 @@ uintptr_t HandleNativeSyscalls(SysFrm *Frame)
}
SyscallData sc = scTbl[Frame->ReturnValue()];
uintptr_t (*call)(SysFrm *, uintptr_t, ...) =
r_cst(uintptr_t(*)(SysFrm *, uintptr_t, ...),
sc.Handler);
uintptr_t (*call)(SysFrm *, uintptr_t, ...) = r_cst(uintptr_t (*)(SysFrm *, uintptr_t, ...), sc.Handler);
if (unlikely(!call))
{
@ -410,16 +407,15 @@ uintptr_t HandleNativeSyscalls(SysFrm *Frame)
return -ENOSYS;
}
debug("> [%d:\"%s\"]( %#lx %#lx %#lx %#lx %#lx %#lx )",
Frame->ReturnValue(), sc.Name,
Frame->Arg0(), Frame->Arg1(), Frame->Arg2(),
Frame->Arg3(), Frame->Arg4(), Frame->Arg5());
uintptr_t arg0 = Frame->Arg0();
uintptr_t arg1 = Frame->Arg1();
uintptr_t arg2 = Frame->Arg2();
uintptr_t arg3 = Frame->Arg3();
uintptr_t arg4 = Frame->Arg4();
uintptr_t arg5 = Frame->Arg5();
long sc_ret = call(Frame,
Frame->Arg0(), Frame->Arg1(), Frame->Arg2(),
Frame->Arg3(), Frame->Arg4(), Frame->Arg5());
debug("< [%d:\"%s\"] = %ld",
Frame->ReturnValue(), sc.Name, sc_ret);
return sc_ret;
debug("> [%d:\"%s\"]( %#lx %#lx %#lx %#lx %#lx %#lx )", Frame->ReturnValue(), sc.Name, arg0, arg1, arg2, arg3, arg4, arg5);
uintptr_t result = call(Frame, arg0, arg1, arg2, arg3, arg4, arg5);
debug("< [%d:\"%s\"] = %ld", Frame->ReturnValue(), sc.Name, result);
return result;
}

View File

@ -105,10 +105,10 @@ namespace KernelConsole
debug("string: \"%*s\"", Size, buf);
for (size_t i = 0; i < Size; i++)
{
if (this->TerminalConfig.c_lflag & ICANON)
this->Process(buf[i]);
else
this->Append(buf[i]);
// if (this->TerminalConfig.c_lflag & ICANON)
this->Process(buf[i]);
// else
// this->Append(buf[i]);
}
debug("ret %ld", Size);

View File

@ -49,10 +49,22 @@ void ReapZombies()
}
}
void HandleSignal(int signal)
{
if (signal == SIGTERM || signal == SIGINT)
{
printf("init: received termination signal, shutting down...\n");
exit(0);
}
}
int main()
{
printf("init starting...\n");
signal(SIGTERM, HandleSignal);
signal(SIGINT, HandleSignal);
char *shellArgs[] = {"/bin/sh", NULL};
StartProcess("/bin/sh", shellArgs);

View File

@ -13,9 +13,6 @@ WARNCFLAG = -Wall -Wextra
build: $(FILENAME).elf
cp $(FILENAME).elf $(WORKSPACE_DIR)/out/bin/$(FILENAME)
# Use static linking
LDFLAGS += -static -fno-pic -fno-pie -Wl,-static
$(FILENAME).elf: $(OBJ)
$(info Linking $@)
$(CC) $(LDFLAGS) $(SYSROOT) $(OBJ) -o $@

View File

@ -0,0 +1,119 @@
/*
This file is part of Fennix Userspace.
Fennix Userspace is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Userspace is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Userspace. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <sys/utsname.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
void print_usage()
{
printf("Usage: uname [OPTION]...\n");
printf("Display specific system information. With no OPTION, defaults to -s.\n\n");
printf(" -a, --all display all information, in the following order,\n");
printf(" except omit -p and -i if unknown:\n");
printf(" -s, --kernel-name display the kernel name\n");
printf(" -n, --nodename display the network node hostname\n");
printf(" -r, --kernel-release display the kernel release\n");
printf(" -v, --kernel-version display the kernel version\n");
printf(" -m, --machine display the machine hardware name\n");
printf(" -p, --processor display the processor type (non-portable)\n");
printf(" -i, --hardware-platform display the hardware platform (non-portable)\n");
printf(" -o, --operating-system display the operating system\n");
printf(" --help show this help message and exit\n");
}
int main(int argc, char *argv[])
{
struct utsname buffer;
if (uname(&buffer) != 0)
{
perror("uname");
exit(EXIT_FAILURE);
}
bool print_all = false;
bool print_kernel_name = false;
bool print_nodename = false;
bool print_kernel_release = false;
bool print_kernel_version = false;
bool print_machine = false;
bool print_processor = false;
bool print_hardware_platform = false;
bool print_operating_system = false;
if (argc == 1)
print_kernel_name = true;
else
{
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--all") == 0)
{
print_all = true;
break;
}
else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--kernel-name") == 0)
print_kernel_name = true;
else if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--nodename") == 0)
print_nodename = true;
else if (strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--kernel-release") == 0)
print_kernel_release = true;
else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--kernel-version") == 0)
print_kernel_version = true;
else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--machine") == 0)
print_machine = true;
else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--processor") == 0)
print_processor = true;
else if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--hardware-platform") == 0)
print_hardware_platform = true;
else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--operating-system") == 0)
print_operating_system = true;
else if (strcmp(argv[i], "--help") == 0)
{
print_usage();
exit(EXIT_SUCCESS);
}
else
{
fprintf(stderr, "uname: invalid option -- '%s'\n", argv[i]);
print_usage();
exit(EXIT_FAILURE);
}
}
}
if (print_all || print_kernel_name)
printf("%s ", buffer.sysname);
if (print_all || print_nodename)
printf("%s ", buffer.nodename);
if (print_all || print_kernel_release)
printf("%s ", buffer.release);
if (print_all || print_kernel_version)
printf("%s ", buffer.version);
if (print_all || print_machine)
printf("%s ", buffer.machine);
if (print_all || print_processor)
printf("%s ", buffer.machine); /* FIXME */
if (print_all || print_hardware_platform)
printf("%s ", buffer.machine); /* FIXME */
if (print_all || print_operating_system)
printf("%s ", buffer.sysname); /* FIXME */
printf("\n");
return 0;
}

View File

@ -0,0 +1,19 @@
FROM mcr.microsoft.com/devcontainers/cpp:ubuntu-24.04
ARG REINSTALL_CMAKE_VERSION_FROM_SOURCE="none"
# Optionally install the cmake for vcpkg
COPY ./reinstall-cmake.sh /tmp/
RUN if [ "${REINSTALL_CMAKE_VERSION_FROM_SOURCE}" != "none" ]; then \
chmod +x /tmp/reinstall-cmake.sh && /tmp/reinstall-cmake.sh ${REINSTALL_CMAKE_VERSION_FROM_SOURCE}; \
fi \
&& rm -f /tmp/reinstall-cmake.sh
# [Optional] Uncomment this section to install additional vcpkg ports.
# RUN su vscode -c "${VCPKG_ROOT}/vcpkg install <your-port-name-here>"
# [Optional] Uncomment this section to install additional packages.
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends build-essential make gdb git

View File

@ -0,0 +1,41 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/cpp
{
"name": "libc test container",
"build": {
"dockerfile": "Dockerfile"
},
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
"settings": {},
"extensions": [
"ms-vsliveshare.vsliveshare",
"ms-azuretools.vscode-docker",
"ms-vscode.cpptools",
"ms-vscode.makefile-tools",
"vivaxy.vscode-conventional-commits",
"EditorConfig.EditorConfig",
"eamodio.gitlens",
"ms-vscode.hexeditor"
]
}
}
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "gcc -v",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

View File

@ -0,0 +1,60 @@
#!/usr/bin/env bash
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------
#
set -e
CMAKE_VERSION=${1:-"none"}
if [ "${CMAKE_VERSION}" = "none" ]; then
echo "No CMake version specified, skipping CMake reinstallation"
exit 0
fi
# Cleanup temporary directory and associated files when exiting the script.
cleanup() {
EXIT_CODE=$?
set +e
if [[ -n "${TMP_DIR}" ]]; then
echo "Executing cleanup of tmp files"
rm -Rf "${TMP_DIR}"
fi
exit $EXIT_CODE
}
trap cleanup EXIT
echo "Installing CMake..."
apt-get -y purge --auto-remove cmake
mkdir -p /opt/cmake
architecture=$(dpkg --print-architecture)
case "${architecture}" in
arm64)
ARCH=aarch64 ;;
amd64)
ARCH=x86_64 ;;
*)
echo "Unsupported architecture ${architecture}."
exit 1
;;
esac
CMAKE_BINARY_NAME="cmake-${CMAKE_VERSION}-linux-${ARCH}.sh"
CMAKE_CHECKSUM_NAME="cmake-${CMAKE_VERSION}-SHA-256.txt"
TMP_DIR=$(mktemp -d -t cmake-XXXXXXXXXX)
echo "${TMP_DIR}"
cd "${TMP_DIR}"
curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_BINARY_NAME}" -O
curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_CHECKSUM_NAME}" -O
sha256sum -c --ignore-missing "${CMAKE_CHECKSUM_NAME}"
sh "${TMP_DIR}/${CMAKE_BINARY_NAME}" --prefix=/opt/cmake --skip-license
ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake
ln -s /opt/cmake/bin/ctest /usr/local/bin/ctest

View File

@ -0,0 +1,19 @@
# EditorConfig is awesome: https://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = tab
indent_size = 4
[Makefile]
indent_style = tab
[*.json]
insert_final_newline = false
[compose.yaml]
indent_style = space

View File

@ -0,0 +1,2 @@
*.o
*.elf

View File

@ -1,6 +1,11 @@
{
"libc_test stub macro": {
"prefix": "zxc",
"body": "int test_$TM_FILENAME_BASE() { return 2; }"
"body": [
"int test_$TM_FILENAME_BASE()",
"{",
"\treturn 2;",
"}",
]
},
}

View File

@ -0,0 +1,30 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/libc_test_n.elf",
"targetArchitecture": "x64",
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb",
"internalConsoleOptions": "neverOpen",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "run-make"
}
]
}

View File

@ -0,0 +1,26 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "run-make",
"type": "shell",
"command": "make build_native",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "never",
"panel": "shared"
},
"options": {
"shell": {
"executable": "bash",
"args": [
"-c"
]
}
}
}
]
}

View File

@ -10,6 +10,9 @@
"git.alwaysSignOff": true,
"git.defaultBranchName": "master",
"git.openRepositoryInParentFolders": "always",
"C_Cpp.autoAddFileAssociations": false
"C_Cpp.autoAddFileAssociations": false,
"conventionalCommits.scopes": [
"userspace/apps/test/libc_test"
]
}
}

View File

@ -14,12 +14,24 @@ build: $(FILENAME).elf
cp $(FILENAME).elf $(WORKSPACE_DIR)/out/bin/$(FILENAME)
# Use static linking
LDFLAGS += -static -fno-pic -fno-pie -Wl,-static
LDFLAGS += -ggdb3 -g -static -fno-pic -fno-pie -Wl,-static -Wl,-Ttext=0x00600000
CFLAGS += -ggdb3 -g -O0 -fdiagnostics-color=always
# Use default toolchain if not specified
CC ?= gcc
CXX ?= g++
AS ?= as
GDBSERVER ?= /usr/bin/gdbserver
.PHONY: build_native run_native debug_native log_docker
log_docker:
# docker compose logs -f libc_test
debug_native:
$(MAKE) --quiet clean
$(MAKE) --quiet -j $(shell nproc) build_native
$(GDBSERVER) 0.0.0.0:9229 $(CURDIR)/$(FILENAME)_n.elf
build_native: $(FILENAME)_n.elf
@ -29,11 +41,11 @@ run_native:
$(FILENAME)_n.elf $(FILENAME).elf: $(OBJ)
$(info Linking $@)
$(CC) $(LDFLAGS) $(SYSROOT) $(OBJ) -o $@
$(CC) $(LDFLAGS) $(SYSROOT) $(OBJ) -o $@ -lm
%.o: %.c $(HEADERS)
$(info Compiling $<)
$(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@
$(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ -lm
%.o: %.cpp $(HEADERS)
$(info Compiling $<)

Some files were not shown because too many files have changed in this diff Show More