Compare commits
No commits in common. "alpha" and "weblog-invisible-stuff-i" have entirely different histories.
alpha
...
weblog-inv
90 changed files with 532 additions and 6663 deletions
|
|
@ -3,8 +3,4 @@
|
||||||
-I
|
-I
|
||||||
dependencies/limine
|
dependencies/limine
|
||||||
-I
|
-I
|
||||||
dependencies/flanterm/src
|
|
||||||
-I
|
|
||||||
include
|
include
|
||||||
-D
|
|
||||||
CALCITE_DEBUG
|
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
root://calcite/apps/terminal/terminal.elf
|
|
||||||
Binary file not shown.
|
|
@ -2,6 +2,7 @@ timeout: 0
|
||||||
quiet: yes
|
quiet: yes
|
||||||
|
|
||||||
/Calcite
|
/Calcite
|
||||||
protocol: limine
|
protocol: limine
|
||||||
path: boot():/calcite/kernel.elf
|
path: boot():/calcite/kernel.elf
|
||||||
cmdline: root-drive=pata0,root-fs=iso9660
|
module_path: boot():/calcite/initfs.tar
|
||||||
|
module_cmdline: initfs
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
LIMINE_VERSION="9.3.4"
|
LIMINE_VERSION="9.3.4"
|
||||||
FLANTERM_VERSION="2.1.0"
|
|
||||||
|
|
||||||
if [ -e dependencies ]; then
|
if [ -e dependencies ]; then
|
||||||
echo dependencies directory already exists.
|
echo dependencies directory already exists.
|
||||||
|
|
@ -13,10 +12,7 @@ set -e
|
||||||
mkdir dependencies
|
mkdir dependencies
|
||||||
|
|
||||||
cd dependencies
|
cd dependencies
|
||||||
curl -L https://codeberg.org/Limine/Limine/archive/v${LIMINE_VERSION}-binary.tar.gz | tar xz
|
curl -L https://github.com/limine-bootloader/limine/archive/refs/tags/v${LIMINE_VERSION}-binary.tar.gz | tar xz
|
||||||
|
mv limine-${LIMINE_VERSION}-binary limine
|
||||||
make -C limine
|
make -C limine
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
cd dependencies
|
|
||||||
curl -L https://codeberg.org/Mintsuki/Flanterm/archive/v${FLANTERM_VERSION}.tar.gz | tar xz
|
|
||||||
cd ..
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Calcite, include/kernel-public/framebuffer.h
|
/* Calcite, include/calcite/calcite.h
|
||||||
* Copyright 2025 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -19,9 +19,13 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
[[noreturn]] void end_thread();
|
||||||
|
|
||||||
struct framebuffer_info {
|
struct framebuffer_info {
|
||||||
uint8_t *fb_base;
|
uint8_t *fb_base;
|
||||||
int fb_width;
|
int fb_width;
|
||||||
int fb_height;
|
int fb_height;
|
||||||
int fb_pitch;
|
int fb_pitch;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void map_framebuffer(struct framebuffer_info *info_out);
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
/* Calcite, include/calcite/dispatch.h
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
struct dispatch_queue;
|
|
||||||
|
|
||||||
struct dispatch_queue *create_dispatch_queue();
|
|
||||||
void dispatch(struct dispatch_queue *queue, void (*callback)(void *x), void *x);
|
|
||||||
[[noreturn]] void dispatch_loop(struct dispatch_queue *queue);
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
/* Calcite, include/calcite/file-streams.h
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <kernel-public/files.h>
|
|
||||||
|
|
||||||
struct file_stream;
|
|
||||||
|
|
||||||
enum fs_access_result open_file_stream(const char *path, struct file_stream **file_stream_out);
|
|
||||||
void close_file_stream(struct file_stream *file_stream);
|
|
||||||
|
|
||||||
enum fs_access_result read_file_stream(struct file_stream *file_stream, void *buffer, uint64_t bytes);
|
|
||||||
|
|
||||||
//returns next byte casted to int. on error or eof, returns -1.
|
|
||||||
int read_file_stream_byte(struct file_stream *file_stream);
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
/* Calcite, include/calcite/format.h
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
//does not write null terminator. buffer must be at least digit_count bytes long.
|
|
||||||
void to_hex(uint64_t value, int digit_count, char *buffer);
|
|
||||||
|
|
||||||
//buffer must be at least digit_count bytes long.
|
|
||||||
uint64_t from_hex(int digit_count, char *buffer);
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
/* Calcite, include/calcite/memory.h
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
void *heap_alloc(uint64_t bytes);
|
|
||||||
|
|
||||||
//start should be something returned from heap_alloc,
|
|
||||||
//unless this is called (carefully) from inside memory.c
|
|
||||||
void heap_dealloc(void *start, uint64_t bytes);
|
|
||||||
|
|
||||||
void memcpy(void *to, const void *from, uint64_t bytes);
|
|
||||||
void memset(void *start, uint8_t value, uint64_t length);
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
/* Calcite, include/calcite/syscalls.h
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <kernel-public/framebuffer.h>
|
|
||||||
#include <kernel-public/process.h>
|
|
||||||
#include <kernel-public/files.h>
|
|
||||||
#include <kernel-public/input.h>
|
|
||||||
#include <kernel-public/sync.h>
|
|
||||||
#include <kernel-public/ipc.h>
|
|
||||||
|
|
||||||
[[noreturn]] void end_thread();
|
|
||||||
|
|
||||||
void map_framebuffer(struct framebuffer_info *info_out);
|
|
||||||
|
|
||||||
enum fs_access_result open_file(const char *path, file_handle_t *handle_out);
|
|
||||||
void close_file(file_handle_t handle);
|
|
||||||
|
|
||||||
enum fs_access_result get_file_size(file_handle_t handle, uint64_t *bytes_out);
|
|
||||||
|
|
||||||
enum fs_access_result read_file(struct read_file_parameter *parameter);
|
|
||||||
enum fs_access_result read_file_splat(file_handle_t handle, void *buffer, uint64_t start, uint64_t bytes);
|
|
||||||
|
|
||||||
void wait_for_mouse_packet(struct mouse_packet *packet_out);
|
|
||||||
int wait_for_keyboard_packet();
|
|
||||||
|
|
||||||
void *map_pages(uint64_t count);
|
|
||||||
|
|
||||||
void sleep_ms(uint64_t ms_to_sleep);
|
|
||||||
|
|
||||||
//1 on success, 0 on failure.
|
|
||||||
int start_elf(const char *path, const struct process_start_info *info);
|
|
||||||
|
|
||||||
enum ipc_dgram_result ipc_create_dgram_receiver(const char *address, ipc_dgram_receiver_handle_t *handle_out);
|
|
||||||
enum ipc_dgram_result ipc_create_dgram_sender(const char *address, ipc_dgram_sender_handle_t *handle_out);
|
|
||||||
|
|
||||||
//on entry, bytes is maximum accepted packet length.
|
|
||||||
//on exit, if result was IPR_SUCCESS or IPR_TOO_BIG, bytes is actual packet length.
|
|
||||||
//actual packet length will always be positive.
|
|
||||||
enum ipc_dgram_result ipc_receive_dgram(ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes);
|
|
||||||
|
|
||||||
//bytes must be positive.
|
|
||||||
enum ipc_dgram_result ipc_send_dgram(ipc_dgram_sender_handle_t handle, const void *data, int bytes);
|
|
||||||
|
|
||||||
//returned id is global across processes. only one receiver and one sender can be created per pipe.
|
|
||||||
uint64_t ipc_create_private_dgram_pipe();
|
|
||||||
enum ipc_dgram_result ipc_create_private_dgram_receiver(uint64_t id, ipc_dgram_receiver_handle_t *handle_out);
|
|
||||||
enum ipc_dgram_result ipc_create_private_dgram_sender(uint64_t id, ipc_dgram_sender_handle_t *handle_out);
|
|
||||||
|
|
||||||
//f should not return
|
|
||||||
void create_thread(void (*f)(uint64_t x), uint64_t x);
|
|
||||||
|
|
||||||
//value_space must be positive at entry.
|
|
||||||
//looks up the environment variable with this key. if there is no such
|
|
||||||
//variable, sets value_space to 0. otherwise, sets value_space to the
|
|
||||||
//length of the value including null terminator (which will be positive),
|
|
||||||
//and if that is at most what value_space was set to at entry, also
|
|
||||||
//copies the value including null terminator to value_out.
|
|
||||||
void get_envvar(const char *key, char *value_out, int *value_space);
|
|
||||||
|
|
||||||
mutex_handle_t create_mutex();
|
|
||||||
void acquire_mutex(mutex_handle_t handle);
|
|
||||||
void release_mutex(mutex_handle_t handle);
|
|
||||||
|
|
||||||
event_handle_t create_event();
|
|
||||||
void signal_event(event_handle_t handle);
|
|
||||||
void wait_event(event_handle_t handle);
|
|
||||||
|
|
||||||
uint64_t get_ms_since_boot();
|
|
||||||
void sleep_until_ms_since_boot(uint64_t ms);
|
|
||||||
|
|
||||||
//vma should be page-aligned. writable and executable should be 1 or 0.
|
|
||||||
void change_page_permissions(void *vma, int writable, int executable);
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
/* Calcite, include/calcite/terminal.h
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
void init_terminal();
|
|
||||||
|
|
||||||
void terminal_write(const void *data, int bytes);
|
|
||||||
void terminal_flush();
|
|
||||||
|
|
||||||
enum terminal_key_flags {
|
|
||||||
TKF_LEFT_SHIFT = 0x20000,
|
|
||||||
TKF_RIGHT_SHIFT = 0x40000,
|
|
||||||
TKF_LEFT_CTRL = 0x80000,
|
|
||||||
TKF_RIGHT_CTRL = 0x100000,
|
|
||||||
TKF_LEFT_ALT = 0x200000,
|
|
||||||
TKF_RIGHT_ALT = 0x400000,
|
|
||||||
TKF_CAPS_LOCK = 0x800000,
|
|
||||||
TKF_NUM_LOCK = 0x1000000
|
|
||||||
};
|
|
||||||
|
|
||||||
//0x000000ff is printable character, if any, otherwise 0.
|
|
||||||
// this includes space, newline, and tab, but not delete or backspace.
|
|
||||||
// control, alt, and make are ignored, but shift, caps lock, and num lock are considered.
|
|
||||||
//0x0001ff00 is result of syscall_wait_for_keyboard_packet, shifted left.
|
|
||||||
//0x01fe0000 is flags as above.
|
|
||||||
int terminal_readkey();
|
|
||||||
|
|
||||||
//has null terminator, no newline. returned pointer is invalidated by next call.
|
|
||||||
const char *terminal_readline();
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
/* Calcite, include/kernel-public/files.h
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
enum fs_access_result {
|
|
||||||
FAR_SUCCESS,
|
|
||||||
FAR_HARDWARE_ERROR,
|
|
||||||
FAR_FORMAT_ERROR,
|
|
||||||
FAR_UNKNOWN_FS_TYPE,
|
|
||||||
FAR_NOT_FOUND,
|
|
||||||
FAR_OUT_OF_BOUNDS,
|
|
||||||
FAR_BAD_HANDLE
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef uint64_t file_handle_t;
|
|
||||||
|
|
||||||
struct read_file_parameter {
|
|
||||||
file_handle_t handle;
|
|
||||||
void *buffer;
|
|
||||||
//in bytes
|
|
||||||
uint64_t start;
|
|
||||||
uint64_t bytes;
|
|
||||||
};
|
|
||||||
|
|
@ -1,153 +0,0 @@
|
||||||
/* Calcite, include/kernel-public/input.h
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
struct mouse_packet {
|
|
||||||
int x_change;
|
|
||||||
int y_change;
|
|
||||||
};
|
|
||||||
|
|
||||||
//named after qwerty value of key
|
|
||||||
enum key_value {
|
|
||||||
KEY_F9 = 0x01,
|
|
||||||
KEY_F5 = 0x03,
|
|
||||||
KEY_F3 = 0x04,
|
|
||||||
KEY_F1 = 0x05,
|
|
||||||
KEY_F2 = 0x06,
|
|
||||||
KEY_F12 = 0x07,
|
|
||||||
KEY_F10 = 0x09,
|
|
||||||
KEY_F8 = 0x0a,
|
|
||||||
KEY_F6 = 0x0b,
|
|
||||||
KEY_F4 = 0x0c,
|
|
||||||
KEY_TAB = 0x0d,
|
|
||||||
KEY_GRAVE = 0x0e,
|
|
||||||
KEY_LEFT_ALT = 0x11,
|
|
||||||
KEY_LEFT_SHIFT = 0x12,
|
|
||||||
KEY_LEFT_CTRL = 0x14,
|
|
||||||
KEY_Q = 0x15,
|
|
||||||
KEY_1 = 0x16,
|
|
||||||
KEY_Z = 0x1a,
|
|
||||||
KEY_S = 0x1b,
|
|
||||||
KEY_A = 0x1c,
|
|
||||||
KEY_W = 0x1d,
|
|
||||||
KEY_2 = 0x1e,
|
|
||||||
KEY_C = 0x21,
|
|
||||||
KEY_X = 0x22,
|
|
||||||
KEY_D = 0x23,
|
|
||||||
KEY_E = 0x24,
|
|
||||||
KEY_4 = 0x25,
|
|
||||||
KEY_3 = 0x26,
|
|
||||||
KEY_SPACE = 0x29,
|
|
||||||
KEY_V = 0x2a,
|
|
||||||
KEY_F = 0x2b,
|
|
||||||
KEY_T = 0x2c,
|
|
||||||
KEY_R = 0x2d,
|
|
||||||
KEY_5 = 0x2e,
|
|
||||||
KEY_N = 0x31,
|
|
||||||
KEY_B = 0x32,
|
|
||||||
KEY_H = 0x33,
|
|
||||||
KEY_G = 0x34,
|
|
||||||
KEY_Y = 0x35,
|
|
||||||
KEY_6 = 0x36,
|
|
||||||
KEY_M = 0x3a,
|
|
||||||
KEY_J = 0x3b,
|
|
||||||
KEY_U = 0x3c,
|
|
||||||
KEY_7 = 0x3d,
|
|
||||||
KEY_8 = 0x3e,
|
|
||||||
KEY_COMMA = 0x41,
|
|
||||||
KEY_K = 0x42,
|
|
||||||
KEY_I = 0x43,
|
|
||||||
KEY_O = 0x44,
|
|
||||||
KEY_0 = 0x45,
|
|
||||||
KEY_9 = 0x46,
|
|
||||||
KEY_PERIOD = 0x49,
|
|
||||||
KEY_SLASH = 0x4a,
|
|
||||||
KEY_L = 0x4b,
|
|
||||||
KEY_SEMICOLON = 0x4c,
|
|
||||||
KEY_P = 0x4d,
|
|
||||||
KEY_MINUS = 0x4e,
|
|
||||||
KEY_APOSTRAPHE = 0x52,
|
|
||||||
KEY_OPEN_BRACKET = 0x54,
|
|
||||||
KEY_EQUALS = 0x55,
|
|
||||||
KEY_CAPS_LOCK = 0x58,
|
|
||||||
KEY_RIGHT_SHIFT = 0x59,
|
|
||||||
KEY_ENTER = 0x5a,
|
|
||||||
KEY_CLOSE_BRACKET = 0x5b,
|
|
||||||
KEY_BACKSLASH = 0x5d,
|
|
||||||
KEY_BACKSPACE = 0x66,
|
|
||||||
KEY_NUM_1 = 0x69,
|
|
||||||
KEY_NUM_4 = 0x6b,
|
|
||||||
KEY_NUM_7 = 0x6c,
|
|
||||||
KEY_NUM_0 = 0x70,
|
|
||||||
KEY_NUM_PERIOD = 0x71,
|
|
||||||
KEY_NUM_2 = 0x72,
|
|
||||||
KEY_NUM_5 = 0x73,
|
|
||||||
KEY_NUM_6 = 0x74,
|
|
||||||
KEY_NUM_8 = 0x75,
|
|
||||||
KEY_ESCAPE = 0x76,
|
|
||||||
KEY_NUM_LOCK = 0x77,
|
|
||||||
KEY_F11 = 0x78,
|
|
||||||
KEY_NUM_PLUS = 0x79,
|
|
||||||
KEY_NUM_3 = 0x7a,
|
|
||||||
KEY_NUM_MINUS = 0x7b,
|
|
||||||
KEY_NUM_TIMES = 0x7c,
|
|
||||||
KEY_NUM_9 = 0x7d,
|
|
||||||
KEY_SCROLL_LOCK = 0x7e,
|
|
||||||
KEY_F7 = 0x83,
|
|
||||||
KEY_WEB_SEARCH = 0x90,
|
|
||||||
KEY_RIGHT_ALT = 0x91,
|
|
||||||
KEY_RIGHT_CTRL = 0x94,
|
|
||||||
KEY_MEDIA_PREVIOUS = 0x95,
|
|
||||||
KEY_WEB_FAVORITES = 0x98,
|
|
||||||
KEY_LEFT_GUI = 0x9f,
|
|
||||||
KEY_WEB_REFRESH = 0xa0,
|
|
||||||
KEY_MEDIA_VOLUME_DOWN = 0xa1,
|
|
||||||
KEY_MEDIA_MUTE = 0xa3,
|
|
||||||
KEY_RIGHT_GUI = 0xa7,
|
|
||||||
KEY_WEB_STOP = 0xa8,
|
|
||||||
KEY_CALCULATOR = 0xab,
|
|
||||||
KEY_MENU = 0xaf,
|
|
||||||
KEY_WEB_FORWARD = 0xb0,
|
|
||||||
KEY_MEDIA_VOLUME_UP = 0xb2,
|
|
||||||
KEY_MEDIA_PAUSE = 0xb4,
|
|
||||||
KEY_ACPI_POWER = 0xb7,
|
|
||||||
KEY_WEB_BACK = 0xb8,
|
|
||||||
KEY_WEB_HOME = 0xba,
|
|
||||||
KEY_MEDIA_STOP = 0xbb,
|
|
||||||
KEY_ACPI_SLEEP = 0xbf,
|
|
||||||
KEY_COMPUTER = 0xc0,
|
|
||||||
KEY_EMAIL = 0xc8,
|
|
||||||
KEY_NUM_DIVIDE = 0xca,
|
|
||||||
KEY_MEDIA_NEXT = 0xcd,
|
|
||||||
KEY_MEDIA_SELECT = 0xd0,
|
|
||||||
KEY_NUM_ENTER = 0xda,
|
|
||||||
KEY_ACPI_WAKE = 0xde,
|
|
||||||
KEY_END = 0xe9,
|
|
||||||
KEY_LEFT = 0xeb,
|
|
||||||
KEY_HOME = 0xec,
|
|
||||||
KEY_INSERT = 0xf0,
|
|
||||||
KEY_DELETE = 0xf1,
|
|
||||||
KEY_DOWN = 0xf2,
|
|
||||||
KEY_RIGHT = 0xf4,
|
|
||||||
KEY_UP = 0xf5,
|
|
||||||
KEY_PAGE_DOWN = 0xfa,
|
|
||||||
KEY_PAGE_UP = 0xfd,
|
|
||||||
KEY_PRINT_SCREEN = 0xfe,
|
|
||||||
KEY_PAUSE_BREAK = 0xff,
|
|
||||||
KEY_FLAG_MAKE = 0x100
|
|
||||||
};
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
/* Calcite, include/kernel-public/process.h
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
struct process_start_info {
|
|
||||||
|
|
||||||
//keys of envvars to copy to new process.
|
|
||||||
const char **forwared_envvars;
|
|
||||||
int forwared_envvar_count;
|
|
||||||
|
|
||||||
//keys and values to set in new process.
|
|
||||||
//entry i has key set_envvars[i * 2] and value set_envvars[i * 2 + 1].
|
|
||||||
//set_envvar_count is number of envvars, not twice that.
|
|
||||||
const char **set_envvars;
|
|
||||||
int set_envvar_count;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
/* Calcite, include/kernel-public/sync.h
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
typedef uint64_t mutex_handle_t;
|
|
||||||
typedef uint64_t event_handle_t;
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, include/kernel-public/syscall-numbers.h
|
/* Calcite, include/kernel-public/syscall-numbers.h
|
||||||
* Copyright 2025-2026 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -19,32 +19,5 @@
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
SYSCALL_END_THREAD,
|
SYSCALL_END_THREAD,
|
||||||
SYSCALL_MAP_FRAMEBUFFER,
|
SYSCALL_MAP_FRAMEBUFFER
|
||||||
SYSCALL_OPEN_FILE,
|
|
||||||
SYSCALL_CLOSE_FILE,
|
|
||||||
SYSCALL_GET_FILE_SIZE,
|
|
||||||
SYSCALL_READ_FILE,
|
|
||||||
SYSCALL_WAIT_FOR_MOUSE_PACKET,
|
|
||||||
SYSCALL_MAP_PAGES,
|
|
||||||
SYSCALL_SLEEP_MS,
|
|
||||||
SYSCALL_START_ELF,
|
|
||||||
SYSCALL_IPC_CREATE_DGRAM_RECEIVER,
|
|
||||||
SYSCALL_IPC_CREATE_DGRAM_SENDER,
|
|
||||||
SYSCALL_IPC_RECEIVE_DGRAM,
|
|
||||||
SYSCALL_IPC_SEND_DGRAM,
|
|
||||||
SYSCALL_CREATE_THREAD,
|
|
||||||
SYSCALL_GET_ENVVAR,
|
|
||||||
SYSCALL_CREATE_MUTEX,
|
|
||||||
SYSCALL_ACQUIRE_MUTEX,
|
|
||||||
SYSCALL_RELEASE_MUTEX,
|
|
||||||
SYSCALL_CREATE_EVENT,
|
|
||||||
SYSCALL_SIGNAL_EVENT,
|
|
||||||
SYSCALL_WAIT_EVENT,
|
|
||||||
SYSCALL_IPC_CREATE_PRIVATE_DGRAM_PIPE,
|
|
||||||
SYSCALL_IPC_CREATE_PRIVATE_DGRAM_RECEIVER,
|
|
||||||
SYSCALL_IPC_CREATE_PRIVATE_DGRAM_SENDER,
|
|
||||||
SYSCALL_GET_MS_SINCE_BOOT,
|
|
||||||
SYSCALL_SLEEP_UNTIL_MS_SINCE_BOOT,
|
|
||||||
SYSCALL_CHANGE_PAGE_PERMISSIONS,
|
|
||||||
SYSCALL_WAIT_FOR_KEYBOARD_PACKET
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
/* Calcite, include/silver/pam.h
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <silver/image.h>
|
|
||||||
|
|
||||||
//returns 0 on failure, 1 on success.
|
|
||||||
int load_pam(const char *path, struct image **image_out);
|
|
||||||
|
|
@ -1,26 +1,10 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
COMMON_CC_EXTRA_FLAGS="-Wall -Wextra"
|
COMMON_CC_EXTRA_FLAGS="-O3 -Wall -Wextra"
|
||||||
COMMON_LD_EXTRA_FLAGS=""
|
|
||||||
|
|
||||||
COMMON_CC_FLAGS="-std=c23 -ffreestanding -I include ${COMMON_CC_EXTRA_FLAGS}"
|
COMMON_CC_FLAGS="-std=c23 -ffreestanding -I include ${COMMON_CC_EXTRA_FLAGS}"
|
||||||
COMMON_LD_FLAGS="${COMMON_LD_EXTRA_FLAGS}"
|
|
||||||
|
|
||||||
if [ "$1" = debug ]; then
|
|
||||||
COMMON_CC_FLAGS="-O0 -ggdb -D CALCITE_DEBUG ${COMMON_CC_FLAGS}"
|
|
||||||
elif [ "$1" = release ]; then
|
|
||||||
COMMON_CC_FLAGS="-O3 -D CALCITE_RELEASE ${COMMON_CC_FLAGS}"
|
|
||||||
COMMON_LD_FLAGS="-s ${COMMON_LD_FLAGS}"
|
|
||||||
else
|
|
||||||
echo pass either "debug" or "release" as an argument.
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
KERNEL_CC_FLAGS="-mno-sse -I dependencies/limine ${COMMON_CC_FLAGS}"
|
KERNEL_CC_FLAGS="-mno-sse -I dependencies/limine ${COMMON_CC_FLAGS}"
|
||||||
#in the future user code will be allowed to use sse
|
#in the future user code will be allowed to use sse
|
||||||
USER_CC_FLAGS="-mno-sse -I dependencies/flanterm/src ${COMMON_CC_FLAGS}"
|
USER_CC_FLAGS="-mno-sse ${COMMON_CC_FLAGS}"
|
||||||
|
|
||||||
FLANTERM_CC_FLAGS="-D FLANTERM_FB_DISABLE_BUMP_ALLOC ${USER_CC_FLAGS}"
|
|
||||||
|
|
||||||
if [ -e build.ninja ]; then
|
if [ -e build.ninja ]; then
|
||||||
echo build.ninja already exists.
|
echo build.ninja already exists.
|
||||||
|
|
@ -40,19 +24,15 @@ echo "rule user_cc" >> build.ninja
|
||||||
echo " depfile = \$out.d" >> build.ninja
|
echo " depfile = \$out.d" >> build.ninja
|
||||||
echo " command = cc -c -MD -MF \$out.d ${USER_CC_FLAGS} \$in -o \$out" >> build.ninja
|
echo " command = cc -c -MD -MF \$out.d ${USER_CC_FLAGS} \$in -o \$out" >> build.ninja
|
||||||
|
|
||||||
echo "rule flanterm_cc" >> build.ninja
|
|
||||||
echo " depfile = \$out.d" >> build.ninja
|
|
||||||
echo " command = cc -c -MD -MF \$out.d ${FLANTERM_CC_FLAGS} \$in -o \$out" >> build.ninja
|
|
||||||
|
|
||||||
echo "rule kernel_ld" >> build.ninja
|
echo "rule kernel_ld" >> build.ninja
|
||||||
echo " command = ld ${COMMON_LD_FLAGS} -T src/kernel/link.ld \$in -o \$out" >> build.ninja
|
echo " command = ld -T src/kernel/link.ld \$in -o \$out" >> build.ninja
|
||||||
|
|
||||||
echo "rule user_lib_ld" >> build.ninja
|
echo "rule user_lib_ld" >> build.ninja
|
||||||
echo " command = ld -r \$in -o \$out" >> build.ninja
|
echo " command = ld -r \$in -o \$out" >> build.ninja
|
||||||
|
|
||||||
#eventually maybe a libc will be linked in
|
#eventually maybe a libc will be linked in
|
||||||
echo "rule user_app_ld" >> build.ninja
|
echo "rule user_app_ld" >> build.ninja
|
||||||
echo " command = ld ${COMMON_LD_FLAGS} -T src/user-apps/link.ld \$in -o \$out" >> build.ninja
|
echo " command = ld \$in -o \$out" >> build.ninja
|
||||||
|
|
||||||
#builds everything in a directory
|
#builds everything in a directory
|
||||||
# $1 - source directory
|
# $1 - source directory
|
||||||
|
|
@ -84,47 +64,38 @@ build_all() {
|
||||||
|
|
||||||
build_all src/kernel kernel_cc kernel_ld kernel.elf
|
build_all src/kernel kernel_cc kernel_ld kernel.elf
|
||||||
|
|
||||||
objects=""
|
|
||||||
for file in flanterm.c flanterm_backends/fb.c; do
|
|
||||||
src="dependencies/flanterm/src/$file"
|
|
||||||
build="build/user-libs/flanterm/$file.o"
|
|
||||||
echo "build $build: flanterm_cc $src" >> build.ninja
|
|
||||||
objects="$objects $build"
|
|
||||||
done
|
|
||||||
echo "build build/user-libs/flanterm/libflanterm.o: user_lib_ld $objects" >> build.ninja
|
|
||||||
|
|
||||||
for dir in src/user-libs/*; do
|
for dir in src/user-libs/*; do
|
||||||
lib_name=$(echo $dir | sed -e 's/^src\/user-libs\///')
|
lib_name=$(echo $dir | sed -e 's/^src\/user-libs\///')
|
||||||
build_all $dir user_cc user_lib_ld lib$lib_name.o
|
build_all $dir user_cc user_lib_ld lib$lib_name.o
|
||||||
done
|
done
|
||||||
|
|
||||||
cp_apps=""
|
apps=""
|
||||||
app_deps=""
|
|
||||||
|
|
||||||
for dir in src/user-apps/*; do
|
for dir in src/user-apps/*; do
|
||||||
if [ -d $dir ]; then
|
|
||||||
|
|
||||||
if [ -e $dir/libraries.txt ]; then
|
|
||||||
lib_paths=$(cat $dir/libraries.txt | sed -e 's/^.*$/build\/user-libs\/\0\/lib\0.o/' | tr '\n' ' ')
|
|
||||||
else
|
|
||||||
lib_paths=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
app_name=$(echo $dir | sed -e 's/^src\/user-apps\///')
|
|
||||||
build_all $dir user_cc user_app_ld $app_name.elf "$lib_paths"
|
|
||||||
|
|
||||||
build_elf=$(echo $dir | sed -e 's/^src/build/')/$app_name.elf
|
|
||||||
disk_dir=build/disk/calcite/apps/$app_name
|
|
||||||
disk_elf=$disk_dir/$app_name.elf
|
|
||||||
|
|
||||||
cp_apps=$(echo "$cp_apps" \
|
|
||||||
"mkdir -p $disk_dir &&" \
|
|
||||||
"cp $build_elf $disk_elf &&")
|
|
||||||
app_deps="$app_deps $build_elf"
|
|
||||||
|
|
||||||
|
if [ -e $dir/libraries.txt ]; then
|
||||||
|
lib_paths=$(cat $dir/libraries.txt | sed -e 's/^.*$/build\/user-libs\/\0\/lib\0.o/')
|
||||||
|
else
|
||||||
|
lib_paths=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
app_name=$(echo $dir | sed -e 's/^src\/user-apps\///')
|
||||||
|
build_all $dir user_cc user_app_ld $app_name.elf $lib_paths
|
||||||
|
build_dir=$(echo $dir | sed -e 's/^src/build/')
|
||||||
|
apps="$apps $build_dir/$app_name.elf"
|
||||||
|
|
||||||
done
|
done
|
||||||
|
|
||||||
|
echo "rule initfs" >> build.ninja
|
||||||
|
echo " command =" \
|
||||||
|
"rm -rf build/initfs &&" \
|
||||||
|
"mkdir -p build/initfs/apps &&" \
|
||||||
|
"cp build/user-apps/hello/hello.elf build/initfs/apps/ &&" \
|
||||||
|
"cd build/initfs &&" \
|
||||||
|
"tar cf ../initfs.tar *" >> build.ninja
|
||||||
|
|
||||||
|
echo "build build/initfs.tar: initfs $apps" >> build.ninja
|
||||||
|
|
||||||
echo "rule disk" >> build.ninja
|
echo "rule disk" >> build.ninja
|
||||||
echo " command =" \
|
echo " command =" \
|
||||||
"rm -rf build/disk &&" \
|
"rm -rf build/disk &&" \
|
||||||
|
|
@ -134,12 +105,12 @@ echo " command =" \
|
||||||
"cp dependencies/limine/limine-bios.sys build/disk/limine/ &&"\
|
"cp dependencies/limine/limine-bios.sys build/disk/limine/ &&"\
|
||||||
"mkdir -p build/disk/EFI/BOOT &&" \
|
"mkdir -p build/disk/EFI/BOOT &&" \
|
||||||
"cp dependencies/limine/BOOTX64.EFI build/disk/EFI/BOOT/ &&" \
|
"cp dependencies/limine/BOOTX64.EFI build/disk/EFI/BOOT/ &&" \
|
||||||
"mkdir -p build/disk/calcite &&" \
|
"mkdir build/disk/calcite &&" \
|
||||||
"cp build/kernel/kernel.elf build/disk/calcite/ &&" \
|
"cp build/kernel/kernel.elf build/disk/calcite/ &&" \
|
||||||
$cp_apps \
|
"cp build/initfs.tar build/disk/calcite/ &&" \
|
||||||
"xorriso -as mkisofs -R -r -J -b limine/limine-bios-cd.bin -no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus -apm-block-size 2048 --efi-boot limine/limine-uefi-cd.bin -efi-boot-part --efi-boot-image --protective-msdos-label -quiet build/disk -o build/disk.iso &&" \
|
"xorriso -as mkisofs -R -r -J -b limine/limine-bios-cd.bin -no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus -apm-block-size 2048 --efi-boot limine/limine-uefi-cd.bin -efi-boot-part --efi-boot-image --protective-msdos-label build/disk -o build/disk.iso &&" \
|
||||||
"dependencies/limine/limine bios-install --quiet build/disk.iso" >> build.ninja
|
"dependencies/limine/limine bios-install build/disk.iso" >> build.ninja
|
||||||
|
|
||||||
echo "build build/disk.iso: disk | build/kernel/kernel.elf $app_deps" >> build.ninja
|
echo "build build/disk.iso: disk | build/kernel/kernel.elf build/initfs.tar" >> build.ninja
|
||||||
|
|
||||||
echo "default build/disk.iso" >> build.ninja
|
echo "default build/disk.iso" >> build.ninja
|
||||||
|
|
|
||||||
41
readme.txt
41
readme.txt
|
|
@ -15,16 +15,17 @@ Calcite requires some software to be installed before it can be built:
|
||||||
* curl
|
* curl
|
||||||
* GCC (Any C compiler supporting both C99 and C23 should work.)
|
* GCC (Any C compiler supporting both C99 and C23 should work.)
|
||||||
* GNU Binutils (Specifically, "ld" is used to link the kernel.)
|
* GNU Binutils (Specifically, "ld" is used to link the kernel.)
|
||||||
|
* GNU tar (Any POSIX tar should be fine.)
|
||||||
* GNU xorriso
|
* GNU xorriso
|
||||||
* NASM
|
* NASM
|
||||||
* Ninja build system
|
* Ninja build system
|
||||||
|
|
||||||
On Debian, it is sufficient to run this command:
|
On Debian, it is sufficient to run this command:
|
||||||
apt install binutils curl gcc nasm ninja-build xorriso
|
apt install binutils curl gcc nasm ninja-build tar xorriso
|
||||||
|
|
||||||
To build Calcite, run these commands:
|
To build Calcite, run these commands:
|
||||||
sh get-dependencies.sh
|
sh get-dependencies.sh
|
||||||
sh make-build.sh release
|
sh make-build.sh
|
||||||
ninja
|
ninja
|
||||||
|
|
||||||
This will build a disk image at "build/disk.iso" that can be booted with
|
This will build a disk image at "build/disk.iso" that can be booted with
|
||||||
|
|
@ -43,30 +44,22 @@ Then, just run "gdb -x qemu.gdb". Calcite will be booted in QEMU, and GDB will
|
||||||
be attached to it. Use "c" or "continue" in GDB to start execution.
|
be attached to it. Use "c" or "continue" in GDB to start execution.
|
||||||
|
|
||||||
While debugging, it is useful to disable optimizations and enable debugging
|
While debugging, it is useful to disable optimizations and enable debugging
|
||||||
information. If you already built Calcite in "release" mode, run:
|
information. In "make-build.sh", under COMMON_CC_EXTRA_FLAGS, you can change
|
||||||
rm -r build build.ninja
|
"-O3" to "-O0 -ggdb". Then, run these commands to rebuild:
|
||||||
sh make-build.sh debug
|
rm -r build
|
||||||
|
rm build.ninja
|
||||||
|
sh make-build.sh
|
||||||
ninja
|
ninja
|
||||||
|
|
||||||
If you did not already build Calcite, then you do not need the first command.
|
|
||||||
|
|
||||||
=== License
|
=== License
|
||||||
|
|
||||||
Calcite is Copyright 2025-2026 Benji Dial, and licensed under the GNU GPL v3.
|
Calcite is Copyright 2025 Benji Dial, and licensed under the GNU GPL v3. See
|
||||||
See "license.txt" for the text of the license.
|
"license.txt" for the text of the license.
|
||||||
|
|
||||||
The disk image includes the Limine bootloader, version 9.3.4. Limine is
|
The disk image includes the Limine bootloader. Limine is Copyright 2019-2025
|
||||||
Copyright 2019-2025 Mintsuki and contributors, and is released under the
|
Mintsuki and contributors, and is released under the BSD 2-Clause license.
|
||||||
BSD 2-Clause license. You can find Limine online at
|
You can find Limine online at <https://github.com/limine-bootloader/limine>.
|
||||||
<https://codeberg.org/Limine/Limine>.
|
After running "sh get-dependencies.sh", you can find the license of Limine at
|
||||||
After running "sh get-dependencies.sh", you can find the license of Limine
|
"dependencies/limine/LICENSE". Limine also has its own dependencies. In the
|
||||||
at "dependencies/limine/LICENSE". Limine also has its own dependencies.
|
source repository of Limine, you can find a list of these with their licenses
|
||||||
In the source repository of Limine, you can find a list of these with
|
at "3RDPARTY.md".
|
||||||
their licenses at "3RDPARTY.md".
|
|
||||||
|
|
||||||
The terminal, included in the disk image, is linked with the Flanterm library,
|
|
||||||
version 2.1.0. Flanterm is Copyright 2022-2025 minstuki and contributors, and
|
|
||||||
is released under the BSD 2-Clause license. You can find Flanterm online at
|
|
||||||
<https://codeberg.org/Mintsuki/Flanterm>.
|
|
||||||
After running "sh get-dependencies.sh", you can find the license of Flanterm
|
|
||||||
at "dependencies/flanterm/LICENSE".
|
|
||||||
|
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
/* Calcite, src/kernel/debug.c
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "serial.h"
|
|
||||||
#include "debug.h"
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
void log_core(const char *file, const char *function, const char *format, ...) {
|
|
||||||
|
|
||||||
int file_length = 0;
|
|
||||||
while (file[file_length] != 0)
|
|
||||||
++file_length;
|
|
||||||
|
|
||||||
int function_length = 0;
|
|
||||||
while (function[function_length] != 0)
|
|
||||||
++function_length;
|
|
||||||
|
|
||||||
write_serial_string_n(file, file_length);
|
|
||||||
write_serial_string_n(" ", 1);
|
|
||||||
write_serial_string_n(function, function_length);
|
|
||||||
|
|
||||||
write_serial_string_n(" -", 2);
|
|
||||||
for (int i = file_length + function_length + 3; i < 49; ++i)
|
|
||||||
write_serial_string_n("-", 1);
|
|
||||||
while (format[0] == ' ') {
|
|
||||||
write_serial_string_n("-", 1);
|
|
||||||
++format;
|
|
||||||
}
|
|
||||||
write_serial_string_n(" ", 1);
|
|
||||||
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
int next_percent = 0;
|
|
||||||
while (format[next_percent] != '%') {
|
|
||||||
if (format[next_percent] == 0) {
|
|
||||||
write_serial_string_n(format, next_percent);
|
|
||||||
va_end(args);
|
|
||||||
write_serial_string_n("\r\n", 2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
++next_percent;
|
|
||||||
}
|
|
||||||
|
|
||||||
write_serial_string_n(format, next_percent);
|
|
||||||
|
|
||||||
switch (format[next_percent + 1]) {
|
|
||||||
case 's': {
|
|
||||||
const char *arg = va_arg(args, const char *);
|
|
||||||
write_serial_string(arg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'd': {
|
|
||||||
int arg = va_arg(args, int);
|
|
||||||
write_serial_integer(arg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'h': {
|
|
||||||
uint64_t arg1 = va_arg(args, uint64_t);
|
|
||||||
int arg2 = va_arg(args, int);
|
|
||||||
write_serial_hex(arg1, arg2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'B': {
|
|
||||||
uint64_t arg = va_arg(args, uint64_t);
|
|
||||||
if (arg < 10000) {
|
|
||||||
write_serial_integer(arg);
|
|
||||||
write_serial_string_n(" B", 2);
|
|
||||||
}
|
|
||||||
else if ((arg + 512) / 1024 < 10000) {
|
|
||||||
write_serial_integer((arg + 512) / 1024);
|
|
||||||
write_serial_string_n(" kiB", 4);
|
|
||||||
}
|
|
||||||
else if ((arg + 512 * 1024) / (1024 * 1024) < 10000) {
|
|
||||||
write_serial_integer((arg + 512 * 1024) / (1024 * 1024));
|
|
||||||
write_serial_string_n(" MiB", 4);
|
|
||||||
}
|
|
||||||
else if ((arg + 512 * 1024 * 1024) / (1024 * 1024 * 1024) < 10000) {
|
|
||||||
write_serial_integer((arg + 512 * 1024 * 1024) / (1024 * 1024 * 1024));
|
|
||||||
write_serial_string_n(" GiB", 4);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
write_serial_integer((arg + 512 * 1024 * 1024 * 1024ULL) / (1024 * 1024 * 1024 * 1024ULL));
|
|
||||||
write_serial_string_n(" TiB", 4);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("bad format string")
|
|
||||||
}
|
|
||||||
|
|
||||||
format = &format[next_percent + 2];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
/* Calcite, src/kernel/debug.h
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
void log_core(const char *file, const char *function, const char *format, ...);
|
|
||||||
|
|
||||||
#define log(...) { \
|
|
||||||
log_core(__FILE__, __func__, __VA_ARGS__); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CALCITE_DEBUG
|
|
||||||
#define debug_log(...) log(__VA_ARGS__)
|
|
||||||
#elif CALCITE_RELEASE
|
|
||||||
#define debug_log(...) {}
|
|
||||||
#else
|
|
||||||
#error neither CALCITE_DEBUG nor CALCITE_RELEASE defined
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define panic(message) { \
|
|
||||||
log_core(__FILE__, __func__, "kernel panic: %s", message); \
|
|
||||||
while (1) \
|
|
||||||
__asm__ ("hlt"); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define assert(condition) \
|
|
||||||
{ \
|
|
||||||
if (!(condition)) \
|
|
||||||
panic("assertion failed: " #condition) \
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
/* Calcite, src/kernel/drives.c
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "utility.h"
|
|
||||||
#include "drives.h"
|
|
||||||
#include "heap.h"
|
|
||||||
|
|
||||||
struct drive_info *drive_list;
|
|
||||||
int drive_list_buffer_length = 0;
|
|
||||||
int drive_list_count = 0;
|
|
||||||
|
|
||||||
#define DRIVE_LIST_INITIAL_LENGTH 16
|
|
||||||
|
|
||||||
struct drive_info *add_drive() {
|
|
||||||
|
|
||||||
if (drive_list_buffer_length == 0) {
|
|
||||||
drive_list = heap_alloc(DRIVE_LIST_INITIAL_LENGTH * sizeof(struct drive_info));
|
|
||||||
drive_list_buffer_length = DRIVE_LIST_INITIAL_LENGTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (drive_list_count == drive_list_buffer_length) {
|
|
||||||
struct drive_info *new_drive_list =
|
|
||||||
heap_alloc(2 * drive_list_buffer_length * sizeof(struct drive_info));
|
|
||||||
memcpy(new_drive_list, drive_list, drive_list_count * sizeof(struct drive_info));
|
|
||||||
heap_dealloc(drive_list, drive_list_count * sizeof(struct drive_info));
|
|
||||||
drive_list = new_drive_list;
|
|
||||||
drive_list_buffer_length *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct drive_info *to_return = &drive_list[drive_list_count];
|
|
||||||
++drive_list_count;
|
|
||||||
return to_return;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct drive_info *look_up_drive(const char *name) {
|
|
||||||
for (int i = 0; i < drive_list_count; ++i)
|
|
||||||
if (strequ(drive_list[i].name, name))
|
|
||||||
return &drive_list[i];
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
/* Calcite, src/kernel/drives.h
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
enum drive_access_result {
|
|
||||||
DAR_SUCCESS,
|
|
||||||
DAR_HARDWARE_ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
struct drive_info {
|
|
||||||
|
|
||||||
const char *name;
|
|
||||||
uint64_t block_size;
|
|
||||||
uint64_t block_count;
|
|
||||||
const void *driver_info;
|
|
||||||
|
|
||||||
//start and count are both in blocks. bounds-checking is caller responsibility.
|
|
||||||
enum drive_access_result (*read_blocks)(
|
|
||||||
const struct drive_info *info, void *buffer, uint64_t start, uint64_t count);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
//add a drive by calling this and writing to the returned pointer.
|
|
||||||
//the returned pointer might be invalidated by future calls.
|
|
||||||
struct drive_info *add_drive();
|
|
||||||
|
|
||||||
//returned pointer might be invalidated by future calls to add_drive.
|
|
||||||
//returns 0 if there is no drive with that name.
|
|
||||||
const struct drive_info *look_up_drive(const char *name);
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/entry.c
|
/* Calcite, src/kernel/entry.c
|
||||||
* Copyright 2025-2026 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -17,23 +17,17 @@
|
||||||
|
|
||||||
#include "framebuffer.h"
|
#include "framebuffer.h"
|
||||||
#include "interrupts.h"
|
#include "interrupts.h"
|
||||||
#include "ipc-dgram.h"
|
#include "kernel-public/syscall-numbers.h"
|
||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
#include "syscalls.h"
|
#include "syscalls.h"
|
||||||
#include "process.h"
|
#include "process.h"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "drives.h"
|
#include "initfs.h"
|
||||||
#include "paging.h"
|
#include "paging.h"
|
||||||
#include "serial.h"
|
#include "panic.h"
|
||||||
#include "debug.h"
|
|
||||||
#include "input.h"
|
|
||||||
#include "timer.h"
|
|
||||||
#include "heap.h"
|
#include "heap.h"
|
||||||
#include "pci.h"
|
|
||||||
#include "ps2.h"
|
#include "ps2.h"
|
||||||
#include "fs.h"
|
|
||||||
|
|
||||||
#include <kernel-public/syscall-numbers.h>
|
|
||||||
#include <limine.h>
|
#include <limine.h>
|
||||||
|
|
||||||
LIMINE_BASE_REVISION(3)
|
LIMINE_BASE_REVISION(3)
|
||||||
|
|
@ -62,8 +56,8 @@ static volatile struct limine_memmap_request memmap_request = {
|
||||||
.response = 0
|
.response = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
static volatile struct limine_executable_cmdline_request cmdline_request = {
|
static volatile struct limine_module_request module_request = {
|
||||||
.id = LIMINE_EXECUTABLE_CMDLINE_REQUEST,
|
.id = LIMINE_MODULE_REQUEST,
|
||||||
.revision = 0,
|
.revision = 0,
|
||||||
.response = 0
|
.response = 0
|
||||||
};
|
};
|
||||||
|
|
@ -89,8 +83,8 @@ static void map_kernel_region(
|
||||||
physical_start + i, (uint8_t *)virtual_start + i, writable, executable);
|
physical_start + i, (uint8_t *)virtual_start + i, writable, executable);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_CMDLINE_LENGTH 1000
|
static uint8_t *initfs_start;
|
||||||
static char cmdline_copy[MAX_CMDLINE_LENGTH + 1];
|
static uint64_t initfs_length;
|
||||||
|
|
||||||
[[noreturn]] static void with_kernel_page_tables();
|
[[noreturn]] static void with_kernel_page_tables();
|
||||||
|
|
||||||
|
|
@ -100,7 +94,9 @@ static char cmdline_copy[MAX_CMDLINE_LENGTH + 1];
|
||||||
|
|
||||||
if (fb_request.response == 0 || hhdm_request.response == 0 ||
|
if (fb_request.response == 0 || hhdm_request.response == 0 ||
|
||||||
ka_request.response == 0 || memmap_request.response == 0 ||
|
ka_request.response == 0 || memmap_request.response == 0 ||
|
||||||
fb_request.response->framebuffer_count == 0)
|
module_request.response == 0 ||
|
||||||
|
fb_request.response->framebuffer_count == 0 ||
|
||||||
|
module_request.response->module_count == 0)
|
||||||
die();
|
die();
|
||||||
|
|
||||||
struct limine_framebuffer *fb = fb_request.response->framebuffers[0];
|
struct limine_framebuffer *fb = fb_request.response->framebuffers[0];
|
||||||
|
|
@ -110,6 +106,19 @@ static char cmdline_copy[MAX_CMDLINE_LENGTH + 1];
|
||||||
fb->blue_mask_shift != 0 || fb->blue_mask_size != 8)
|
fb->blue_mask_shift != 0 || fb->blue_mask_size != 8)
|
||||||
die();
|
die();
|
||||||
|
|
||||||
|
//find the initfs, and die if we don't have one.
|
||||||
|
|
||||||
|
struct limine_file *initfs = 0;
|
||||||
|
struct limine_module_response *response = module_request.response;
|
||||||
|
for (uint64_t i = 0; i < response->module_count; ++i)
|
||||||
|
if (strequ(response->modules[i]->cmdline, "initfs")) {
|
||||||
|
initfs = response->modules[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!initfs)
|
||||||
|
die();
|
||||||
|
|
||||||
//set up page tables. we will mark the regions with bootloader structures as
|
//set up page tables. we will mark the regions with bootloader structures as
|
||||||
//usable, so we need to be careful not to allocate any physical pages until
|
//usable, so we need to be careful not to allocate any physical pages until
|
||||||
//after we are done using bootloader structures. we map the kernel into our
|
//after we are done using bootloader structures. we map the kernel into our
|
||||||
|
|
@ -144,8 +153,7 @@ static char cmdline_copy[MAX_CMDLINE_LENGTH + 1];
|
||||||
uint64_t fb_length = ((fb->height * fb->pitch - 1) / 4096 + 1) * 4096;
|
uint64_t fb_length = ((fb->height * fb->pitch - 1) / 4096 + 1) * 4096;
|
||||||
fb_physical_base = (uint64_t)fb->address - hhdm_request.response->offset;
|
fb_physical_base = (uint64_t)fb->address - hhdm_request.response->offset;
|
||||||
fb_base = find_free_kernel_region(fb_length);
|
fb_base = find_free_kernel_region(fb_length);
|
||||||
map_kernel_region(
|
map_kernel_region(fb_physical_base, fb_base, fb_length, 1, 0);
|
||||||
fb_physical_base, fb_base, fb_length, 1, 0);
|
|
||||||
|
|
||||||
//store rest of framebuffer information
|
//store rest of framebuffer information
|
||||||
|
|
||||||
|
|
@ -153,17 +161,17 @@ static char cmdline_copy[MAX_CMDLINE_LENGTH + 1];
|
||||||
fb_height = fb->height;
|
fb_height = fb->height;
|
||||||
fb_pitch = fb->pitch;
|
fb_pitch = fb->pitch;
|
||||||
|
|
||||||
//make a copy of the kernel cmdline
|
//remap initfs module and store information.
|
||||||
|
|
||||||
const char *original_cmdline = cmdline_request.response->cmdline;
|
uint64_t initfs_physical_start =
|
||||||
int cmdline_length = 0;
|
(uint64_t)initfs->address - hhdm_request.response->offset;
|
||||||
while (original_cmdline[cmdline_length] != 0)
|
|
||||||
++cmdline_length;
|
|
||||||
|
|
||||||
if (cmdline_length > MAX_CMDLINE_LENGTH)
|
initfs_length = initfs->size;
|
||||||
die();
|
uint64_t initfs_length_rounded = ((initfs_length - 1) / 4096 + 1) * 4096;
|
||||||
|
|
||||||
memcpy(cmdline_copy, original_cmdline, cmdline_length + 1);
|
initfs_start = find_free_kernel_region(initfs_length_rounded);
|
||||||
|
map_kernel_region(
|
||||||
|
initfs_physical_start, initfs_start, initfs_length_rounded, 0, 0);
|
||||||
|
|
||||||
//switch to kernel page tables!
|
//switch to kernel page tables!
|
||||||
|
|
||||||
|
|
@ -171,157 +179,47 @@ static char cmdline_copy[MAX_CMDLINE_LENGTH + 1];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cmdline_pair {
|
|
||||||
const char *key;
|
|
||||||
const char *value;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct cmdline_pair *cmdline_pairs;
|
|
||||||
static int cmdline_pair_count;
|
|
||||||
|
|
||||||
//returns the corresponding value if the key exists, otherwise returns 0.
|
|
||||||
static const char *cmdline_look_up(const char *key) {
|
|
||||||
for (int i = 0; i < cmdline_pair_count; ++i)
|
|
||||||
if (strequ(cmdline_pairs[i].key, key))
|
|
||||||
return cmdline_pairs[i].value;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[noreturn]] static void with_kernel_page_tables() {
|
[[noreturn]] static void with_kernel_page_tables() {
|
||||||
|
|
||||||
init_serial();
|
//set initfs
|
||||||
|
|
||||||
//store cmdline as key-value pairs
|
set_initfs(initfs_start, initfs_length);
|
||||||
|
|
||||||
if (cmdline_copy[0] == 0)
|
|
||||||
cmdline_pair_count = 0;
|
|
||||||
|
|
||||||
else {
|
|
||||||
|
|
||||||
int comma_count = 0;
|
|
||||||
for (int i = 0; cmdline_copy[i] != 0; ++i)
|
|
||||||
if (cmdline_copy[i] == ',')
|
|
||||||
++comma_count;
|
|
||||||
|
|
||||||
cmdline_pair_count = comma_count + 1;
|
|
||||||
struct cmdline_pair *pairs = heap_alloc(cmdline_pair_count * sizeof(struct cmdline_pair));
|
|
||||||
|
|
||||||
int key_start = 0;
|
|
||||||
for (int i = 0; i < cmdline_pair_count; ++i) {
|
|
||||||
|
|
||||||
int key_end = key_start;
|
|
||||||
while (1) {
|
|
||||||
if (cmdline_copy[key_end] == '=')
|
|
||||||
break;
|
|
||||||
if (cmdline_copy[key_end] == ',' || cmdline_copy[key_end] == 0)
|
|
||||||
die();
|
|
||||||
++key_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
int value_start = key_end + 1;
|
|
||||||
int value_end = value_start;
|
|
||||||
while (1) {
|
|
||||||
if (cmdline_copy[value_end] == ',' || cmdline_copy[value_end] == 0)
|
|
||||||
break;
|
|
||||||
if (cmdline_copy[value_end] == '=')
|
|
||||||
die();
|
|
||||||
++value_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *key = heap_alloc(key_end - key_start + 1);
|
|
||||||
memcpy(key, &cmdline_copy[key_start], key_end - key_start);
|
|
||||||
key[key_end - key_start] = 0;
|
|
||||||
pairs[i].key = key;
|
|
||||||
|
|
||||||
char *value = heap_alloc(value_end - value_start + 1);
|
|
||||||
memcpy(value, &cmdline_copy[value_start], value_end - value_start);
|
|
||||||
value[value_end - value_start] = 0;
|
|
||||||
pairs[i].value = value;
|
|
||||||
|
|
||||||
key_start = value_end + 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdline_pairs = pairs;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_log("command line")
|
|
||||||
for (int i = 0; i < cmdline_pair_count; ++i)
|
|
||||||
debug_log(" %s %s", cmdline_pairs[i].key, cmdline_pairs[i].value)
|
|
||||||
|
|
||||||
//set up interrupts
|
//set up interrupts
|
||||||
|
|
||||||
init_timer();
|
|
||||||
init_ps2();
|
init_ps2();
|
||||||
|
|
||||||
set_irq_handler(0x00, &on_pit_irq);
|
|
||||||
set_irq_handler(0x01, &on_keyboard_irq);
|
set_irq_handler(0x01, &on_keyboard_irq);
|
||||||
set_irq_handler(0x0c, &on_mouse_irq);
|
set_irq_handler(0x0c, &on_mouse_irq);
|
||||||
|
enable_interrupts();
|
||||||
init_interrupts();
|
|
||||||
|
|
||||||
//set up syscalls
|
//set up syscalls
|
||||||
|
|
||||||
init_syscalls();
|
init_syscalls();
|
||||||
register_syscall(SYSCALL_END_THREAD, (void *)&syscall_end_thread);
|
register_syscall(SYSCALL_END_THREAD, (void *)&syscall_end_thread);
|
||||||
register_syscall(SYSCALL_MAP_FRAMEBUFFER, (void *)&syscall_map_framebuffer);
|
register_syscall(SYSCALL_MAP_FRAMEBUFFER, (void *)&syscall_map_framebuffer);
|
||||||
register_syscall(SYSCALL_OPEN_FILE, (void *)&syscall_open_file);
|
|
||||||
register_syscall(SYSCALL_CLOSE_FILE, (void *)&syscall_close_file);
|
|
||||||
register_syscall(SYSCALL_GET_FILE_SIZE, (void *)&syscall_get_file_size);
|
|
||||||
register_syscall(SYSCALL_READ_FILE, (void *)&syscall_read_file);
|
|
||||||
register_syscall(SYSCALL_WAIT_FOR_MOUSE_PACKET, (void *)&syscall_wait_for_mouse_packet);
|
|
||||||
register_syscall(SYSCALL_MAP_PAGES, (void *)&syscall_map_pages);
|
|
||||||
register_syscall(SYSCALL_SLEEP_MS, (void *)&syscall_sleep_ms);
|
|
||||||
register_syscall(SYSCALL_START_ELF, (void *)&syscall_start_elf);
|
|
||||||
register_syscall(SYSCALL_IPC_CREATE_DGRAM_RECEIVER, (void *)&syscall_ipc_create_dgram_receiver);
|
|
||||||
register_syscall(SYSCALL_IPC_CREATE_DGRAM_SENDER, (void *)&syscall_ipc_create_dgram_sender);
|
|
||||||
register_syscall(SYSCALL_IPC_RECEIVE_DGRAM, (void *)&syscall_ipc_receive_dgram);
|
|
||||||
register_syscall(SYSCALL_IPC_SEND_DGRAM, (void *)&syscall_ipc_send_dgram);
|
|
||||||
register_syscall(SYSCALL_CREATE_THREAD, (void *)&syscall_create_thread);
|
|
||||||
register_syscall(SYSCALL_GET_ENVVAR, (void *)&syscall_get_envvar);
|
|
||||||
register_syscall(SYSCALL_CREATE_MUTEX, (void *)&syscall_create_mutex);
|
|
||||||
register_syscall(SYSCALL_ACQUIRE_MUTEX, (void *)&syscall_acquire_mutex);
|
|
||||||
register_syscall(SYSCALL_RELEASE_MUTEX, (void *)&syscall_release_mutex);
|
|
||||||
register_syscall(SYSCALL_CREATE_EVENT, (void *)&syscall_create_event);
|
|
||||||
register_syscall(SYSCALL_SIGNAL_EVENT, (void *)&syscall_signal_event);
|
|
||||||
register_syscall(SYSCALL_WAIT_EVENT, (void *)&syscall_wait_event);
|
|
||||||
register_syscall(SYSCALL_IPC_CREATE_PRIVATE_DGRAM_PIPE, (void *)&syscall_ipc_create_private_dgram_pipe);
|
|
||||||
register_syscall(SYSCALL_IPC_CREATE_PRIVATE_DGRAM_RECEIVER, (void *)&syscall_ipc_create_private_dgram_receiver);
|
|
||||||
register_syscall(SYSCALL_IPC_CREATE_PRIVATE_DGRAM_SENDER, (void *)&syscall_ipc_create_private_dgram_sender);
|
|
||||||
register_syscall(SYSCALL_GET_MS_SINCE_BOOT, (void *)&syscall_get_ms_since_boot);
|
|
||||||
register_syscall(SYSCALL_SLEEP_UNTIL_MS_SINCE_BOOT, (void *)&syscall_sleep_until_ms_since_boot);
|
|
||||||
register_syscall(SYSCALL_CHANGE_PAGE_PERMISSIONS, (void *)&syscall_change_page_permissions);
|
|
||||||
register_syscall(SYSCALL_WAIT_FOR_KEYBOARD_PACKET, (void *)&syscall_wait_for_keyboard_packet);
|
|
||||||
|
|
||||||
//probe pci devices
|
//load hello and start it
|
||||||
|
|
||||||
probe_pci();
|
|
||||||
|
|
||||||
//load root file system
|
|
||||||
|
|
||||||
const char *root_drive_name = cmdline_look_up("root-drive");
|
|
||||||
if (!root_drive_name)
|
|
||||||
panic("root drive mising from cmdline")
|
|
||||||
const struct drive_info *root_drive = look_up_drive(root_drive_name);
|
|
||||||
if (!root_drive)
|
|
||||||
panic("could not find root drive")
|
|
||||||
|
|
||||||
const char *root_fs_type = cmdline_look_up("root-fs");
|
|
||||||
if (!root_fs_type)
|
|
||||||
panic("root fs missing from cmdline")
|
|
||||||
struct fs_info *root_fs = heap_alloc(sizeof(struct fs_info));
|
|
||||||
if (create_fs_info(root_drive, root_fs, root_fs_type) != FAR_SUCCESS)
|
|
||||||
panic("could not create root file system info")
|
|
||||||
|
|
||||||
set_fs("root", root_fs);
|
|
||||||
|
|
||||||
//load init and start it
|
|
||||||
|
|
||||||
init_scheduler();
|
init_scheduler();
|
||||||
|
|
||||||
if (start_elf("root://calcite/apps/init/init.elf") == 0)
|
const uint8_t *hello_start;
|
||||||
panic("could not start init.elf")
|
uint64_t hello_length;
|
||||||
|
look_up_initfs_file("apps/hello.elf", &hello_start, &hello_length);
|
||||||
|
|
||||||
|
struct process *hello = heap_alloc(sizeof(struct process));
|
||||||
|
uint64_t hello_entry;
|
||||||
|
create_process(hello);
|
||||||
|
load_elf(hello, &hello_entry, hello_start, hello_length);
|
||||||
|
|
||||||
|
struct thread *hello_thread = heap_alloc(sizeof(struct thread));
|
||||||
|
create_thread(hello, hello_thread);
|
||||||
|
|
||||||
|
create_user_task(
|
||||||
|
hello->p4_physical_base,
|
||||||
|
hello_entry,
|
||||||
|
(uint64_t)hello_thread->stack_top);
|
||||||
|
|
||||||
|
running_thread = hello_thread;
|
||||||
|
|
||||||
resume_next_continuation();
|
resume_next_continuation();
|
||||||
|
|
||||||
|
|
|
||||||
105
src/kernel/fs.c
105
src/kernel/fs.c
|
|
@ -1,105 +0,0 @@
|
||||||
/* Calcite, src/kernel/fs.c
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "iso9660.h"
|
|
||||||
#include "utility.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "heap.h"
|
|
||||||
#include "fs.h"
|
|
||||||
|
|
||||||
#include <kernel-public/files.h>
|
|
||||||
|
|
||||||
enum fs_access_result create_fs_info(
|
|
||||||
const struct drive_info *drive, struct fs_info *fs_out, const char *fs_type) {
|
|
||||||
|
|
||||||
if (strequ(fs_type, "iso9660"))
|
|
||||||
return create_iso9660_info(drive, fs_out);
|
|
||||||
|
|
||||||
return FAR_UNKNOWN_FS_TYPE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
struct set_fs {
|
|
||||||
const char *name;
|
|
||||||
const struct fs_info *fs;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct set_fs *set_fses;
|
|
||||||
int set_fses_buffer_length = 0;
|
|
||||||
int set_fses_count = 0;
|
|
||||||
|
|
||||||
#define SET_FSES_INITIAL_LENGTH 16
|
|
||||||
|
|
||||||
void set_fs(const char *name, const struct fs_info *fs) {
|
|
||||||
|
|
||||||
assert(get_fs(name) == 0)
|
|
||||||
|
|
||||||
if (set_fses_buffer_length == 0) {
|
|
||||||
set_fses = heap_alloc(SET_FSES_INITIAL_LENGTH * sizeof(struct set_fs));
|
|
||||||
set_fses_buffer_length = SET_FSES_INITIAL_LENGTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (set_fses_count == set_fses_buffer_length) {
|
|
||||||
struct set_fs *new_set_fses = heap_alloc(2 * set_fses_buffer_length * sizeof(struct set_fs));
|
|
||||||
memcpy(new_set_fses, set_fses, set_fses_count * sizeof(struct set_fs));
|
|
||||||
heap_dealloc(set_fses, set_fses_count * sizeof(struct set_fs));
|
|
||||||
set_fses = new_set_fses;
|
|
||||||
set_fses_buffer_length *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
set_fses[set_fses_count].name = name;
|
|
||||||
set_fses[set_fses_count].fs = fs;
|
|
||||||
++set_fses_count;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct fs_info *get_fs(const char *name) {
|
|
||||||
for (int i = 0; i < set_fses_count; ++i)
|
|
||||||
if (strequ(set_fses[i].name, name))
|
|
||||||
return set_fses[i].fs;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum fs_access_result look_up_file(
|
|
||||||
const char *uri, const struct fs_info **fs_out, void **node_out) {
|
|
||||||
|
|
||||||
int colon = 0;
|
|
||||||
while (1) {
|
|
||||||
if (uri[colon] == ':')
|
|
||||||
break;
|
|
||||||
if (uri[colon] == 0)
|
|
||||||
panic("bad uri")
|
|
||||||
++colon;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uri[colon + 1] != '/' || uri[colon + 2] != '/')
|
|
||||||
panic("bad uri")
|
|
||||||
|
|
||||||
char *uri_copy = heap_alloc(colon + 1);
|
|
||||||
memcpy(uri_copy, uri, colon);
|
|
||||||
uri_copy[colon] = 0;
|
|
||||||
|
|
||||||
*fs_out = get_fs(uri_copy);
|
|
||||||
|
|
||||||
heap_dealloc(uri_copy, colon + 1);
|
|
||||||
|
|
||||||
if (!*fs_out)
|
|
||||||
return FAR_NOT_FOUND;
|
|
||||||
|
|
||||||
return (*(*fs_out)->look_up_file)(*fs_out, node_out, &uri[colon + 3]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
/* Calcite, src/kernel/fs.h
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "drives.h"
|
|
||||||
|
|
||||||
#include <kernel-public/files.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
struct fs_stat {
|
|
||||||
uint64_t bytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct fs_info {
|
|
||||||
|
|
||||||
const struct drive_info *drive;
|
|
||||||
const void *driver_info;
|
|
||||||
|
|
||||||
enum fs_access_result (*free_node)(
|
|
||||||
const struct fs_info *info, void *node);
|
|
||||||
|
|
||||||
enum fs_access_result (*look_up_file)(
|
|
||||||
const struct fs_info *info, void **node_out, const char *path);
|
|
||||||
|
|
||||||
enum fs_access_result (*stat_file)(
|
|
||||||
const struct fs_info *info, void *node, struct fs_stat *stat_out);
|
|
||||||
|
|
||||||
//start is in bytes. bounds-checking is caller responsibility.
|
|
||||||
enum fs_access_result (*read_file)(
|
|
||||||
const struct fs_info *info, void *node,
|
|
||||||
void *buffer, uint64_t start, uint64_t bytes);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
enum fs_access_result create_fs_info(
|
|
||||||
const struct drive_info *drive, struct fs_info *fs_out, const char *fs_type);
|
|
||||||
|
|
||||||
void set_fs(const char *name, const struct fs_info *fs);
|
|
||||||
|
|
||||||
//returns 0 if no fs is set with that name.
|
|
||||||
const struct fs_info *get_fs(const char *name);
|
|
||||||
|
|
||||||
enum fs_access_result look_up_file(const char *uri, const struct fs_info **fs_out, void **node_out);
|
|
||||||
74
src/kernel/initfs.c
Normal file
74
src/kernel/initfs.c
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
/* Calcite, src/kernel/initfs.c
|
||||||
|
* Copyright 2025 Benji Dial
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "initfs.h"
|
||||||
|
|
||||||
|
static const uint8_t *initfs_start;
|
||||||
|
static uint64_t initfs_length;
|
||||||
|
|
||||||
|
void set_initfs(const uint8_t *start, uint64_t length) {
|
||||||
|
initfs_start = start;
|
||||||
|
initfs_length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t decode_octal(const char *from) {
|
||||||
|
uint64_t value = 0;
|
||||||
|
while (*from != '\0') {
|
||||||
|
value = value * 8 + *from - '0';
|
||||||
|
++from;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void look_up_initfs_file(
|
||||||
|
const char *path, const uint8_t **start_out, uint64_t *length_out) {
|
||||||
|
|
||||||
|
int path_len = 0;
|
||||||
|
while (path[path_len] != '\0')
|
||||||
|
++path_len;
|
||||||
|
|
||||||
|
uint64_t offset = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
|
||||||
|
if (offset + 512 > initfs_length) {
|
||||||
|
*start_out = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t length =
|
||||||
|
decode_octal((const char *)(initfs_start + offset + 0x7c));
|
||||||
|
|
||||||
|
int found_it = 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < path_len; ++i)
|
||||||
|
if (initfs_start[offset + i] != path[i]) {
|
||||||
|
found_it = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found_it) {
|
||||||
|
*start_out = initfs_start + offset + 512;
|
||||||
|
*length_out = length;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += 512 + ((length - 1) / 512 + 1) * 512;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Calcite, src/kernel/serial.h
|
/* Calcite, src/kernel/initfs.h
|
||||||
* Copyright 2025 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -19,8 +19,8 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void init_serial();
|
void set_initfs(const uint8_t *start, uint64_t length);
|
||||||
void write_serial_string(const char *string);
|
|
||||||
void write_serial_string_n(const char *string, int n);
|
//if the file does not exist, *start_out is set to a null pointer.
|
||||||
void write_serial_integer(int integer);
|
void look_up_initfs_file(
|
||||||
void write_serial_hex(uint64_t value, int places);
|
const char *path, const uint8_t **start_out, uint64_t *length_out);
|
||||||
|
|
@ -1,140 +0,0 @@
|
||||||
/* Calcite, src/kernel/input.c
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "scheduler.h"
|
|
||||||
#include "process.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "input.h"
|
|
||||||
#include "heap.h"
|
|
||||||
|
|
||||||
static int is_somebody_waiting_for_mouse_packet = 0;
|
|
||||||
static struct continuation_info waiting_continuation_mouse;
|
|
||||||
static struct mouse_packet *resume_by_reporting_to_mouse;
|
|
||||||
|
|
||||||
static int total_unreported_x_movement = 0;
|
|
||||||
static int total_unreported_y_movement = 0;
|
|
||||||
|
|
||||||
void add_mouse_movement(int x, int y) {
|
|
||||||
|
|
||||||
total_unreported_x_movement += x;
|
|
||||||
total_unreported_y_movement += y;
|
|
||||||
|
|
||||||
if (is_somebody_waiting_for_mouse_packet) {
|
|
||||||
resume_by_reporting_to_mouse->x_change = total_unreported_x_movement;
|
|
||||||
resume_by_reporting_to_mouse->y_change = total_unreported_y_movement;
|
|
||||||
add_to_queue(&ready_continuations, &waiting_continuation_mouse);
|
|
||||||
total_unreported_x_movement = 0;
|
|
||||||
total_unreported_y_movement = 0;
|
|
||||||
is_somebody_waiting_for_mouse_packet = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void syscall_wait_for_mouse_packet(struct mouse_packet *packet_out) {
|
|
||||||
|
|
||||||
assert(running_thread != 0);
|
|
||||||
|
|
||||||
//TODO: handle these
|
|
||||||
if (is_somebody_waiting_for_mouse_packet ||
|
|
||||||
!is_mapped_writable(
|
|
||||||
running_thread->process, packet_out, sizeof(struct mouse_packet)))
|
|
||||||
panic("TODO")
|
|
||||||
|
|
||||||
if (total_unreported_x_movement != 0 || total_unreported_y_movement != 0) {
|
|
||||||
packet_out->x_change = total_unreported_x_movement;
|
|
||||||
packet_out->y_change = total_unreported_y_movement;
|
|
||||||
total_unreported_x_movement = 0;
|
|
||||||
total_unreported_y_movement = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
is_somebody_waiting_for_mouse_packet = 1;
|
|
||||||
resume_by_reporting_to_mouse = packet_out;
|
|
||||||
yield(&waiting_continuation_mouse);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_somebody_waiting_for_keyboard_packet = 0;
|
|
||||||
static struct continuation_info waiting_continuation_keyboard;
|
|
||||||
static int *resume_by_reporting_to_keyboard;
|
|
||||||
|
|
||||||
//in packets
|
|
||||||
#define INITIAL_KEYBOARD_BUFFER_LENGTH 1024
|
|
||||||
|
|
||||||
static int *keyboard_buffer = 0;
|
|
||||||
static int keyboard_buffer_length = 0;
|
|
||||||
static int keyboard_buffer_used = 0;
|
|
||||||
static int keyboard_buffer_read_pointer = 0;
|
|
||||||
|
|
||||||
void add_keyboard_packet(int packet) {
|
|
||||||
|
|
||||||
if (is_somebody_waiting_for_keyboard_packet) {
|
|
||||||
*resume_by_reporting_to_keyboard = packet;
|
|
||||||
add_to_queue(&ready_continuations, &waiting_continuation_keyboard);
|
|
||||||
is_somebody_waiting_for_keyboard_packet = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (keyboard_buffer_used < keyboard_buffer_length) {
|
|
||||||
int write_pointer = (keyboard_buffer_read_pointer + keyboard_buffer_used) % keyboard_buffer_length;
|
|
||||||
keyboard_buffer[write_pointer] = packet;
|
|
||||||
++keyboard_buffer_used;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (keyboard_buffer == 0) {
|
|
||||||
keyboard_buffer = heap_alloc(INITIAL_KEYBOARD_BUFFER_LENGTH * sizeof(int));
|
|
||||||
keyboard_buffer_length = INITIAL_KEYBOARD_BUFFER_LENGTH;
|
|
||||||
keyboard_buffer[0] = packet;
|
|
||||||
keyboard_buffer_used = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
int *new_buffer = heap_alloc(2 * keyboard_buffer_length * sizeof(int));
|
|
||||||
memcpy(
|
|
||||||
new_buffer, &keyboard_buffer[keyboard_buffer_read_pointer],
|
|
||||||
keyboard_buffer_length - keyboard_buffer_read_pointer);
|
|
||||||
memcpy(
|
|
||||||
&new_buffer[keyboard_buffer_length - keyboard_buffer_read_pointer],
|
|
||||||
keyboard_buffer, keyboard_buffer_read_pointer);
|
|
||||||
heap_dealloc(keyboard_buffer, keyboard_buffer_length * sizeof(int));
|
|
||||||
keyboard_buffer = new_buffer;
|
|
||||||
keyboard_buffer_length *= 2;
|
|
||||||
keyboard_buffer_read_pointer = 0;
|
|
||||||
keyboard_buffer[keyboard_buffer_length / 2] = packet;
|
|
||||||
++keyboard_buffer_used;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int syscall_wait_for_keyboard_packet() {
|
|
||||||
|
|
||||||
if (keyboard_buffer_used > 0) {
|
|
||||||
int value = keyboard_buffer[keyboard_buffer_read_pointer];
|
|
||||||
keyboard_buffer_read_pointer = (keyboard_buffer_read_pointer + 1) % keyboard_buffer_length;
|
|
||||||
--keyboard_buffer_used;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_somebody_waiting_for_keyboard_packet)
|
|
||||||
panic("TODO")
|
|
||||||
|
|
||||||
is_somebody_waiting_for_keyboard_packet = 1;
|
|
||||||
int result;
|
|
||||||
resume_by_reporting_to_keyboard = &result;
|
|
||||||
yield(&waiting_continuation_keyboard);
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
/* Calcite, src/kernel/input.h
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <kernel-public/input.h>
|
|
||||||
|
|
||||||
void add_mouse_movement(int x, int y);
|
|
||||||
|
|
||||||
void syscall_wait_for_mouse_packet(struct mouse_packet *packet_out);
|
|
||||||
|
|
||||||
void add_keyboard_packet(int packet);
|
|
||||||
|
|
||||||
int syscall_wait_for_keyboard_packet();
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
; Calcite, src/kernel/interrupts.asm
|
; Calcite, src/kernel/interrupts.asm
|
||||||
; Copyright 2025-2026 Benji Dial
|
; Copyright 2025 Benji Dial
|
||||||
;
|
;
|
||||||
; This program is free software: you can redistribute it and/or modify
|
; This program is free software: you can redistribute it and/or modify
|
||||||
; it under the terms of the GNU General Public License as published by
|
; it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
|
|
||||||
bits 64
|
bits 64
|
||||||
default rel
|
|
||||||
|
|
||||||
;both defined in interrupts.c
|
;both defined in interrupts.c
|
||||||
extern isr_exception_c
|
extern isr_exception_c
|
||||||
|
|
@ -71,28 +70,8 @@ isr_exception_common:
|
||||||
push r13
|
push r13
|
||||||
push r14
|
push r14
|
||||||
push r15
|
push r15
|
||||||
mov rax, cr2
|
|
||||||
push rax
|
|
||||||
mov rax, cr3
|
|
||||||
push rax
|
|
||||||
mov rdi, rsp
|
mov rdi, rsp
|
||||||
|
jmp isr_exception_c
|
||||||
call isr_exception_c
|
|
||||||
|
|
||||||
add rsp, 48
|
|
||||||
pop r11
|
|
||||||
pop r10
|
|
||||||
pop r9
|
|
||||||
pop r8
|
|
||||||
add rsp, 8
|
|
||||||
pop rsi
|
|
||||||
pop rdi
|
|
||||||
pop rdx
|
|
||||||
pop rcx
|
|
||||||
add rsp, 8
|
|
||||||
pop rax
|
|
||||||
add rsp, 16
|
|
||||||
iretq
|
|
||||||
|
|
||||||
isr_irq_common:
|
isr_irq_common:
|
||||||
push rax
|
push rax
|
||||||
|
|
@ -135,8 +114,8 @@ isr_irq_common:
|
||||||
;rdi is pointer to gdt descriptor
|
;rdi is pointer to gdt descriptor
|
||||||
;rsi is pointer to idt descriptor
|
;rsi is pointer to idt descriptor
|
||||||
;dx is offset of tss into gdt
|
;dx is offset of tss into gdt
|
||||||
global init_interrupts_asm
|
global enable_interrupts_asm
|
||||||
init_interrupts_asm:
|
enable_interrupts_asm:
|
||||||
|
|
||||||
;load tables
|
;load tables
|
||||||
|
|
||||||
|
|
@ -168,4 +147,7 @@ init_interrupts_asm:
|
||||||
mov al, 0x00
|
mov al, 0x00
|
||||||
out 0xa1, al
|
out 0xa1, al
|
||||||
|
|
||||||
|
;enable interrupts and return
|
||||||
|
|
||||||
|
sti
|
||||||
ret
|
ret
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/interrupts.c
|
/* Calcite, src/kernel/interrupts.c
|
||||||
* Copyright 2025-2026 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -22,14 +22,9 @@
|
||||||
// https://osdev.wiki/wiki/Task_State_Segment
|
// https://osdev.wiki/wiki/Task_State_Segment
|
||||||
|
|
||||||
#include "interrupts.h"
|
#include "interrupts.h"
|
||||||
#include "process.h"
|
#include "panic.h"
|
||||||
#include "paging.h"
|
|
||||||
#include "debug.h"
|
|
||||||
|
|
||||||
//interrupts.asm depends on layout
|
|
||||||
struct [[gnu::packed]] exception_parameter {
|
struct [[gnu::packed]] exception_parameter {
|
||||||
uint64_t cr3;
|
|
||||||
uint64_t cr2;
|
|
||||||
uint64_t r15;
|
uint64_t r15;
|
||||||
uint64_t r14;
|
uint64_t r14;
|
||||||
uint64_t r13;
|
uint64_t r13;
|
||||||
|
|
@ -55,73 +50,10 @@ struct [[gnu::packed]] exception_parameter {
|
||||||
uint64_t ss;
|
uint64_t ss;
|
||||||
};
|
};
|
||||||
|
|
||||||
//names are from amd64 manual mentioned above
|
|
||||||
const char *exception_names[32] = {
|
|
||||||
"divide-by-zero-error", "debug", "non-maskable-interrupt", "breakpoint", "overflow",
|
|
||||||
"bound-range", "invalid-opcode", "device-not-available", "double-fault", 0,
|
|
||||||
"invalid-tss", "segment-not-present", "stack", "general-protection", "page-fault",
|
|
||||||
0, "x87 floating-point exception-pending", "alignment-check", "machine-check",
|
|
||||||
"simd floating-point", 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
"hypervisor injection exception", "vmm communication exception", "security exception", 0
|
|
||||||
};
|
|
||||||
|
|
||||||
[[noreturn]] static void unhandled_exception(const struct exception_parameter *parameter) {
|
|
||||||
|
|
||||||
assert(parameter->exception_number < 32 && exception_names[parameter->exception_number] != 0)
|
|
||||||
|
|
||||||
log("unhandled exception")
|
|
||||||
log(" exception name = %s", exception_names[parameter->exception_number])
|
|
||||||
log(" error code = 0x%h", parameter->error_code, 16)
|
|
||||||
log(" cr2 = 0x%h", parameter->cr2, 16)
|
|
||||||
log(" cr3 = 0x%h", parameter->cr3, 16)
|
|
||||||
log(" cs = 0x%h", parameter->cs, 16)
|
|
||||||
log(" rip = 0x%h", parameter->rip, 16)
|
|
||||||
log(" ss = 0x%h", parameter->ss, 16)
|
|
||||||
log(" rsp = 0x%h", parameter->rsp, 16)
|
|
||||||
log(" rflags = 0x%h", parameter->rflags, 16)
|
|
||||||
log(" rax = 0x%h", parameter->rax, 16)
|
|
||||||
log(" rbx = 0x%h", parameter->rbx, 16)
|
|
||||||
log(" rcx = 0x%h", parameter->rcx, 16)
|
|
||||||
log(" rdx = 0x%h", parameter->rdx, 16)
|
|
||||||
log(" rdi = 0x%h", parameter->rdi, 16)
|
|
||||||
log(" rsi = 0x%h", parameter->rsi, 16)
|
|
||||||
log(" rbp = 0x%h", parameter->rbp, 16)
|
|
||||||
log(" r8 = 0x%h", parameter->r8, 16)
|
|
||||||
log(" r9 = 0x%h", parameter->r9, 16)
|
|
||||||
log(" r10 = 0x%h", parameter->r10, 16)
|
|
||||||
log(" r11 = 0x%h", parameter->r11, 16)
|
|
||||||
log(" r12 = 0x%h", parameter->r12, 16)
|
|
||||||
log(" r13 = 0x%h", parameter->r13, 16)
|
|
||||||
log(" r14 = 0x%h", parameter->r14, 16)
|
|
||||||
log(" r15 = 0x%h", parameter->r15, 16)
|
|
||||||
log("halting")
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
__asm__ ("hlt");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//referenced in interrupts.asm
|
//referenced in interrupts.asm
|
||||||
void isr_exception_c(const struct exception_parameter *parameter) {
|
void isr_exception_c(struct exception_parameter *parameter) {
|
||||||
|
(void)parameter;
|
||||||
if (running_thread != 0 &&
|
panic("TODO");
|
||||||
parameter->exception_number == 0x0e &&
|
|
||||||
(parameter->error_code & 0x1) == 0x0 &&
|
|
||||||
parameter->cr2 >= (uint64_t)running_thread->stack_bottom + 4096 &&
|
|
||||||
parameter->cr2 < (uint64_t)running_thread->stack_top) {
|
|
||||||
|
|
||||||
void *vma = (void *)(parameter->cr2 & 0xfffffffffffff000);
|
|
||||||
unmap_page_for_process(running_thread->process, vma);
|
|
||||||
uint64_t pma = take_free_physical_page();
|
|
||||||
map_page_for_process(
|
|
||||||
running_thread->process, pma,
|
|
||||||
vma, 1, 0, 1);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
unhandled_exception(parameter);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void (*irq_handlers[16])() = {
|
void (*irq_handlers[16])() = {
|
||||||
|
|
@ -253,12 +185,12 @@ static struct idt_descriptor the_idt_descriptor = {
|
||||||
extern void *isrs[48];
|
extern void *isrs[48];
|
||||||
|
|
||||||
//defined in interrupts.asm
|
//defined in interrupts.asm
|
||||||
void init_interrupts_asm(
|
void enable_interrupts_asm(
|
||||||
struct gdt_descriptor *gdtr,
|
struct gdt_descriptor *gdtr,
|
||||||
struct idt_descriptor *idtr,
|
struct idt_descriptor *idtr,
|
||||||
uint16_t tssr);
|
uint16_t tssr);
|
||||||
|
|
||||||
void init_interrupts() {
|
void enable_interrupts() {
|
||||||
|
|
||||||
the_tss.ist1 = (uint64_t)interrupt_stack_one + INTERRUPT_STACK_SIZE;
|
the_tss.ist1 = (uint64_t)interrupt_stack_one + INTERRUPT_STACK_SIZE;
|
||||||
the_tss.ist2 = (uint64_t)interrupt_stack_two + INTERRUPT_STACK_SIZE;
|
the_tss.ist2 = (uint64_t)interrupt_stack_two + INTERRUPT_STACK_SIZE;
|
||||||
|
|
@ -315,6 +247,6 @@ void init_interrupts() {
|
||||||
the_idt[i].offset_32 = offset >> 32;
|
the_idt[i].offset_32 = offset >> 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
init_interrupts_asm(&the_gdt_descriptor, &the_idt_descriptor, 0x08);
|
enable_interrupts_asm(&the_gdt_descriptor, &the_idt_descriptor, 0x08);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/interrupts.h
|
/* Calcite, src/kernel/interrupts.h
|
||||||
* Copyright 2025-2026 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -19,8 +19,8 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
//switches to kernel gdt and idt, maps and unmasks irqs
|
//switches to kernel gdt and idt, maps and unmasks irqs, enables interrupts
|
||||||
void init_interrupts();
|
void enable_interrupts();
|
||||||
|
|
||||||
//the handler should have normal C linkage
|
//the handler should have normal C linkage
|
||||||
void set_irq_handler(uint8_t irq_line, void (*handler)());
|
void set_irq_handler(uint8_t irq_line, void (*handler)());
|
||||||
|
|
|
||||||
|
|
@ -1,269 +0,0 @@
|
||||||
/* Calcite, src/kernel/ipc-dgram.c
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ipc-dgram.h"
|
|
||||||
#include "process.h"
|
|
||||||
#include "scheduler.h"
|
|
||||||
#include "utility.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "heap.h"
|
|
||||||
|
|
||||||
#include <kernel-public/ipc.h>
|
|
||||||
|
|
||||||
struct ipc_dgram_address_book {
|
|
||||||
const char *address;
|
|
||||||
struct ipc_dgram_box box;
|
|
||||||
struct ipc_dgram_address_book *prev;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct ipc_dgram_address_book *last = 0;
|
|
||||||
|
|
||||||
#define INITIAL_SEND_BACKLOG 4
|
|
||||||
|
|
||||||
struct ipc_dgram_box *get_ipc_dgram_box(const char *address) {
|
|
||||||
|
|
||||||
for (struct ipc_dgram_address_book *book = last; book != 0; book = book->prev)
|
|
||||||
if (strequ(book->address, address))
|
|
||||||
return &book->box;
|
|
||||||
|
|
||||||
struct ipc_dgram_address_book *new = heap_alloc(sizeof(struct ipc_dgram_address_book));
|
|
||||||
|
|
||||||
int address_length = 0;
|
|
||||||
while (address[address_length] != 0)
|
|
||||||
++address_length;
|
|
||||||
|
|
||||||
char *new_address = heap_alloc(address_length + 1);
|
|
||||||
memcpy(new_address, address, address_length);
|
|
||||||
new_address[address_length] = 0;
|
|
||||||
new->address = new_address;
|
|
||||||
|
|
||||||
new->box.is_receiver_held = 0;
|
|
||||||
new->box.is_private = 0;
|
|
||||||
new->box.is_someone_waiting_to_receive = 0;
|
|
||||||
create_queue(&new->box.waiting_to_send, INITIAL_SEND_BACKLOG);
|
|
||||||
new->box.buffer_read_pointer = 0;
|
|
||||||
new->box.buffer_read_available = 0;
|
|
||||||
|
|
||||||
new->prev = last;
|
|
||||||
last = new;
|
|
||||||
return &new->box;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_ipc_dgram_size(struct ipc_dgram_box *box) {
|
|
||||||
|
|
||||||
if (box->is_someone_waiting_to_receive)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
while (box->buffer_read_available == 0) {
|
|
||||||
box->is_someone_waiting_to_receive = 1;
|
|
||||||
yield(&box->waiting_to_receive);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(box->buffer_read_available >= 4)
|
|
||||||
return *(int *)&box->buffer[box->buffer_read_pointer];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void receive_ipc_dgram(struct ipc_dgram_box *box, void *buffer) {
|
|
||||||
|
|
||||||
assert(box->is_someone_waiting_to_receive == 0)
|
|
||||||
assert(box->buffer_read_available >= 4)
|
|
||||||
|
|
||||||
int size = *(int *)&box->buffer[box->buffer_read_pointer];
|
|
||||||
|
|
||||||
box->buffer_read_available -= 4;
|
|
||||||
box->buffer_read_pointer = (box->buffer_read_pointer + 4) % IPC_DGRAM_BOX_BUFFER_LENGTH;
|
|
||||||
|
|
||||||
int size_rounded_up = ((size - 1) / 4 + 1) * 4;
|
|
||||||
|
|
||||||
assert(box->buffer_read_available >= size_rounded_up)
|
|
||||||
|
|
||||||
int first_block = IPC_DGRAM_BOX_BUFFER_LENGTH - box->buffer_read_pointer;
|
|
||||||
if (size < first_block)
|
|
||||||
first_block = size;
|
|
||||||
|
|
||||||
memcpy(buffer, &box->buffer[box->buffer_read_pointer], first_block);
|
|
||||||
if (first_block != size)
|
|
||||||
memcpy(buffer + first_block, box->buffer, size - first_block);
|
|
||||||
|
|
||||||
box->buffer_read_available -= size_rounded_up;
|
|
||||||
box->buffer_read_pointer = (box->buffer_read_pointer + size_rounded_up) % IPC_DGRAM_BOX_BUFFER_LENGTH;
|
|
||||||
|
|
||||||
struct continuation_info ci;
|
|
||||||
while (take_from_queue(&box->waiting_to_send, &ci))
|
|
||||||
add_to_queue(&ready_continuations, &ci);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ipc_dgram_result send_ipc_dgram(struct ipc_dgram_box *box, const void *data, int bytes) {
|
|
||||||
|
|
||||||
assert(bytes > 0)
|
|
||||||
|
|
||||||
int bytes_round_up = ((bytes - 1) / 4 + 1) * 4;
|
|
||||||
|
|
||||||
if (4 + bytes_round_up > IPC_DGRAM_BOX_BUFFER_LENGTH)
|
|
||||||
return IPR_TOO_BIG;
|
|
||||||
|
|
||||||
while (IPC_DGRAM_BOX_BUFFER_LENGTH - box->buffer_read_available < 4 + bytes_round_up)
|
|
||||||
yield_to_queue(&box->waiting_to_send);
|
|
||||||
|
|
||||||
int write_pointer = (box->buffer_read_pointer + box->buffer_read_available) % IPC_DGRAM_BOX_BUFFER_LENGTH;
|
|
||||||
assert(write_pointer + 4 <= IPC_DGRAM_BOX_BUFFER_LENGTH)
|
|
||||||
|
|
||||||
*(int *)&box->buffer[write_pointer] = bytes;
|
|
||||||
|
|
||||||
write_pointer = (write_pointer + 4) % IPC_DGRAM_BOX_BUFFER_LENGTH;
|
|
||||||
|
|
||||||
int first_block = IPC_DGRAM_BOX_BUFFER_LENGTH - write_pointer;
|
|
||||||
if (bytes < first_block)
|
|
||||||
first_block = bytes;
|
|
||||||
|
|
||||||
memcpy(&box->buffer[write_pointer], data, first_block);
|
|
||||||
if (first_block != bytes)
|
|
||||||
memcpy(box->buffer, data + first_block, bytes - first_block);
|
|
||||||
|
|
||||||
box->buffer_read_available += 4 + bytes_round_up;
|
|
||||||
|
|
||||||
if (box->is_someone_waiting_to_receive) {
|
|
||||||
box->is_someone_waiting_to_receive = 0;
|
|
||||||
add_to_queue(&ready_continuations, &box->waiting_to_receive);
|
|
||||||
}
|
|
||||||
|
|
||||||
return IPR_SUCCESS;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t next_private_id = 0;
|
|
||||||
|
|
||||||
struct ipc_dgram_private_book {
|
|
||||||
uint64_t id;
|
|
||||||
struct ipc_dgram_box *box;
|
|
||||||
struct ipc_dgram_private_book *prev;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct ipc_dgram_private_book *last_without_receiver = 0;
|
|
||||||
static struct ipc_dgram_private_book *last_without_sender = 0;
|
|
||||||
|
|
||||||
//0 for none with that id
|
|
||||||
static struct ipc_dgram_box *take_from_private_book(struct ipc_dgram_private_book **book, uint64_t id) {
|
|
||||||
|
|
||||||
while (*book != 0) {
|
|
||||||
|
|
||||||
if ((*book)->id == id) {
|
|
||||||
struct ipc_dgram_box *box = (*book)->box;
|
|
||||||
struct ipc_dgram_private_book *prev = (*book)->prev;
|
|
||||||
heap_dealloc(*book, sizeof(struct ipc_dgram_private_book));
|
|
||||||
*book = prev;
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
|
|
||||||
book = &(*book)->prev;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t syscall_ipc_create_private_dgram_pipe() {
|
|
||||||
|
|
||||||
uint64_t id = next_private_id++;
|
|
||||||
|
|
||||||
struct ipc_dgram_box *box = heap_alloc(sizeof(struct ipc_dgram_box));
|
|
||||||
box->is_receiver_held = 0;
|
|
||||||
box->is_private = 1;
|
|
||||||
box->is_someone_waiting_to_receive = 0;
|
|
||||||
create_queue(&box->waiting_to_send, INITIAL_SEND_BACKLOG);
|
|
||||||
box->buffer_read_pointer = 0;
|
|
||||||
box->buffer_read_available = 0;
|
|
||||||
|
|
||||||
struct ipc_dgram_private_book *without_receiver = heap_alloc(sizeof(struct ipc_dgram_private_book));
|
|
||||||
without_receiver->id = id;
|
|
||||||
without_receiver->box = box;
|
|
||||||
without_receiver->prev = last_without_receiver;
|
|
||||||
last_without_receiver = without_receiver;
|
|
||||||
|
|
||||||
struct ipc_dgram_private_book *without_sender = heap_alloc(sizeof(struct ipc_dgram_private_book));
|
|
||||||
without_sender->id = id;
|
|
||||||
without_sender->box = box;
|
|
||||||
without_sender->prev = last_without_sender;
|
|
||||||
last_without_sender = without_sender;
|
|
||||||
|
|
||||||
return id;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ipc_dgram_result syscall_ipc_create_private_dgram_receiver(
|
|
||||||
uint64_t id, ipc_dgram_receiver_handle_t *handle_out) {
|
|
||||||
|
|
||||||
assert(running_thread != 0)
|
|
||||||
|
|
||||||
if (!is_mapped_writable(
|
|
||||||
running_thread->process, handle_out, sizeof(ipc_dgram_receiver_handle_t)))
|
|
||||||
syscall_illegal_args();
|
|
||||||
|
|
||||||
struct ipc_dgram_box *box = take_from_private_book(&last_without_receiver, id);
|
|
||||||
if (box == 0)
|
|
||||||
return IPR_BAD_HANDLE;
|
|
||||||
|
|
||||||
assert(box->is_receiver_held == 0)
|
|
||||||
assert(box->private_sides < 2)
|
|
||||||
|
|
||||||
box->is_receiver_held = 1;
|
|
||||||
++box->private_sides;
|
|
||||||
|
|
||||||
struct process_ipc_dgram_handle_info *handle_info =
|
|
||||||
add_handle(&running_thread->process->ipc_dgram_handles, handle_out);
|
|
||||||
|
|
||||||
handle_info->box = box;
|
|
||||||
handle_info->is_receiver = 1;
|
|
||||||
return IPR_SUCCESS;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ipc_dgram_result syscall_ipc_create_private_dgram_sender(
|
|
||||||
uint64_t id, ipc_dgram_sender_handle_t *handle_out) {
|
|
||||||
|
|
||||||
assert(running_thread != 0)
|
|
||||||
|
|
||||||
if (!is_mapped_writable(
|
|
||||||
running_thread->process, handle_out, sizeof(ipc_dgram_sender_handle_t)))
|
|
||||||
syscall_illegal_args();
|
|
||||||
|
|
||||||
struct ipc_dgram_box *box = take_from_private_book(&last_without_sender, id);
|
|
||||||
if (box == 0)
|
|
||||||
return IPR_BAD_HANDLE;
|
|
||||||
|
|
||||||
assert(box->private_sides < 2)
|
|
||||||
|
|
||||||
++box->private_sides;
|
|
||||||
|
|
||||||
struct process_ipc_dgram_handle_info *handle_info =
|
|
||||||
add_handle(&running_thread->process->ipc_dgram_handles, handle_out);
|
|
||||||
|
|
||||||
handle_info->box = box;
|
|
||||||
handle_info->is_receiver = 0;
|
|
||||||
return IPR_SUCCESS;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_ipc_dgram_box(struct ipc_dgram_box *box) {
|
|
||||||
assert(box->is_someone_waiting_to_receive == 0)
|
|
||||||
assert(box->waiting_to_send.count == 0)
|
|
||||||
destroy_queue(&box->waiting_to_send);
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
/* Calcite, src/kernel/ipc-dgram.h
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "scheduler.h"
|
|
||||||
|
|
||||||
#include <kernel-public/ipc.h>
|
|
||||||
|
|
||||||
//must be a multiple of 4
|
|
||||||
#define IPC_DGRAM_BOX_BUFFER_LENGTH 1024
|
|
||||||
|
|
||||||
struct ipc_dgram_box {
|
|
||||||
|
|
||||||
int is_receiver_held;
|
|
||||||
int is_private;
|
|
||||||
int private_sides;
|
|
||||||
|
|
||||||
int is_someone_waiting_to_receive;
|
|
||||||
struct continuation_info waiting_to_receive;
|
|
||||||
|
|
||||||
struct continuation_queue waiting_to_send;
|
|
||||||
|
|
||||||
int buffer_read_pointer;
|
|
||||||
int buffer_read_available;
|
|
||||||
|
|
||||||
//packets are of the form:
|
|
||||||
// int size;
|
|
||||||
// uint8_t packet[size];
|
|
||||||
// uint8_t padding[size % 4 == 0 ? 0 : 4 - size % 4];
|
|
||||||
uint8_t buffer[IPC_DGRAM_BOX_BUFFER_LENGTH];
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ipc_dgram_box *get_ipc_dgram_box(const char *address);
|
|
||||||
|
|
||||||
//waits until a packet is available, then returns the size of that packet.
|
|
||||||
//returns -1 if someone is already waiting.
|
|
||||||
int get_ipc_dgram_size(struct ipc_dgram_box *box);
|
|
||||||
|
|
||||||
//should only be called just after get_ipc_dgram_size
|
|
||||||
//(i.e. with no intervening continuation resumptions)
|
|
||||||
void receive_ipc_dgram(struct ipc_dgram_box *box, void *buffer);
|
|
||||||
|
|
||||||
//only returns IPR_SUCCESS or IPR_TOO_BIG
|
|
||||||
enum ipc_dgram_result send_ipc_dgram(struct ipc_dgram_box *box, const void *data, int bytes);
|
|
||||||
|
|
||||||
uint64_t syscall_ipc_create_private_dgram_pipe();
|
|
||||||
enum ipc_dgram_result syscall_ipc_create_private_dgram_receiver(
|
|
||||||
uint64_t id, ipc_dgram_receiver_handle_t *handle_out);
|
|
||||||
enum ipc_dgram_result syscall_ipc_create_private_dgram_sender(
|
|
||||||
uint64_t id, ipc_dgram_sender_handle_t *handle_out);
|
|
||||||
|
|
||||||
void destroy_ipc_dgram_box(struct ipc_dgram_box *box);
|
|
||||||
|
|
@ -1,382 +0,0 @@
|
||||||
/* Calcite, src/kernel/iso9660.c
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "iso9660.h"
|
|
||||||
#include "kernel-public/files.h"
|
|
||||||
#include "utility.h"
|
|
||||||
#include "drives.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "heap.h"
|
|
||||||
#include "fs.h"
|
|
||||||
|
|
||||||
//relevant sources:
|
|
||||||
// https://www.ecma-international.org/wp-content/uploads/ECMA-119_2nd_edition_december_1987.pdf
|
|
||||||
|
|
||||||
//case-insensitive equality
|
|
||||||
static bool ciequ(char c1, char c2) {
|
|
||||||
if (c1 >= 'a' && c1 <= 'z')
|
|
||||||
c1 &= 0xdf;
|
|
||||||
if (c2 >= 'a' && c2 <= 'z')
|
|
||||||
c2 &= 0xdf;
|
|
||||||
return c1 == c2;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iso9660_driver_info {
|
|
||||||
const void *path_table;
|
|
||||||
//in bytes
|
|
||||||
int path_table_length;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct iso9660_driver_info *get_driver_info(const struct fs_info *info) {
|
|
||||||
return (const struct iso9660_driver_info *)info->driver_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iso9660_node {
|
|
||||||
uint32_t first_block;
|
|
||||||
//in bytes
|
|
||||||
uint32_t length;
|
|
||||||
};
|
|
||||||
|
|
||||||
static enum fs_access_result free_node_iso9660(const struct fs_info *info, void *node) {
|
|
||||||
(void)info;
|
|
||||||
heap_dealloc(node, sizeof(struct iso9660_node));
|
|
||||||
return FAR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t directory_buffer[2048];
|
|
||||||
|
|
||||||
static enum fs_access_result look_up_in_directory(
|
|
||||||
const struct fs_info *info, void **node_out,
|
|
||||||
const char *path, int path_length,
|
|
||||||
uint32_t first_directory_block) {
|
|
||||||
|
|
||||||
if (first_directory_block >= info->drive->block_count)
|
|
||||||
return FAR_FORMAT_ERROR;
|
|
||||||
|
|
||||||
switch ((*info->drive->read_blocks)(info->drive, directory_buffer, first_directory_block, 1)) {
|
|
||||||
case DAR_SUCCESS:
|
|
||||||
break;
|
|
||||||
case DAR_HARDWARE_ERROR:
|
|
||||||
return FAR_HARDWARE_ERROR;
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = *(uint32_t *)&directory_buffer[10];
|
|
||||||
if (length % 2048 != 0)
|
|
||||||
return FAR_FORMAT_ERROR;
|
|
||||||
|
|
||||||
uint32_t loaded_block = first_directory_block;
|
|
||||||
int offset_in_block = 0;
|
|
||||||
int total_offset = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
if (total_offset == length)
|
|
||||||
return FAR_NOT_FOUND;
|
|
||||||
|
|
||||||
if (offset_in_block == 2048 || directory_buffer[offset_in_block] == 0) {
|
|
||||||
++loaded_block;
|
|
||||||
if (loaded_block >= info->drive->block_count)
|
|
||||||
return FAR_FORMAT_ERROR;
|
|
||||||
switch ((*info->drive->read_blocks)(info->drive, directory_buffer, loaded_block, 1)) {
|
|
||||||
case DAR_SUCCESS:
|
|
||||||
break;
|
|
||||||
case DAR_HARDWARE_ERROR:
|
|
||||||
return FAR_HARDWARE_ERROR;
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
total_offset = total_offset - offset_in_block + 2048;
|
|
||||||
offset_in_block = 0;
|
|
||||||
if (total_offset == length)
|
|
||||||
return FAR_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dr_length = directory_buffer[offset_in_block];
|
|
||||||
if (offset_in_block + dr_length > 2048)
|
|
||||||
return FAR_FORMAT_ERROR;
|
|
||||||
|
|
||||||
int id_length = directory_buffer[offset_in_block + 32];
|
|
||||||
if (33 + id_length > dr_length)
|
|
||||||
return FAR_FORMAT_ERROR;
|
|
||||||
|
|
||||||
for (int i = 0; i < id_length; ++i)
|
|
||||||
if (directory_buffer[offset_in_block + 33 + i] == ';') {
|
|
||||||
id_length = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id_length != path_length)
|
|
||||||
goto next_entry;
|
|
||||||
|
|
||||||
for (int i = 0; i < id_length; ++i)
|
|
||||||
if (!ciequ(directory_buffer[offset_in_block + 33 + i], path[i]))
|
|
||||||
goto next_entry;
|
|
||||||
|
|
||||||
struct iso9660_node *node = heap_alloc(sizeof(struct iso9660_node));
|
|
||||||
node->first_block = *(uint32_t *)&directory_buffer[offset_in_block + 2];
|
|
||||||
node->length = *(uint32_t *)&directory_buffer[offset_in_block + 10];
|
|
||||||
|
|
||||||
*node_out = node;
|
|
||||||
return FAR_SUCCESS;
|
|
||||||
|
|
||||||
next_entry:
|
|
||||||
offset_in_block += dr_length;
|
|
||||||
total_offset += dr_length;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum fs_access_result look_up_recursive(
|
|
||||||
const struct fs_info *info, void **node_out,
|
|
||||||
const char *path, int parent_path_table_offset,
|
|
||||||
int parent_directory_number) {
|
|
||||||
|
|
||||||
//FIXME: this function is pretty unoptimized
|
|
||||||
|
|
||||||
recurse:
|
|
||||||
|
|
||||||
int slash_location = 0;
|
|
||||||
int path_length;
|
|
||||||
while (1) {
|
|
||||||
if (path[slash_location] == '/')
|
|
||||||
break;
|
|
||||||
if (path[slash_location] == 0) {
|
|
||||||
path_length = slash_location;
|
|
||||||
slash_location = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++slash_location;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct iso9660_driver_info *driver_info = get_driver_info(info);
|
|
||||||
|
|
||||||
int on_offset = parent_path_table_offset;
|
|
||||||
int on_number = parent_directory_number;
|
|
||||||
|
|
||||||
if (slash_location == -1) {
|
|
||||||
if (on_offset + 6 > driver_info->path_table_length)
|
|
||||||
return FAR_NOT_FOUND;
|
|
||||||
uint32_t first_directory_block = *(uint32_t *)(driver_info->path_table + on_offset + 2);
|
|
||||||
return look_up_in_directory(info, node_out, path, path_length, first_directory_block);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
if (on_offset >= driver_info->path_table_length)
|
|
||||||
return FAR_NOT_FOUND;
|
|
||||||
|
|
||||||
int old_id_length = *(uint8_t *)(driver_info->path_table + on_offset);
|
|
||||||
on_offset += 8 + old_id_length + (old_id_length & 0x01);
|
|
||||||
++on_number;
|
|
||||||
|
|
||||||
if (on_offset + 8 > driver_info->path_table_length)
|
|
||||||
return FAR_NOT_FOUND;
|
|
||||||
|
|
||||||
int this_entry_parent = *(uint16_t *)(driver_info->path_table + on_offset + 6);
|
|
||||||
if (this_entry_parent != parent_directory_number)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int this_entry_id_length = *(uint8_t *)(driver_info->path_table + on_offset);
|
|
||||||
if (this_entry_id_length != slash_location)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (on_offset + 8 + this_entry_id_length > driver_info->path_table_length)
|
|
||||||
return FAR_NOT_FOUND;
|
|
||||||
|
|
||||||
char *this_entry_id = (char *)(driver_info->path_table + on_offset + 8);
|
|
||||||
for (int i = 0; i < slash_location; ++i)
|
|
||||||
if (!ciequ(this_entry_id[i], path[i]))
|
|
||||||
goto next_entry;
|
|
||||||
|
|
||||||
path = path + slash_location + 1;
|
|
||||||
parent_path_table_offset = on_offset;
|
|
||||||
parent_directory_number = on_number;
|
|
||||||
goto recurse;
|
|
||||||
|
|
||||||
next_entry:
|
|
||||||
;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum fs_access_result look_up_file_iso9660(
|
|
||||||
const struct fs_info *info, void **node_out, const char *path) {
|
|
||||||
|
|
||||||
return look_up_recursive(info, node_out, path, 0, 1);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum fs_access_result stat_file_iso9660(
|
|
||||||
const struct fs_info *info, void *node, struct fs_stat *stat_out) {
|
|
||||||
|
|
||||||
(void)info;
|
|
||||||
struct iso9660_node *iso9660_node = (struct iso9660_node *)node;
|
|
||||||
stat_out->bytes = iso9660_node->length;
|
|
||||||
return FAR_SUCCESS;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t read_buffer[2048];
|
|
||||||
|
|
||||||
static enum fs_access_result read_file_iso9660(
|
|
||||||
const struct fs_info *info, void *node, void *buffer, uint64_t start, uint64_t bytes) {
|
|
||||||
|
|
||||||
struct iso9660_node *iso9660_node = (struct iso9660_node *)node;
|
|
||||||
if (iso9660_node->first_block + (start + bytes - 1) / 2048 + 1 > info->drive->block_count)
|
|
||||||
return FAR_FORMAT_ERROR;
|
|
||||||
|
|
||||||
if (start % 2048 != 0) {
|
|
||||||
|
|
||||||
switch ((*info->drive->read_blocks)(
|
|
||||||
info->drive, read_buffer, iso9660_node->first_block + start / 2048, 1)) {
|
|
||||||
case DAR_SUCCESS:
|
|
||||||
break;
|
|
||||||
case DAR_HARDWARE_ERROR:
|
|
||||||
return FAR_HARDWARE_ERROR;
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start + bytes <= ((start - 1) / 2048 + 1) * 2048) {
|
|
||||||
memcpy(buffer, read_buffer + (start % 2048), bytes);
|
|
||||||
return FAR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int to_copy = 2048 - (start % 2048);
|
|
||||||
memcpy(buffer, read_buffer + (start % 2048), to_copy);
|
|
||||||
buffer += to_copy;
|
|
||||||
start += to_copy;
|
|
||||||
bytes -= to_copy;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ((*info->drive->read_blocks)(
|
|
||||||
info->drive, buffer,
|
|
||||||
iso9660_node->first_block + start / 2048,
|
|
||||||
bytes / 2048)) {
|
|
||||||
case DAR_SUCCESS:
|
|
||||||
break;
|
|
||||||
case DAR_HARDWARE_ERROR:
|
|
||||||
return FAR_HARDWARE_ERROR;
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer += (bytes / 2048) * 2048;
|
|
||||||
start += (bytes / 2048) * 2048;
|
|
||||||
bytes %= 2048;
|
|
||||||
|
|
||||||
if (bytes > 0) {
|
|
||||||
|
|
||||||
switch ((*info->drive->read_blocks)(
|
|
||||||
info->drive, read_buffer, iso9660_node->first_block + start / 2048, 1)) {
|
|
||||||
case DAR_SUCCESS:
|
|
||||||
break;
|
|
||||||
case DAR_HARDWARE_ERROR:
|
|
||||||
return FAR_HARDWARE_ERROR;
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(buffer, read_buffer + (start % 2048), bytes);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return FAR_SUCCESS;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t vd_buffer[2048];
|
|
||||||
|
|
||||||
enum fs_access_result create_iso9660_info(const struct drive_info *drive, struct fs_info *fs_out) {
|
|
||||||
|
|
||||||
if (drive->block_size != 2048)
|
|
||||||
panic("TODO")
|
|
||||||
|
|
||||||
int descriptor_number = 0;
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
if ((uint64_t)(16 + descriptor_number) >= drive->block_count)
|
|
||||||
return FAR_FORMAT_ERROR;
|
|
||||||
|
|
||||||
switch ((*drive->read_blocks)(drive, vd_buffer, 16 + descriptor_number, 1)) {
|
|
||||||
case DAR_SUCCESS:
|
|
||||||
break;
|
|
||||||
case DAR_HARDWARE_ERROR:
|
|
||||||
return FAR_HARDWARE_ERROR;
|
|
||||||
default:
|
|
||||||
assert(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vd_buffer[0] == 0xff)
|
|
||||||
return FAR_FORMAT_ERROR;
|
|
||||||
|
|
||||||
if (vd_buffer[0] == 0x01)
|
|
||||||
break;
|
|
||||||
|
|
||||||
++descriptor_number;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t path_table_length = *(uint32_t *)&vd_buffer[132];
|
|
||||||
uint32_t path_table_start = *(uint32_t *)&vd_buffer[140];
|
|
||||||
|
|
||||||
uint64_t path_table_length_rounded_up = ((path_table_length - 1) / 2048 + 1) * 2048;
|
|
||||||
void *path_table = heap_alloc(path_table_length_rounded_up);
|
|
||||||
|
|
||||||
if (path_table_start >= drive->block_count)
|
|
||||||
return FAR_FORMAT_ERROR;
|
|
||||||
|
|
||||||
switch ((*drive->read_blocks)(
|
|
||||||
drive, path_table, path_table_start,
|
|
||||||
path_table_length_rounded_up / 2048)) {
|
|
||||||
case DAR_SUCCESS:
|
|
||||||
break;
|
|
||||||
case DAR_HARDWARE_ERROR:
|
|
||||||
heap_dealloc(path_table, path_table_length_rounded_up);
|
|
||||||
return FAR_HARDWARE_ERROR;
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
heap_dealloc(
|
|
||||||
path_table + path_table_length,
|
|
||||||
path_table_length_rounded_up - path_table_length);
|
|
||||||
|
|
||||||
struct iso9660_driver_info *driver_info = heap_alloc(sizeof(struct iso9660_driver_info));
|
|
||||||
driver_info->path_table = path_table;
|
|
||||||
driver_info->path_table_length = path_table_length;
|
|
||||||
|
|
||||||
fs_out->drive = drive;
|
|
||||||
fs_out->driver_info = driver_info;
|
|
||||||
fs_out->free_node = &free_node_iso9660;
|
|
||||||
fs_out->look_up_file = &look_up_file_iso9660;
|
|
||||||
fs_out->stat_file = &stat_file_iso9660;
|
|
||||||
fs_out->read_file = &read_file_iso9660;
|
|
||||||
|
|
||||||
debug_log("created iso9660 file system")
|
|
||||||
debug_log(" drive name %s", drive->name)
|
|
||||||
debug_log(" path table start block %d", path_table_start)
|
|
||||||
debug_log(" path table block count %d", path_table_length_rounded_up / 2048)
|
|
||||||
|
|
||||||
return FAR_SUCCESS;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
/* Calcite, src/kernel/iso9660.h
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "fs.h"
|
|
||||||
|
|
||||||
enum fs_access_result create_iso9660_info(const struct drive_info *drive, struct fs_info *fs_out);
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
|
|
||||||
bits 64
|
bits 64
|
||||||
default rel
|
|
||||||
|
|
||||||
;both defined in paging.c
|
;both defined in paging.c
|
||||||
extern kernel_p4_physical_address
|
extern kernel_p4_physical_address
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/paging.c
|
/* Calcite, src/kernel/paging.c
|
||||||
* Copyright 2025-2026 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "paging.h"
|
#include "paging.h"
|
||||||
#include "debug.h"
|
#include "panic.h"
|
||||||
|
|
||||||
#define MAX_PHYSICAL_GB 64ULL
|
#define MAX_PHYSICAL_GB 64ULL
|
||||||
|
|
||||||
|
|
@ -104,7 +104,9 @@ void mark_physical_memory_free(uint64_t base, uint64_t length) {
|
||||||
//defined in paging.asm
|
//defined in paging.asm
|
||||||
void invlpg(void *address);
|
void invlpg(void *address);
|
||||||
|
|
||||||
void map_in_kernel_page_table(uint64_t physical_base, void *virtual_base, int writable, int executable) {
|
void map_in_kernel_page_table(
|
||||||
|
uint64_t physical_base, void *virtual_base,
|
||||||
|
int writable, int executable) {
|
||||||
|
|
||||||
uint64_t virtual_base_u64 = (uint64_t)virtual_base;
|
uint64_t virtual_base_u64 = (uint64_t)virtual_base;
|
||||||
assert(virtual_base_u64 >= 0xffffffffc0000000);
|
assert(virtual_base_u64 >= 0xffffffffc0000000);
|
||||||
|
|
@ -120,18 +122,6 @@ void map_in_kernel_page_table(uint64_t physical_base, void *virtual_base, int wr
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void create_kernel_guard_page(void *virtual_base) {
|
|
||||||
|
|
||||||
uint64_t virtual_base_u64 = (uint64_t)virtual_base;
|
|
||||||
assert(virtual_base_u64 >= 0xffffffffc0000000);
|
|
||||||
|
|
||||||
uint64_t p1s_index = (virtual_base_u64 - 0xffffffffc0000000) >> 12;
|
|
||||||
assert(kernel_p1s[p1s_index] == 0);
|
|
||||||
|
|
||||||
kernel_p1s[p1s_index] = 0x2;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void unmap_kernel_page(void *virtual_base) {
|
void unmap_kernel_page(void *virtual_base) {
|
||||||
|
|
||||||
uint64_t virtual_base_u64 = (uint64_t)virtual_base;
|
uint64_t virtual_base_u64 = (uint64_t)virtual_base;
|
||||||
|
|
@ -197,36 +187,3 @@ uint64_t take_free_physical_page() {
|
||||||
}
|
}
|
||||||
panic("out of physical memory");
|
panic("out of physical memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SYSCALL_STACK_BYTES (1 << 15)
|
|
||||||
|
|
||||||
void *create_syscall_stack() {
|
|
||||||
void *vma = find_free_kernel_region(SYSCALL_STACK_BYTES);
|
|
||||||
for (uint64_t i = 0; i < SYSCALL_STACK_BYTES; i += 4096) {
|
|
||||||
uint64_t pma = take_free_physical_page();
|
|
||||||
map_in_kernel_page_table(pma, vma + i * 4096, 1, 0);
|
|
||||||
}
|
|
||||||
return vma + SYSCALL_STACK_BYTES;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_syscall_stack(void *stack_top) {
|
|
||||||
for (uint64_t i = 0; i < SYSCALL_STACK_BYTES; i += 4096)
|
|
||||||
unmap_and_free_kernel_page(stack_top - SYSCALL_STACK_BYTES + i * 4096);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t count_free_pram() {
|
|
||||||
uint64_t total = 0;
|
|
||||||
for (uint64_t i = 0; i < (MAX_PHYSICAL_GB << 15); ++i)
|
|
||||||
for (int j = 0; j < 8; ++j)
|
|
||||||
if (physical_map[i] & (1 << j))
|
|
||||||
total += 4096;
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t count_free_kernel_vram() {
|
|
||||||
uint64_t total = 0;
|
|
||||||
for (uint64_t i = 0; i < 512 * 512; ++i)
|
|
||||||
if (kernel_p1s[i] == 0)
|
|
||||||
total += 4096;
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/paging.h
|
/* Calcite, src/kernel/paging.h
|
||||||
* Copyright 2025-2026 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -33,9 +33,6 @@ void mark_physical_memory_free(uint64_t base, uint64_t length);
|
||||||
void map_in_kernel_page_table(
|
void map_in_kernel_page_table(
|
||||||
uint64_t physical_base, void *virtual_base, int writable, int executable);
|
uint64_t physical_base, void *virtual_base, int writable, int executable);
|
||||||
|
|
||||||
//virtual base should be page-aligned and within kernel range.
|
|
||||||
void create_kernel_guard_page(void *virtual_base);
|
|
||||||
|
|
||||||
//unmaps one page. base should be page-aligned and within kernel range.
|
//unmaps one page. base should be page-aligned and within kernel range.
|
||||||
void unmap_kernel_page(void *virtual_base);
|
void unmap_kernel_page(void *virtual_base);
|
||||||
|
|
||||||
|
|
@ -51,13 +48,3 @@ uint64_t take_free_physical_page();
|
||||||
|
|
||||||
//implemented in paging.asm. the continuation should be noreturn.
|
//implemented in paging.asm. the continuation should be noreturn.
|
||||||
[[noreturn]] void switch_to_kernel_page_tables(void (*continuation)());
|
[[noreturn]] void switch_to_kernel_page_tables(void (*continuation)());
|
||||||
|
|
||||||
//returns the top
|
|
||||||
void *create_syscall_stack();
|
|
||||||
void destroy_syscall_stack(void *stack_top);
|
|
||||||
|
|
||||||
//return value in bytes
|
|
||||||
uint64_t count_free_pram();
|
|
||||||
|
|
||||||
//return value in bytes
|
|
||||||
uint64_t count_free_kernel_vram();
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Calcite, src/user-libs/calcite/entry.c
|
/* Calcite, src/kernel/panic.c
|
||||||
* Copyright 2025 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -15,11 +15,16 @@
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <calcite/syscalls.h>
|
[[noreturn]] void panic_core(
|
||||||
|
const char *file, const char *function, int line, const char *message) {
|
||||||
|
|
||||||
void main();
|
//TODO
|
||||||
|
|
||||||
|
(void)file;
|
||||||
|
(void)function;
|
||||||
|
(void)line;
|
||||||
|
(void)message;
|
||||||
|
while (1)
|
||||||
|
__asm__ ("hlt");
|
||||||
|
|
||||||
[[noreturn]] void _start() {
|
|
||||||
main();
|
|
||||||
end_thread();
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Calcite, include/silver/image.h
|
/* Calcite, src/kernel/panic.h
|
||||||
* Copyright 2025 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -17,20 +17,17 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
[[noreturn]] void panic_core(
|
||||||
|
const char *file, const char *function, int line, const char *message);
|
||||||
|
|
||||||
struct pixel {
|
#define panic(message) panic_core(__FILE__, __func__, __LINE__, message);
|
||||||
uint8_t r;
|
|
||||||
uint8_t g;
|
|
||||||
uint8_t b;
|
|
||||||
uint8_t a;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct image {
|
#ifdef NDEBUG
|
||||||
int width;
|
#define assert(condition) ((void)0)
|
||||||
int height;
|
#else
|
||||||
struct pixel *pixels;
|
#define assert(condition) \
|
||||||
};
|
{ \
|
||||||
|
if (!(condition)) \
|
||||||
void create_image(int width, int height, struct image **image_out);
|
panic("assertion failed: " #condition) \
|
||||||
void destroy_image(struct image *image);
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -1,250 +0,0 @@
|
||||||
/* Calcite, src/kernel/pata.c
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "utility.h"
|
|
||||||
#include "drives.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "heap.h"
|
|
||||||
#include "pata.h"
|
|
||||||
#include "pci.h"
|
|
||||||
|
|
||||||
//some relevant sources:
|
|
||||||
// https://www.isdaman.com/alsos/hardware/hdc/pciide.pdf
|
|
||||||
// ANSI: AT Attachment 8 - ATA/ATAPI Command Set
|
|
||||||
// Seagate: SCSI Commands Reference Manual
|
|
||||||
// OSDev Wiki: ATAPI, PCI IDE Controller
|
|
||||||
|
|
||||||
enum pata_result {
|
|
||||||
PR_SUCCESS,
|
|
||||||
PR_NO_CONTROLLER,
|
|
||||||
PR_ABORTED
|
|
||||||
};
|
|
||||||
|
|
||||||
enum pata_result wait_pio(uint16_t command_block_base) {
|
|
||||||
while (1) {
|
|
||||||
uint8_t status = inb(command_block_base + 7);
|
|
||||||
//0x00 happens in qemu, 0x7f happens in virtualbox
|
|
||||||
if (status == 0x00 || status == 0x7f)
|
|
||||||
return PR_NO_CONTROLLER;
|
|
||||||
if (status & 0x01) {
|
|
||||||
uint8_t error = inb(command_block_base + 1);
|
|
||||||
if (error == 0x04)
|
|
||||||
return PR_ABORTED;
|
|
||||||
panic("TODO")
|
|
||||||
}
|
|
||||||
if ((status & 0x80) || !(status & 0x08))
|
|
||||||
continue;
|
|
||||||
return PR_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//buffer should have room for 256 words.
|
|
||||||
static enum pata_result
|
|
||||||
pata_identify_packet_device(uint16_t command_block_base, uint8_t device_byte, uint16_t *buffer) {
|
|
||||||
|
|
||||||
outb(command_block_base + 6, device_byte);
|
|
||||||
outb(command_block_base + 7, 0xa1);
|
|
||||||
|
|
||||||
enum pata_result result = wait_pio(command_block_base);
|
|
||||||
if (result != PR_SUCCESS)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
insw(command_block_base, buffer, 256);
|
|
||||||
return PR_SUCCESS;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum pata_result patapi_read_capacity(
|
|
||||||
uint16_t command_block_base, uint8_t device_byte, uint32_t *max_block_out, uint32_t *block_size_out) {
|
|
||||||
|
|
||||||
outb(command_block_base + 6, device_byte);
|
|
||||||
outb(command_block_base + 1, 0);
|
|
||||||
outb(command_block_base + 3, 0);
|
|
||||||
outb(command_block_base + 4, 8);
|
|
||||||
outb(command_block_base + 5, 0);
|
|
||||||
outb(command_block_base + 7, 0xa0);
|
|
||||||
|
|
||||||
enum pata_result result = wait_pio(command_block_base);
|
|
||||||
if (result != PR_SUCCESS)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
uint8_t cmd[12] = {
|
|
||||||
0x25, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0 };
|
|
||||||
outsw(command_block_base, cmd, 6);
|
|
||||||
|
|
||||||
result = wait_pio(command_block_base);
|
|
||||||
if (result != PR_SUCCESS)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
uint32_t actual_response_size =
|
|
||||||
inb(command_block_base + 4) | (inb(command_block_base + 5) << 8);
|
|
||||||
if (actual_response_size != 8)
|
|
||||||
panic("TODO")
|
|
||||||
|
|
||||||
uint32_t response[2];
|
|
||||||
insw(command_block_base, response, 4);
|
|
||||||
|
|
||||||
*max_block_out = end_swap_u32(response[0]);
|
|
||||||
*block_size_out = end_swap_u32(response[1]);
|
|
||||||
return PR_SUCCESS;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum pata_result patapi_read(
|
|
||||||
uint16_t command_block_base, uint8_t device_byte, uint16_t block_size,
|
|
||||||
uint32_t start_block, uint32_t block_count, void *buffer) {
|
|
||||||
|
|
||||||
outb(command_block_base + 6, device_byte);
|
|
||||||
outb(command_block_base + 1, 0);
|
|
||||||
outb(command_block_base + 3, 0);
|
|
||||||
outb(command_block_base + 4, block_size & 0xff);
|
|
||||||
outb(command_block_base + 5, block_size >> 8);
|
|
||||||
outb(command_block_base + 7, 0xa0);
|
|
||||||
|
|
||||||
enum pata_result result = wait_pio(command_block_base);
|
|
||||||
if (result != PR_SUCCESS)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
uint8_t cmd[12] = {
|
|
||||||
0xa8, 0,
|
|
||||||
start_block >> 24,
|
|
||||||
(start_block >> 16) & 0xff,
|
|
||||||
(start_block >> 8) & 0xff,
|
|
||||||
start_block & 0xff,
|
|
||||||
block_count >> 24,
|
|
||||||
(block_count >> 16) & 0xff,
|
|
||||||
(block_count >> 8) & 0xff,
|
|
||||||
block_count & 0xff,
|
|
||||||
0, 0 };
|
|
||||||
outsw(command_block_base, cmd, 6);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < block_count; ++i) {
|
|
||||||
|
|
||||||
result = wait_pio(command_block_base);
|
|
||||||
if (result != PR_SUCCESS)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
uint32_t actual_response_size =
|
|
||||||
inb(command_block_base + 4) | (inb(command_block_base + 5) << 8);
|
|
||||||
|
|
||||||
if (actual_response_size != block_size)
|
|
||||||
panic("TODO")
|
|
||||||
|
|
||||||
insw(command_block_base, buffer + i * block_size, block_size / 2);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return PR_SUCCESS;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t ipd_buffer[256];
|
|
||||||
|
|
||||||
struct pata_driver_info {
|
|
||||||
uint16_t command_block_base;
|
|
||||||
uint8_t device_byte;
|
|
||||||
};
|
|
||||||
|
|
||||||
static enum drive_access_result read_blocks_patapi(
|
|
||||||
const struct drive_info *drive_info,
|
|
||||||
void *buffer, uint64_t start, uint64_t count) {
|
|
||||||
|
|
||||||
if (count >= (1ULL << 32) || start >= (1ULL << 32))
|
|
||||||
panic("TODO")
|
|
||||||
|
|
||||||
const struct pata_driver_info *driver_info = drive_info->driver_info;
|
|
||||||
|
|
||||||
return
|
|
||||||
patapi_read(
|
|
||||||
driver_info->command_block_base, driver_info->device_byte,
|
|
||||||
drive_info->block_size, start, count, buffer) == PR_SUCCESS
|
|
||||||
? DAR_SUCCESS : DAR_HARDWARE_ERROR;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int next_pata_drive_number = 0;
|
|
||||||
|
|
||||||
static void probe_pata_drive(uint16_t command_block_base, uint8_t device_byte) {
|
|
||||||
|
|
||||||
if (pata_identify_packet_device(command_block_base, device_byte, ipd_buffer) != PR_SUCCESS)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint32_t max_block, block_size;
|
|
||||||
if (patapi_read_capacity(
|
|
||||||
command_block_base, device_byte, &max_block, &block_size) != PR_SUCCESS)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (next_pata_drive_number > 9)
|
|
||||||
panic("TODO")
|
|
||||||
|
|
||||||
struct drive_info *di = add_drive();
|
|
||||||
|
|
||||||
char *name = heap_alloc(6);
|
|
||||||
memcpy(name, "pata", 4);
|
|
||||||
name[4] = '0' + next_pata_drive_number++;
|
|
||||||
name[5] = 0;
|
|
||||||
di->name = name;
|
|
||||||
|
|
||||||
di->block_size = block_size;
|
|
||||||
di->block_count = max_block + 1;
|
|
||||||
|
|
||||||
struct pata_driver_info *driver_info =
|
|
||||||
heap_alloc(sizeof(struct pata_driver_info));
|
|
||||||
driver_info->command_block_base = command_block_base;
|
|
||||||
driver_info->device_byte = device_byte;
|
|
||||||
di->driver_info = driver_info;
|
|
||||||
|
|
||||||
di->read_blocks = &read_blocks_patapi;
|
|
||||||
|
|
||||||
debug_log("added pata drive")
|
|
||||||
debug_log(" drive name %s", di->name)
|
|
||||||
debug_log(" command block base 0x%h", command_block_base, 4)
|
|
||||||
debug_log(" device byte 0x%h", device_byte, 2)
|
|
||||||
debug_log(" block size %d", di->block_size)
|
|
||||||
debug_log(" block count %d", di->block_count)
|
|
||||||
debug_log(" total size %B", di->block_count * di->block_size)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void probe_pata_drives(uint32_t pci_address_base, uint32_t pci_class_etc) {
|
|
||||||
|
|
||||||
if (pci_class_etc & 0x00000100) {
|
|
||||||
uint32_t bar = read_pci_config(pci_address_base + 0x10);
|
|
||||||
if ((bar & 0xffff0003) != 0x00000001)
|
|
||||||
panic("TODO")
|
|
||||||
probe_pata_drive(bar & 0xfffc, 0x00);
|
|
||||||
probe_pata_drive(bar & 0xfffc, 0x10);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
probe_pata_drive(0x01f0, 0x00);
|
|
||||||
probe_pata_drive(0x01f0, 0x10);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pci_class_etc & 0x00000400) {
|
|
||||||
uint32_t bar = read_pci_config(pci_address_base + 0x18);
|
|
||||||
if ((bar & 0xffff0003) != 0x00000001)
|
|
||||||
panic("TODO")
|
|
||||||
probe_pata_drive(bar & 0xfffc, 0x00);
|
|
||||||
probe_pata_drive(bar & 0xfffc, 0x10);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
probe_pata_drive(0x0170, 0x00);
|
|
||||||
probe_pata_drive(0x0170, 0x10);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
/* Calcite, src/kernel/pata.h
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
//probes for pata drives on this "card" and adds any that exist to drives.h
|
|
||||||
void probe_pata_drives(uint32_t pci_address_base, uint32_t pci_class_etc);
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
; Calcite, src/kernel/pci.asm
|
|
||||||
; Copyright 2025 Benji Dial
|
|
||||||
;
|
|
||||||
; This program is free software: you can redistribute it and/or modify
|
|
||||||
; it under the terms of the GNU General Public License as published by
|
|
||||||
; the Free Software Foundation, either version 3 of the License, or
|
|
||||||
; (at your option) any later version.
|
|
||||||
;
|
|
||||||
; This program is distributed in the hope that it will be useful, but
|
|
||||||
; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
; for more details.
|
|
||||||
;
|
|
||||||
; You should have received a copy of the GNU General Public License along
|
|
||||||
; with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
bits 64
|
|
||||||
default rel
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
global read_pci_config
|
|
||||||
read_pci_config:
|
|
||||||
mov dx, 0x0cf8
|
|
||||||
mov eax, edi
|
|
||||||
out dx, eax
|
|
||||||
mov dx, 0x0cfc
|
|
||||||
in eax, dx
|
|
||||||
ret
|
|
||||||
|
|
||||||
global write_pci_config
|
|
||||||
write_pci_config:
|
|
||||||
mov dx, 0x0cf8
|
|
||||||
mov eax, edi
|
|
||||||
out dx, eax
|
|
||||||
mov dx, 0x0cfc
|
|
||||||
mov eax, esi
|
|
||||||
out dx, eax
|
|
||||||
ret
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
/* Calcite, src/kernel/pci.c
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "debug.h"
|
|
||||||
#include "pata.h"
|
|
||||||
#include "pci.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
static void probe_bus(uint32_t bus_address_base) {
|
|
||||||
|
|
||||||
for (uint32_t device_address_base = bus_address_base;
|
|
||||||
device_address_base < bus_address_base + 0x00010000;
|
|
||||||
device_address_base += 0x00000800)
|
|
||||||
for (uint32_t function_address_base = device_address_base;
|
|
||||||
function_address_base < device_address_base + 0x00000800;
|
|
||||||
function_address_base += 0x00000100) {
|
|
||||||
|
|
||||||
uint8_t header_type = (read_pci_config(function_address_base | 0x0c) >> 16) & 0xff;
|
|
||||||
if (header_type == 0xff)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if ((header_type & 0x7f) == 0x01)
|
|
||||||
//this is a pci-to-pci bridge
|
|
||||||
probe_bus((read_pci_config(function_address_base | 0x18) >> 8) & 0xff);
|
|
||||||
|
|
||||||
else if ((header_type & 0x7f) == 0x00) {
|
|
||||||
//this is a normal function
|
|
||||||
uint32_t class_etc = read_pci_config(function_address_base | 0x08);
|
|
||||||
debug_log("pci device with class %h:%h", class_etc >> 24, 2, class_etc >> 16, 2);
|
|
||||||
switch (class_etc & 0xffff0000) {
|
|
||||||
case 0x01010000:
|
|
||||||
probe_pata_drives(function_address_base, class_etc);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(header_type & 0x80))
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void probe_pci() {
|
|
||||||
probe_bus(0x80000000);
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
/* Calcite, src/kernel/pci.h
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
//reads one dword from config space.
|
|
||||||
//address is 0x8000000 | (bus << 16) | (device << 11) | (function << 8) | (bytes into that function's space).
|
|
||||||
//bytes into that function's space must be dword-aligned.
|
|
||||||
uint32_t read_pci_config(uint32_t address);
|
|
||||||
|
|
||||||
//writes one dword to config space.
|
|
||||||
//address is 0x8000000 | (bus << 16) | (device << 11) | (function << 8) | (bytes into that function's space).
|
|
||||||
//bytes into that function's space must be dword-aligned.
|
|
||||||
void write_pci_config(uint32_t address, uint32_t value);
|
|
||||||
|
|
||||||
void probe_pci();
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
; Calcite, src/kernel/process.asm
|
|
||||||
; Copyright 2025 Benji Dial
|
|
||||||
;
|
|
||||||
; This program is free software: you can redistribute it and/or modify
|
|
||||||
; it under the terms of the GNU General Public License as published by
|
|
||||||
; the Free Software Foundation, either version 3 of the License, or
|
|
||||||
; (at your option) any later version.
|
|
||||||
;
|
|
||||||
; This program is distributed in the hope that it will be useful, but
|
|
||||||
; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
; for more details.
|
|
||||||
;
|
|
||||||
; You should have received a copy of the GNU General Public License along
|
|
||||||
; with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
bits 64
|
|
||||||
default rel
|
|
||||||
|
|
||||||
section .bss
|
|
||||||
|
|
||||||
resb 16384
|
|
||||||
temporary_stack:
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
;referenced in process.c
|
|
||||||
global thread_start
|
|
||||||
thread_start:
|
|
||||||
mov cr3, rbp
|
|
||||||
mov rcx, r12
|
|
||||||
mov r11, 0x200
|
|
||||||
mov rdi, r13
|
|
||||||
|
|
||||||
xor rax, rax
|
|
||||||
xor rbx, rbx
|
|
||||||
xor rdx, rdx
|
|
||||||
xor rsi, rsi
|
|
||||||
xor rbp, rbp
|
|
||||||
xor r8, r8
|
|
||||||
xor r9, r9
|
|
||||||
xor r10, r10
|
|
||||||
xor r12, r12
|
|
||||||
xor r13, r13
|
|
||||||
xor r14, r14
|
|
||||||
xor r15, r15
|
|
||||||
|
|
||||||
o64 sysret
|
|
||||||
|
|
||||||
;referenced in process.c
|
|
||||||
global with_temporary_stack
|
|
||||||
with_temporary_stack:
|
|
||||||
mov rsp, temporary_stack
|
|
||||||
jmp rdi
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/process.h
|
/* Calcite, src/kernel/process.h
|
||||||
* Copyright 2025-2026 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -17,51 +17,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "scheduler.h"
|
#include <stdint.h>
|
||||||
#include "utility.h"
|
|
||||||
#include "fs.h"
|
|
||||||
|
|
||||||
#include <kernel-public/framebuffer.h>
|
|
||||||
#include <kernel-public/process.h>
|
|
||||||
#include <kernel-public/files.h>
|
|
||||||
#include <kernel-public/sync.h>
|
|
||||||
#include <kernel-public/ipc.h>
|
|
||||||
|
|
||||||
struct ipc_dgram_box;
|
|
||||||
|
|
||||||
struct process_file_info {
|
|
||||||
const struct fs_info *fs;
|
|
||||||
void *node;
|
|
||||||
struct fs_stat stat;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct process_ipc_dgram_handle_info {
|
|
||||||
struct ipc_dgram_box *box;
|
|
||||||
//1 if this is a receiver handle, 0 if this is a sender handle.
|
|
||||||
int is_receiver;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct process_mutex_info {
|
|
||||||
int is_acquired;
|
|
||||||
struct continuation_queue waiting_to_acquire;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct process_event_info {
|
|
||||||
int is_signaled;
|
|
||||||
struct continuation_queue waiting_for_signal;
|
|
||||||
};
|
|
||||||
|
|
||||||
//lengths don't include null terminators, buffers do.
|
|
||||||
//key and value should not be empty.
|
|
||||||
struct process_env_pair {
|
|
||||||
//0 for unused
|
|
||||||
int key_length;
|
|
||||||
int value_length;
|
|
||||||
char *key;
|
|
||||||
char *value;
|
|
||||||
};
|
|
||||||
|
|
||||||
//layout used in scheduler.asm
|
|
||||||
struct process {
|
struct process {
|
||||||
|
|
||||||
uint64_t p4_physical_base;
|
uint64_t p4_physical_base;
|
||||||
|
|
@ -71,25 +28,12 @@ struct process {
|
||||||
|
|
||||||
int n_threads;
|
int n_threads;
|
||||||
|
|
||||||
struct handle_list file_handles;
|
|
||||||
struct handle_list ipc_dgram_handles;
|
|
||||||
struct handle_list mutex_handles;
|
|
||||||
struct handle_list event_handles;
|
|
||||||
|
|
||||||
struct process_env_pair *env_pairs;
|
|
||||||
int env_pairs_buffer_size;
|
|
||||||
|
|
||||||
//0 for missing levels. just bottom p3 of address space.
|
//0 for missing levels. just bottom p3 of address space.
|
||||||
uint64_t *p2_virtual_bases[512];
|
uint64_t *p2_virtual_bases[512];
|
||||||
uint64_t **p1_virtual_bases[512];
|
uint64_t **p1_virtual_bases[512];
|
||||||
|
|
||||||
//also just bottom p3. bit set indicates we should free
|
|
||||||
//the physical page when we clean up this process.
|
|
||||||
uint8_t **owned_pages_bitmaps[512];
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//layout used in syscalls.asm and scheduler.asm
|
|
||||||
struct thread {
|
struct thread {
|
||||||
|
|
||||||
struct process *process;
|
struct process *process;
|
||||||
|
|
@ -98,10 +42,6 @@ struct thread {
|
||||||
void *stack_bottom;
|
void *stack_bottom;
|
||||||
void *stack_top;
|
void *stack_top;
|
||||||
|
|
||||||
//both page-aligned
|
|
||||||
void *syscall_stack_bottom;
|
|
||||||
void *syscall_stack_top;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct thread *running_thread;
|
extern struct thread *running_thread;
|
||||||
|
|
@ -113,13 +53,9 @@ void create_thread(struct process *process, struct thread *thread_out);
|
||||||
//virtual base must be in bottom p3 and not zero.
|
//virtual base must be in bottom p3 and not zero.
|
||||||
void map_page_for_process(
|
void map_page_for_process(
|
||||||
struct process *process, uint64_t physical_base,
|
struct process *process, uint64_t physical_base,
|
||||||
void *virtual_base, int writable, int executable, int owned);
|
void *virtual_base, int writable, int executable);
|
||||||
|
|
||||||
//physical and virtual bases must be page-aligned.
|
|
||||||
void create_guard_page_for_process(struct process *process, void *virtual_base);
|
|
||||||
|
|
||||||
//virtual base must be page-aligned, in bottom p3, and not zero.
|
//virtual base must be page-aligned, in bottom p3, and not zero.
|
||||||
//also frees physical page if owned by process.
|
|
||||||
void unmap_page_for_process(
|
void unmap_page_for_process(
|
||||||
struct process *process, void *virtual_base);
|
struct process *process, void *virtual_base);
|
||||||
|
|
||||||
|
|
@ -128,78 +64,25 @@ void unmap_page_for_process(
|
||||||
void *find_free_process_region(
|
void *find_free_process_region(
|
||||||
struct process *process, uint64_t page_count);
|
struct process *process, uint64_t page_count);
|
||||||
|
|
||||||
//returns 0 on failure, 1 on success.
|
//loaded sections are copied to new pages. once storage drivers are written,
|
||||||
int load_elf(
|
//this should probably just take a file handle so it can load and parse the
|
||||||
|
//header and then load sections directly into user pages.
|
||||||
|
void load_elf(
|
||||||
struct process *process, uint64_t *entry_out,
|
struct process *process, uint64_t *entry_out,
|
||||||
const struct fs_info *fs_info, void *fs_node);
|
const void *elf_start, uint64_t elf_length);
|
||||||
|
|
||||||
//returns 0 on failure.
|
|
||||||
//creates a process and a thread in that process, loads the elf into the process,
|
|
||||||
//and schedules a ready task that sets the running thread to the new thread and
|
|
||||||
//starts user mode at the elf's entry point.
|
|
||||||
struct process *start_elf(const char *uri);
|
|
||||||
|
|
||||||
int syscall_start_elf(const char *path, const struct process_start_info *info);
|
|
||||||
|
|
||||||
void destroy_process(struct process *process);
|
void destroy_process(struct process *process);
|
||||||
//must not be called with syscall stack of the thread being destroyed
|
|
||||||
void destroy_thread(struct thread *thread);
|
|
||||||
|
|
||||||
[[noreturn]] void syscall_illegal_args();
|
|
||||||
|
|
||||||
//returs 1 if [start, start + length) is writable by process, otherwise 0.
|
//returs 1 if [start, start + length) is writable by process, otherwise 0.
|
||||||
int is_mapped_writable(struct process *process, const void *start, uint64_t length);
|
int is_mapped_writable(struct process *process, void *start, uint64_t length);
|
||||||
|
|
||||||
//returs 1 if [start, start + length) is readable by process, otherwise 0.
|
|
||||||
int is_mapped_readable(struct process *process, const void *start, uint64_t length);
|
|
||||||
|
|
||||||
//return 1 if the entire null-terminated string starting at start is readable by process, otherwise 0.
|
|
||||||
int is_mapped_readable_string(struct process *process, const char *start);
|
|
||||||
|
|
||||||
[[noreturn]] void syscall_end_thread();
|
[[noreturn]] void syscall_end_thread();
|
||||||
|
|
||||||
void syscall_map_framebuffer(struct framebuffer_info *info_out);
|
struct syscall_framebuffer_info {
|
||||||
|
uint8_t *fb_base;
|
||||||
|
int fb_width;
|
||||||
|
int fb_height;
|
||||||
|
int fb_pitch;
|
||||||
|
};
|
||||||
|
|
||||||
enum fs_access_result syscall_open_file(const char *path, file_handle_t *handle_out);
|
void syscall_map_framebuffer(struct syscall_framebuffer_info *info_out);
|
||||||
void syscall_close_file(file_handle_t handle);
|
|
||||||
enum fs_access_result syscall_get_file_size(file_handle_t handle, uint64_t *bytes_out);
|
|
||||||
enum fs_access_result syscall_read_file(struct read_file_parameter *parameter);
|
|
||||||
|
|
||||||
void *syscall_map_pages(uint64_t count);
|
|
||||||
|
|
||||||
void syscall_change_page_permissions(void *vma, int writable, int executable);
|
|
||||||
|
|
||||||
enum ipc_dgram_result syscall_ipc_create_dgram_receiver(
|
|
||||||
const char *address, ipc_dgram_receiver_handle_t *handle_out);
|
|
||||||
|
|
||||||
enum ipc_dgram_result syscall_ipc_create_dgram_sender(
|
|
||||||
const char *address, ipc_dgram_sender_handle_t *handle_out);
|
|
||||||
|
|
||||||
//on entry, bytes is maximum accepted packet length.
|
|
||||||
//on exit, if result was IPR_SUCCESS or IPR_TOO_BIG, bytes is actual packet length.
|
|
||||||
//actual packet length will always be positive.
|
|
||||||
enum ipc_dgram_result syscall_ipc_receive_dgram(
|
|
||||||
ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes);
|
|
||||||
|
|
||||||
//bytes must be positive.
|
|
||||||
enum ipc_dgram_result syscall_ipc_send_dgram(
|
|
||||||
ipc_dgram_sender_handle_t handle, const void *data, int bytes);
|
|
||||||
|
|
||||||
//f should not return.
|
|
||||||
void syscall_create_thread(void (*f)(uint64_t x), uint64_t x);
|
|
||||||
|
|
||||||
void set_envvar(struct process *process, const char *key, const char *value);
|
|
||||||
|
|
||||||
//0 if unset
|
|
||||||
struct process_env_pair *get_envvar(struct process *process, const char *key);
|
|
||||||
|
|
||||||
//see comment in include/calcite/syscalls.h
|
|
||||||
void syscall_get_envvar(const char *key, char *value_out, int *value_space);
|
|
||||||
|
|
||||||
mutex_handle_t syscall_create_mutex();
|
|
||||||
void syscall_acquire_mutex(mutex_handle_t handle);
|
|
||||||
void syscall_release_mutex(mutex_handle_t handle);
|
|
||||||
|
|
||||||
event_handle_t syscall_create_event();
|
|
||||||
void syscall_signal_event(event_handle_t handle);
|
|
||||||
void syscall_wait_event(event_handle_t handle);
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
|
|
||||||
bits 64
|
bits 64
|
||||||
default rel
|
|
||||||
|
|
||||||
section .text
|
section .text
|
||||||
|
|
||||||
|
|
|
||||||
123
src/kernel/ps2.c
123
src/kernel/ps2.c
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/ps2.c
|
/* Calcite, src/kernel/ps2.c
|
||||||
* Copyright 2025-2026 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -15,88 +15,26 @@
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "input.h"
|
#include "framebuffer.h"
|
||||||
|
#include "panic.h"
|
||||||
#include "ps2.h"
|
#include "ps2.h"
|
||||||
|
|
||||||
#include <kernel-public/input.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
//defined in ps2.asm
|
//defined in ps2.asm
|
||||||
//returns -1 if no byte available
|
//returns -1 if no byte available
|
||||||
int read_ps2_byte();
|
int read_ps2_byte();
|
||||||
|
|
||||||
static uint8_t keyboard_packet[7];
|
static void process_keyboard_byte(uint8_t byte) {
|
||||||
static int keyboard_packet_length = 0;
|
(void)byte;
|
||||||
|
//TODO
|
||||||
void on_keyboard_irq() {
|
|
||||||
|
|
||||||
int byte = read_ps2_byte();
|
|
||||||
if (byte == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (keyboard_packet_length == 0 && byte != 0xe0 && byte != 0xe1 && byte != 0xf0) {
|
|
||||||
add_keyboard_packet(KEY_FLAG_MAKE | byte);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyboard_packet_length == 1 && keyboard_packet[0] == 0xe0 && byte != 0xf0 && byte != 0x12) {
|
|
||||||
add_keyboard_packet(KEY_FLAG_MAKE | 0x80 | byte);
|
|
||||||
keyboard_packet_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyboard_packet_length == 1 && keyboard_packet[0] == 0xf0) {
|
|
||||||
add_keyboard_packet(byte);
|
|
||||||
keyboard_packet_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyboard_packet_length == 2 && keyboard_packet[0] == 0xe0 && keyboard_packet[1] == 0xf0 && byte != 0x7c) {
|
|
||||||
add_keyboard_packet(0x80 | byte);
|
|
||||||
keyboard_packet_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyboard_packet_length == 3 && keyboard_packet[0] == 0xe0 && keyboard_packet[1] == 0x12 &&
|
|
||||||
keyboard_packet[2] == 0xe0 && byte == 0x7c) {
|
|
||||||
add_keyboard_packet(KEY_FLAG_MAKE | KEY_PRINT_SCREEN);
|
|
||||||
keyboard_packet_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyboard_packet_length == 5 && keyboard_packet[0] == 0xe0 && keyboard_packet[1] == 0xf0 &&
|
|
||||||
keyboard_packet[2] == 0x7c && keyboard_packet[3] == 0xe0 && keyboard_packet[4] == 0xf0 && byte == 0x12) {
|
|
||||||
add_keyboard_packet(KEY_PRINT_SCREEN);
|
|
||||||
keyboard_packet_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyboard_packet_length == 7 && keyboard_packet[0] == 0xe1 && keyboard_packet[1] == 0x14 &&
|
|
||||||
keyboard_packet[2] == 0x77 && keyboard_packet[3] == 0xe1 && keyboard_packet[4] == 0xf0 &&
|
|
||||||
keyboard_packet[5] == 0x14 && keyboard_packet[6] == 0xf0 && byte == 0x77) {
|
|
||||||
add_keyboard_packet(KEY_FLAG_MAKE | KEY_PAUSE_BREAK);
|
|
||||||
add_keyboard_packet(KEY_PAUSE_BREAK);
|
|
||||||
keyboard_packet_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyboard_packet_length == 7) {
|
|
||||||
keyboard_packet_length = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
keyboard_packet[keyboard_packet_length++] = byte;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t mouse_packet[3];
|
static uint8_t mouse_packet[3];
|
||||||
static int mouse_packet_length = 0;
|
static int mouse_packet_length = 0;
|
||||||
|
|
||||||
void on_mouse_irq() {
|
static int total_x = 0;
|
||||||
|
static int total_y = 0;
|
||||||
|
|
||||||
int byte = read_ps2_byte();
|
static void process_mouse_byte(uint8_t byte) {
|
||||||
if (byte == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
mouse_packet[mouse_packet_length] = byte;
|
mouse_packet[mouse_packet_length] = byte;
|
||||||
if (mouse_packet_length < 2) {
|
if (mouse_packet_length < 2) {
|
||||||
|
|
@ -105,14 +43,47 @@ void on_mouse_irq() {
|
||||||
}
|
}
|
||||||
mouse_packet_length = 0;
|
mouse_packet_length = 0;
|
||||||
|
|
||||||
int x_change = mouse_packet[1];
|
int x = mouse_packet[1];
|
||||||
if (mouse_packet[0] & 0x10)
|
if (mouse_packet[0] & 0x10)
|
||||||
x_change -= 256;
|
x -= 256;
|
||||||
|
|
||||||
int y_change = mouse_packet[2];
|
int y = mouse_packet[2];
|
||||||
if (mouse_packet[0] & 0x20)
|
if (mouse_packet[0] & 0x20)
|
||||||
y_change -= 256;
|
y -= 256;
|
||||||
|
|
||||||
add_mouse_movement(x_change, y_change);
|
total_x += x;
|
||||||
|
total_y -= y;
|
||||||
|
|
||||||
|
if (total_x < 0)
|
||||||
|
total_x = 0;
|
||||||
|
if (total_x >= fb_width)
|
||||||
|
total_x = fb_width - 1;
|
||||||
|
|
||||||
|
if (total_y < 0)
|
||||||
|
total_y = 0;
|
||||||
|
if (total_y >= fb_height)
|
||||||
|
total_y = fb_height - 1;
|
||||||
|
|
||||||
|
fb_base[total_y * fb_pitch + total_x * 4] = 0xff;
|
||||||
|
fb_base[total_y * fb_pitch + total_x * 4 + 1] = 0xff;
|
||||||
|
fb_base[total_y * fb_pitch + total_x * 4 + 2] = 0xff;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_keyboard_irq() {
|
||||||
|
while (1) {
|
||||||
|
int byte = read_ps2_byte();
|
||||||
|
if (byte == -1)
|
||||||
|
return;
|
||||||
|
process_keyboard_byte(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_mouse_irq() {
|
||||||
|
while (1) {
|
||||||
|
int byte = read_ps2_byte();
|
||||||
|
if (byte == -1)
|
||||||
|
return;
|
||||||
|
process_mouse_byte(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
; Calcite, src/kernel/scheduler.asm
|
; Calcite, src/kernel/scheduler.c
|
||||||
; Copyright 2025-2026 Benji Dial
|
; Copyright 2025 Benji Dial
|
||||||
;
|
;
|
||||||
; This program is free software: you can redistribute it and/or modify
|
; This program is free software: you can redistribute it and/or modify
|
||||||
; it under the terms of the GNU General Public License as published by
|
; it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -16,11 +16,30 @@
|
||||||
|
|
||||||
|
|
||||||
bits 64
|
bits 64
|
||||||
default rel
|
|
||||||
|
|
||||||
extern resume_next_continuation
|
;referenced in scheduler.c
|
||||||
extern running_thread
|
global user_task_start
|
||||||
extern add_to_queue
|
user_task_start:
|
||||||
|
|
||||||
|
mov cr3, rbx
|
||||||
|
mov rcx, rbp
|
||||||
|
mov r11, 0x200
|
||||||
|
|
||||||
|
xor rax, rax
|
||||||
|
xor rbx, rbx
|
||||||
|
xor rdx, rdx
|
||||||
|
xor rdi, rdi
|
||||||
|
xor rsi, rsi
|
||||||
|
xor rbp, rbp
|
||||||
|
xor r8, r8
|
||||||
|
xor r9, r9
|
||||||
|
xor r10, r10
|
||||||
|
xor r12, r12
|
||||||
|
xor r13, r13
|
||||||
|
xor r14, r14
|
||||||
|
xor r15, r15
|
||||||
|
|
||||||
|
o64 sysret
|
||||||
|
|
||||||
;referenced in scheduler.c
|
;referenced in scheduler.c
|
||||||
global resume_continuation
|
global resume_continuation
|
||||||
|
|
@ -34,49 +53,5 @@ resume_continuation:
|
||||||
mov r13, qword [rdi + 40]
|
mov r13, qword [rdi + 40]
|
||||||
mov r14, qword [rdi + 48]
|
mov r14, qword [rdi + 48]
|
||||||
mov r15, qword [rdi + 56]
|
mov r15, qword [rdi + 56]
|
||||||
mov rdi, qword [rdi + 64]
|
|
||||||
mov qword [running_thread], rdi
|
|
||||||
mov rdi, qword [rdi]
|
|
||||||
mov rdi, qword [rdi]
|
|
||||||
mov cr3, rdi
|
|
||||||
|
|
||||||
jmp rax
|
jmp rax
|
||||||
|
|
||||||
global yield
|
|
||||||
yield:
|
|
||||||
mov qword [rdi], .ret
|
|
||||||
mov qword [rdi + 8], rbx
|
|
||||||
mov qword [rdi + 16], rbp
|
|
||||||
mov qword [rdi + 24], rsp
|
|
||||||
mov qword [rdi + 32], r12
|
|
||||||
mov qword [rdi + 40], r13
|
|
||||||
mov qword [rdi + 48], r14
|
|
||||||
mov qword [rdi + 56], r15
|
|
||||||
mov qword rsi, qword [running_thread]
|
|
||||||
mov qword [rdi + 64], rsi
|
|
||||||
|
|
||||||
jmp resume_next_continuation
|
|
||||||
|
|
||||||
.ret:
|
|
||||||
ret
|
|
||||||
|
|
||||||
global yield_to_queue
|
|
||||||
yield_to_queue:
|
|
||||||
mov qword [rsp - 72], .ret
|
|
||||||
mov qword [rsp - 64], rbx
|
|
||||||
mov qword [rsp - 56], rbp
|
|
||||||
mov qword [rsp - 48], rsp
|
|
||||||
mov qword [rsp - 40], r12
|
|
||||||
mov qword [rsp - 32], r13
|
|
||||||
mov qword [rsp - 24], r14
|
|
||||||
mov qword [rsp - 16], r15
|
|
||||||
mov rdx, qword [running_thread]
|
|
||||||
mov qword [rsp - 8], rdx
|
|
||||||
|
|
||||||
sub rsp, 72
|
|
||||||
mov rsi, rsp
|
|
||||||
call add_to_queue
|
|
||||||
jmp resume_next_continuation
|
|
||||||
|
|
||||||
.ret:
|
|
||||||
ret
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/scheduler.c
|
/* Calcite, src/kernel/scheduler.c
|
||||||
* Copyright 2025-2026 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -16,85 +16,103 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
#include "process.h"
|
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "debug.h"
|
|
||||||
#include "heap.h"
|
#include "heap.h"
|
||||||
|
|
||||||
struct continuation_queue ready_continuations;
|
struct continuation_info {
|
||||||
|
uint64_t rip;
|
||||||
|
uint64_t rbx;
|
||||||
|
uint64_t rbp;
|
||||||
|
uint64_t rsp;
|
||||||
|
uint64_t r12;
|
||||||
|
uint64_t r13;
|
||||||
|
uint64_t r14;
|
||||||
|
uint64_t r15;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct continuation_info **ready_continuations = 0;
|
||||||
|
static int rc_buffer_length = 0;
|
||||||
|
static int rc_read_ptr = 0;
|
||||||
|
static int rc_count = 0;
|
||||||
|
|
||||||
|
#define INITIAL_RC_BUFFER_LENGTH 128
|
||||||
|
|
||||||
void init_scheduler() {
|
void init_scheduler() {
|
||||||
create_queue(&ready_continuations, 128);
|
ready_continuations = heap_alloc(INITIAL_RC_BUFFER_LENGTH * sizeof(void *));
|
||||||
|
rc_buffer_length = INITIAL_RC_BUFFER_LENGTH;
|
||||||
|
for (int i = 0; i < INITIAL_RC_BUFFER_LENGTH; ++i)
|
||||||
|
ready_continuations[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void queue_continuation(struct continuation_info *info) {
|
||||||
|
|
||||||
|
if (rc_count == rc_buffer_length) {
|
||||||
|
|
||||||
|
struct continuation_info **new_rc_buffer =
|
||||||
|
heap_alloc(2 * rc_buffer_length * sizeof(void *));
|
||||||
|
|
||||||
|
memcpy(
|
||||||
|
new_rc_buffer,
|
||||||
|
ready_continuations + rc_read_ptr,
|
||||||
|
(rc_buffer_length - rc_read_ptr) * sizeof(void *));
|
||||||
|
|
||||||
|
memcpy(
|
||||||
|
new_rc_buffer + rc_buffer_length - rc_read_ptr,
|
||||||
|
ready_continuations,
|
||||||
|
rc_read_ptr * sizeof(void *));
|
||||||
|
|
||||||
|
heap_dealloc(ready_continuations, rc_buffer_length * sizeof(void *));
|
||||||
|
|
||||||
|
new_rc_buffer[rc_buffer_length] = info;
|
||||||
|
for (int i = rc_buffer_length + 1; i < 2 * rc_buffer_length; ++i)
|
||||||
|
new_rc_buffer[i] = 0;
|
||||||
|
|
||||||
|
ready_continuations = new_rc_buffer;
|
||||||
|
rc_buffer_length *= 2;
|
||||||
|
rc_read_ptr = 0;
|
||||||
|
++rc_count;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
ready_continuations[(rc_read_ptr + rc_count) % rc_buffer_length] = info;
|
||||||
|
++rc_count;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//defined in scheduler.asm
|
//defined in scheduler.asm
|
||||||
[[noreturn]] void resume_continuation(const struct continuation_info *info);
|
void user_task_start();
|
||||||
|
|
||||||
|
void create_user_task(
|
||||||
|
uint64_t cr3, uint64_t rip, uint64_t rsp) {
|
||||||
|
|
||||||
|
struct continuation_info *info =
|
||||||
|
heap_alloc(sizeof(struct continuation_info));
|
||||||
|
|
||||||
|
info->rip = (uint64_t)&user_task_start;
|
||||||
|
info->rsp = (uint64_t)rsp;
|
||||||
|
info->rbx = cr3;
|
||||||
|
info->rbp = rip;
|
||||||
|
|
||||||
|
queue_continuation(info);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//defined in scheduler.asm
|
||||||
|
[[noreturn]] void resume_continuation(struct continuation_info *info);
|
||||||
|
|
||||||
[[noreturn]] void resume_next_continuation() {
|
[[noreturn]] void resume_next_continuation() {
|
||||||
|
|
||||||
running_thread = 0;
|
while (rc_count == 0)
|
||||||
|
|
||||||
struct continuation_info ci;
|
|
||||||
while (!take_from_queue(&ready_continuations, &ci)) {
|
|
||||||
__asm__ ("sti");
|
|
||||||
__asm__ ("hlt");
|
__asm__ ("hlt");
|
||||||
__asm__ ("cli");
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(running_thread == 0)
|
struct continuation_info *info = ready_continuations[rc_read_ptr];
|
||||||
assert(ci.running_thread != 0)
|
|
||||||
resume_continuation(&ci);
|
ready_continuations[rc_read_ptr] = 0;
|
||||||
|
rc_read_ptr = (rc_read_ptr + 1) % rc_buffer_length;
|
||||||
}
|
--rc_count;
|
||||||
|
|
||||||
void create_queue(struct continuation_queue *queue, int buffer_size) {
|
resume_continuation(info);
|
||||||
queue->cis = heap_alloc(buffer_size * sizeof(struct continuation_info));
|
|
||||||
queue->next_read_index = 0;
|
|
||||||
queue->buffer_size = buffer_size;
|
|
||||||
queue->count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_queue(struct continuation_queue *queue) {
|
|
||||||
assert(queue->count == 0)
|
|
||||||
heap_dealloc(queue->cis, queue->buffer_size * sizeof(struct continuation_info));
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_to_queue(struct continuation_queue *queue, struct continuation_info *ci) {
|
|
||||||
|
|
||||||
if (queue->count == queue->buffer_size) {
|
|
||||||
|
|
||||||
struct continuation_info *new_buffer =
|
|
||||||
heap_alloc(2 * queue->buffer_size * sizeof(struct continuation_info));
|
|
||||||
|
|
||||||
memcpy(
|
|
||||||
new_buffer, &queue->cis[queue->next_read_index],
|
|
||||||
(queue->buffer_size - queue->next_read_index) * sizeof(struct continuation_info));
|
|
||||||
memcpy(
|
|
||||||
&new_buffer[queue->buffer_size - queue->next_read_index],
|
|
||||||
queue->cis, queue->next_read_index * sizeof(struct continuation_info));
|
|
||||||
|
|
||||||
heap_dealloc(queue->cis, queue->buffer_size * sizeof(struct continuation_info));
|
|
||||||
queue->cis = new_buffer;
|
|
||||||
queue->buffer_size *= 2;
|
|
||||||
queue->next_read_index = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(
|
|
||||||
&queue->cis[(queue->next_read_index + queue->count) % queue->buffer_size],
|
|
||||||
ci, sizeof(struct continuation_info));
|
|
||||||
++queue->count;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int take_from_queue(struct continuation_queue *queue, struct continuation_info *ci) {
|
|
||||||
|
|
||||||
if (queue->count == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
memcpy(ci, &queue->cis[queue->next_read_index], sizeof(struct continuation_info));
|
|
||||||
queue->next_read_index = (queue->next_read_index + 1) % queue->buffer_size;
|
|
||||||
--queue->count;
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/scheduler.h
|
/* Calcite, src/kernel/scheduler.h
|
||||||
* Copyright 2025-2026 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -19,46 +19,9 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct thread;
|
|
||||||
|
|
||||||
//scheduler.asm depends on layout
|
|
||||||
struct continuation_info {
|
|
||||||
uint64_t rip;
|
|
||||||
uint64_t rbx;
|
|
||||||
uint64_t rbp;
|
|
||||||
uint64_t rsp;
|
|
||||||
uint64_t r12;
|
|
||||||
uint64_t r13;
|
|
||||||
uint64_t r14;
|
|
||||||
uint64_t r15;
|
|
||||||
struct thread *running_thread;
|
|
||||||
};
|
|
||||||
|
|
||||||
void init_scheduler();
|
void init_scheduler();
|
||||||
|
|
||||||
|
void create_user_task(
|
||||||
|
uint64_t cr3, uint64_t rip, uint64_t rsp);
|
||||||
|
|
||||||
[[noreturn]] void resume_next_continuation();
|
[[noreturn]] void resume_next_continuation();
|
||||||
|
|
||||||
struct continuation_queue {
|
|
||||||
struct continuation_info *cis;
|
|
||||||
int next_read_index;
|
|
||||||
int buffer_size;
|
|
||||||
int count;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern struct continuation_queue ready_continuations;
|
|
||||||
|
|
||||||
void create_queue(struct continuation_queue *queue, int buffer_size);
|
|
||||||
void destroy_queue(struct continuation_queue *queue);
|
|
||||||
|
|
||||||
//ci is copied.
|
|
||||||
void add_to_queue(struct continuation_queue *queue, struct continuation_info *ci);
|
|
||||||
|
|
||||||
//if queue is empty, returns 0. otherwise, copies next read to ci, advances read, and returns 1.
|
|
||||||
int take_from_queue(struct continuation_queue *queue, struct continuation_info *ci);
|
|
||||||
|
|
||||||
//saves a continuation that returns from this function to save_current_continuation_to, then resumes
|
|
||||||
//the next ready continuation (thus doesn't return until save_current_continuation_to is resumed).
|
|
||||||
void yield(struct continuation_info *save_current_continuation_to);
|
|
||||||
|
|
||||||
//same as yield, but instead of saving continuation to a pointer, saves it to the end of the queue.
|
|
||||||
void yield_to_queue(struct continuation_queue *queue);
|
|
||||||
|
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
; Calcite, src/kernel/serial.asm
|
|
||||||
; Copyright 2025 Benji Dial
|
|
||||||
;
|
|
||||||
; This program is free software: you can redistribute it and/or modify
|
|
||||||
; it under the terms of the GNU General Public License as published by
|
|
||||||
; the Free Software Foundation, either version 3 of the License, or
|
|
||||||
; (at your option) any later version.
|
|
||||||
;
|
|
||||||
; This program is distributed in the hope that it will be useful, but
|
|
||||||
; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
; for more details.
|
|
||||||
;
|
|
||||||
; You should have received a copy of the GNU General Public License along
|
|
||||||
; with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
bits 64
|
|
||||||
default rel
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
global init_serial
|
|
||||||
init_serial:
|
|
||||||
mov dx, 0x03f9
|
|
||||||
mov al, 0x00
|
|
||||||
out dx, al
|
|
||||||
|
|
||||||
mov dl, 0xfb
|
|
||||||
mov al, 0x80
|
|
||||||
out dx, al
|
|
||||||
|
|
||||||
mov dl, 0xf8
|
|
||||||
mov al, 0x03
|
|
||||||
out dx, al
|
|
||||||
|
|
||||||
mov dl, 0xf9
|
|
||||||
mov al, 0x00
|
|
||||||
out dx, al
|
|
||||||
|
|
||||||
mov dl, 0xfb
|
|
||||||
mov al, 0x03
|
|
||||||
out dx, al
|
|
||||||
|
|
||||||
mov dl, 0xfa
|
|
||||||
mov al, 0xc7
|
|
||||||
out dx, al
|
|
||||||
|
|
||||||
ret
|
|
||||||
|
|
||||||
global write_serial_string_n
|
|
||||||
write_serial_string_n:
|
|
||||||
test esi, esi
|
|
||||||
jz .ret
|
|
||||||
movzx rcx, esi
|
|
||||||
|
|
||||||
.wait_ready:
|
|
||||||
mov dx, 0x03fd
|
|
||||||
in al, dx
|
|
||||||
test al, 0x20
|
|
||||||
jnz .ready
|
|
||||||
pause
|
|
||||||
jmp .wait_ready
|
|
||||||
|
|
||||||
.ready:
|
|
||||||
mov dl, 0xf8
|
|
||||||
mov al, byte [rdi]
|
|
||||||
out dx, al
|
|
||||||
|
|
||||||
inc rdi
|
|
||||||
loop .wait_ready
|
|
||||||
.ret:
|
|
||||||
ret
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
/* Calcite, src/kernel/serial.c
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "serial.h"
|
|
||||||
#include "debug.h"
|
|
||||||
|
|
||||||
void write_serial_string(const char *string) {
|
|
||||||
int n = 0;
|
|
||||||
while (string[n] != 0)
|
|
||||||
++n;
|
|
||||||
write_serial_string_n(string, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_serial_integer(int integer) {
|
|
||||||
|
|
||||||
if (integer == 0) {
|
|
||||||
write_serial_string_n("0", 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char buffer[10];
|
|
||||||
char *ptr = &buffer[9];
|
|
||||||
|
|
||||||
while (integer != 0) {
|
|
||||||
*ptr = '0' + integer % 10;
|
|
||||||
integer /= 10;
|
|
||||||
--ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
write_serial_string_n(ptr + 1, (buffer + 10) - (ptr + 1));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_serial_hex(uint64_t value, int places) {
|
|
||||||
assert(places <= 16)
|
|
||||||
char buffer[16];
|
|
||||||
for (int i = 0; i < places; ++i)
|
|
||||||
buffer[i] = "0123456789abcdef"[(value >> (4 * (places - i - 1))) & 0xf];
|
|
||||||
write_serial_string_n(buffer, places);
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
; Calcite, src/kernel/syscalls.asm
|
; Calcite, src/kernel/syscalls.asm
|
||||||
; Copyright 2025-2026 Benji Dial
|
; Copyright 2025 Benji Dial
|
||||||
;
|
;
|
||||||
; This program is free software: you can redistribute it and/or modify
|
; This program is free software: you can redistribute it and/or modify
|
||||||
; it under the terms of the GNU General Public License as published by
|
; it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -16,53 +16,37 @@
|
||||||
|
|
||||||
|
|
||||||
bits 64
|
bits 64
|
||||||
default rel
|
|
||||||
|
|
||||||
;defined in syscalls.c
|
|
||||||
extern syscall_entry_c
|
extern syscall_entry_c
|
||||||
|
|
||||||
extern running_thread
|
section .bss
|
||||||
|
|
||||||
|
;this should have guard pages blah blah blah
|
||||||
|
resb 16 << 20
|
||||||
|
syscall_stack_top:
|
||||||
|
|
||||||
section .text
|
section .text
|
||||||
|
|
||||||
;system call number is in rax.
|
;system call number is in rax.
|
||||||
;system call arguments are in rdi, rsi, rdx.
|
;system call arguments are in rdi, rsi, rdx.
|
||||||
;system call returns a value in rax.
|
;system call returns a value in rax.
|
||||||
;depends on layout of struct thread from process.h
|
|
||||||
syscall_entry:
|
syscall_entry:
|
||||||
mov r8, qword [running_thread]
|
mov qword [syscall_stack_top - 8], rsp
|
||||||
test r8, r8
|
mov rsp, syscall_stack_top - 8
|
||||||
jz .assert_fail
|
|
||||||
|
|
||||||
mov r8, qword [r8 + 32]
|
|
||||||
mov qword [r8 - 8], rsp
|
|
||||||
mov rsp, r8
|
|
||||||
sub rsp, 8
|
|
||||||
push rcx
|
|
||||||
push r11
|
push r11
|
||||||
|
push rcx
|
||||||
|
|
||||||
mov rcx, rax
|
mov rcx, rax
|
||||||
cli
|
|
||||||
call syscall_entry_c
|
call syscall_entry_c
|
||||||
sti
|
|
||||||
|
|
||||||
xor rdi, rdi
|
|
||||||
xor rsi, rsi
|
|
||||||
xor rdx, rdx
|
|
||||||
xor r8, r8
|
|
||||||
xor r9, r9
|
|
||||||
xor r10, r10
|
|
||||||
|
|
||||||
pop r11
|
|
||||||
pop rcx
|
pop rcx
|
||||||
|
pop r11
|
||||||
pop rsp
|
pop rsp
|
||||||
o64 sysret
|
o64 sysret
|
||||||
|
|
||||||
.assert_fail:
|
|
||||||
ud2
|
|
||||||
|
|
||||||
global init_syscalls
|
global init_syscalls
|
||||||
init_syscalls:
|
init_syscalls:
|
||||||
|
|
||||||
mov ecx, 0xc0000080
|
mov ecx, 0xc0000080
|
||||||
rdmsr
|
rdmsr
|
||||||
or al, 0x01
|
or al, 0x01
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "syscalls.h"
|
#include "syscalls.h"
|
||||||
#include "debug.h"
|
#include "panic.h"
|
||||||
|
|
||||||
#define MAX_SYSCALL_NUMBER 99
|
#define MAX_SYSCALL_NUMBER 99
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
; Calcite, src/kernel/timer.asm
|
|
||||||
; Copyright 2025 Benji Dial
|
|
||||||
;
|
|
||||||
; This program is free software: you can redistribute it and/or modify
|
|
||||||
; it under the terms of the GNU General Public License as published by
|
|
||||||
; the Free Software Foundation, either version 3 of the License, or
|
|
||||||
; (at your option) any later version.
|
|
||||||
;
|
|
||||||
; This program is distributed in the hope that it will be useful, but
|
|
||||||
; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
; for more details.
|
|
||||||
;
|
|
||||||
; You should have received a copy of the GNU General Public License along
|
|
||||||
; with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
bits 64
|
|
||||||
default rel
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
global init_timer
|
|
||||||
init_timer:
|
|
||||||
|
|
||||||
;generate an irq every 1193 cycles (~ every millisecond)
|
|
||||||
|
|
||||||
mov al, 0x34
|
|
||||||
out 0x43, al
|
|
||||||
|
|
||||||
mov al, 0xa9
|
|
||||||
out 0x40, al
|
|
||||||
|
|
||||||
mov al, 0x04
|
|
||||||
out 0x40, al
|
|
||||||
|
|
||||||
ret
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
/* Calcite, src/kernel/timer.c
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "scheduler.h"
|
|
||||||
#include "timer.h"
|
|
||||||
#include "heap.h"
|
|
||||||
|
|
||||||
struct sleeping_list_entry {
|
|
||||||
struct sleeping_list_entry *next;
|
|
||||||
uint64_t sleeping_until;//pit irqs since boot
|
|
||||||
struct continuation_info continuation;
|
|
||||||
};
|
|
||||||
|
|
||||||
//entries are always sorted increasing by sleeping_until
|
|
||||||
static struct sleeping_list_entry *first_entry = 0;
|
|
||||||
static struct sleeping_list_entry *last_entry = 0;
|
|
||||||
|
|
||||||
//pit irq happens ~ once per millisecond
|
|
||||||
static uint64_t pit_irqs_since_boot = 0;
|
|
||||||
|
|
||||||
static struct sleeping_list_entry *local_sleeping_list_entry_pool = 0;
|
|
||||||
|
|
||||||
void on_pit_irq() {
|
|
||||||
|
|
||||||
++pit_irqs_since_boot;
|
|
||||||
|
|
||||||
while (first_entry != 0 && pit_irqs_since_boot >= first_entry->sleeping_until) {
|
|
||||||
|
|
||||||
add_to_queue(&ready_continuations, &first_entry->continuation);
|
|
||||||
|
|
||||||
struct sleeping_list_entry *old_first = first_entry;
|
|
||||||
first_entry = old_first->next;
|
|
||||||
*(struct sleeping_list_entry **)old_first = local_sleeping_list_entry_pool;
|
|
||||||
local_sleeping_list_entry_pool = old_first;
|
|
||||||
|
|
||||||
if (first_entry == 0)
|
|
||||||
last_entry = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//until in pit irqs since boot.
|
|
||||||
//must be called with interrupts disabled.
|
|
||||||
//on return, interrupts are enabled.
|
|
||||||
static void sleep_until(uint64_t until) {
|
|
||||||
|
|
||||||
struct sleeping_list_entry *entry;
|
|
||||||
if (local_sleeping_list_entry_pool != 0) {
|
|
||||||
entry = local_sleeping_list_entry_pool;
|
|
||||||
local_sleeping_list_entry_pool = *(struct sleeping_list_entry **)entry;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
entry = heap_alloc(sizeof(struct sleeping_list_entry));
|
|
||||||
|
|
||||||
entry->sleeping_until = until;
|
|
||||||
|
|
||||||
if (first_entry == 0) {
|
|
||||||
entry->next = 0;
|
|
||||||
first_entry = entry;
|
|
||||||
last_entry = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (first_entry->sleeping_until >= until) {
|
|
||||||
entry->next = first_entry;
|
|
||||||
first_entry = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (last_entry->sleeping_until <= until) {
|
|
||||||
entry->next = 0;
|
|
||||||
last_entry->next = entry;
|
|
||||||
last_entry = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
struct sleeping_list_entry *just_before = first_entry;
|
|
||||||
while (just_before->next->sleeping_until <= until)
|
|
||||||
just_before = just_before->next;
|
|
||||||
entry->next = just_before->next;
|
|
||||||
just_before->next = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
yield(&entry->continuation);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void syscall_sleep_ms(uint64_t ms_to_sleep) {
|
|
||||||
sleep_until(pit_irqs_since_boot + ms_to_sleep);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t syscall_get_ms_since_boot() {
|
|
||||||
return pit_irqs_since_boot;
|
|
||||||
}
|
|
||||||
|
|
||||||
void syscall_sleep_until_ms_since_boot(uint64_t ms) {
|
|
||||||
sleep_until(ms);
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
/* Calcite, src/kernel/timer.h
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
void init_timer();
|
|
||||||
|
|
||||||
void on_pit_irq();
|
|
||||||
|
|
||||||
void syscall_sleep_ms(uint64_t ms_to_sleep);
|
|
||||||
uint64_t syscall_get_ms_since_boot();
|
|
||||||
void syscall_sleep_until_ms_since_boot(uint64_t ms);
|
|
||||||
|
|
@ -16,47 +16,18 @@
|
||||||
|
|
||||||
|
|
||||||
bits 64
|
bits 64
|
||||||
default rel
|
|
||||||
|
|
||||||
section .text
|
section .text
|
||||||
|
|
||||||
global memcpy
|
;global memcpy
|
||||||
memcpy:
|
;memcpy:
|
||||||
mov rcx, rdx
|
; mov rcx, rdx
|
||||||
rep movsb
|
; rep movsb
|
||||||
ret
|
; ret
|
||||||
|
|
||||||
global memzero
|
;global memzero
|
||||||
memzero:
|
;memzero:
|
||||||
xor al, al
|
; xor al, al
|
||||||
mov rcx, rsi
|
; mov rcx, rsi
|
||||||
rep stosb
|
; rep stosb
|
||||||
ret
|
; ret
|
||||||
|
|
||||||
global outb
|
|
||||||
outb:
|
|
||||||
mov dx, di
|
|
||||||
mov al, sil
|
|
||||||
out dx, al
|
|
||||||
ret
|
|
||||||
|
|
||||||
global inb
|
|
||||||
inb:
|
|
||||||
mov dx, di
|
|
||||||
in al, dx
|
|
||||||
ret
|
|
||||||
|
|
||||||
global outsw
|
|
||||||
outsw:
|
|
||||||
mov rcx, rdx
|
|
||||||
mov dx, di
|
|
||||||
rep outsw
|
|
||||||
ret
|
|
||||||
|
|
||||||
global insw
|
|
||||||
insw:
|
|
||||||
mov rcx, rdx
|
|
||||||
mov dx, di
|
|
||||||
mov rdi, rsi
|
|
||||||
rep insw
|
|
||||||
ret
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/utility.c
|
/* Calcite, src/kernel/utility.c
|
||||||
* Copyright 2025-2026 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "heap.h"
|
|
||||||
|
|
||||||
int strequ(const char *str1, const char *str2) {
|
int strequ(const char *str1, const char *str2) {
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
@ -29,89 +28,12 @@ int strequ(const char *str1, const char *str2) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t end_swap_u32(uint32_t value) {
|
void memcpy(void *to, const void *from, uint64_t bytes) {
|
||||||
return
|
for (uint64_t i = 0; i < bytes; ++i)
|
||||||
(value >> 24) |
|
*(uint8_t *)(to + i) = *(const uint8_t *)(from + i);
|
||||||
(((value >> 16) & 0xff) << 8) |
|
|
||||||
(((value >> 8) & 0xff) << 16) |
|
|
||||||
((value & 0xff) << 24);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void double_buffer_zero(void **buffer, int *length, uint64_t bytes_per_entry) {
|
void memzero(void *start, uint64_t bytes) {
|
||||||
void *new_buffer = heap_alloc(2 * *length * bytes_per_entry);
|
for (uint64_t i = 0; i < bytes; ++i)
|
||||||
memcpy(new_buffer, *buffer, *length * bytes_per_entry);
|
*(uint8_t *)(start + i) = 0;
|
||||||
heap_dealloc(*buffer, *length * bytes_per_entry);
|
|
||||||
memzero(new_buffer + *length * bytes_per_entry, *length * bytes_per_entry);
|
|
||||||
*buffer = new_buffer;
|
|
||||||
*length *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void create_handle_list(struct handle_list *list, uint64_t entry_size) {
|
|
||||||
list->entry_size = entry_size;
|
|
||||||
list->buffer = 0;
|
|
||||||
list->buffer_used_bitmap = 0;
|
|
||||||
list->buffer_size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//must be multiple of 8
|
|
||||||
#define HANDLE_LIST_INITIAL_SIZE 16
|
|
||||||
|
|
||||||
void *add_handle(struct handle_list *list, uint64_t *handle_out) {
|
|
||||||
|
|
||||||
if (list->buffer == 0) {
|
|
||||||
list->buffer = heap_alloc(HANDLE_LIST_INITIAL_SIZE * list->entry_size);
|
|
||||||
list->buffer_used_bitmap = heap_alloc(HANDLE_LIST_INITIAL_SIZE / 8);
|
|
||||||
list->buffer_size = HANDLE_LIST_INITIAL_SIZE;
|
|
||||||
list->buffer_used_bitmap[0] = 1;
|
|
||||||
memzero(list->buffer_used_bitmap + 1, HANDLE_LIST_INITIAL_SIZE / 8 - 1);
|
|
||||||
*handle_out = 0;
|
|
||||||
return list->buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint64_t i = 0; i < list->buffer_size; ++i)
|
|
||||||
if ((list->buffer_used_bitmap[i / 8] & (1 << (i % 8))) == 0) {
|
|
||||||
list->buffer_used_bitmap[i / 8] |= (1 << (i % 8));
|
|
||||||
*handle_out = i;
|
|
||||||
return list->buffer + i * list->entry_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *new_buffer = heap_alloc(2 * list->buffer_size * list->entry_size);
|
|
||||||
uint8_t *new_used_bitmap = heap_alloc(list->buffer_size / 4);
|
|
||||||
|
|
||||||
memcpy(new_buffer, list->buffer, list->buffer_size * list->entry_size);
|
|
||||||
memcpy(new_used_bitmap, list->buffer_used_bitmap, list->buffer_size / 8);
|
|
||||||
|
|
||||||
heap_dealloc(list->buffer, list->buffer_size * list->entry_size);
|
|
||||||
heap_dealloc(list->buffer_used_bitmap, list->buffer_size / 8);
|
|
||||||
|
|
||||||
new_used_bitmap[list->buffer_size / 8] = 1;
|
|
||||||
memzero(new_used_bitmap + list->buffer_size / 8 + 1, list->buffer_size / 8 - 1);
|
|
||||||
|
|
||||||
list->buffer = new_buffer;
|
|
||||||
list->buffer_used_bitmap = new_used_bitmap;
|
|
||||||
list->buffer_size *= 2;
|
|
||||||
|
|
||||||
*handle_out = list->buffer_size / 2;
|
|
||||||
return list->buffer + (list->buffer_size / 2) * list->entry_size;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void *look_up_handle(struct handle_list *list, uint64_t handle) {
|
|
||||||
if (list->buffer == 0 || handle >= list->buffer_size ||
|
|
||||||
(list->buffer_used_bitmap[handle / 8] & (1 << (handle % 8))) == 0)
|
|
||||||
return 0;
|
|
||||||
return list->buffer + handle * list->entry_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void for_each_handle(struct handle_list *list, void (*callback)(void *object)) {
|
|
||||||
for (uint64_t i = 0; i < list->buffer_size; ++i)
|
|
||||||
if ((list->buffer_used_bitmap[i / 8] & (1 << (i % 8))) != 0)
|
|
||||||
(*callback)(list->buffer + i * list->entry_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_handle_list(struct handle_list *list) {
|
|
||||||
if (list->buffer != 0) {
|
|
||||||
heap_dealloc(list->buffer, list->buffer_size * list->entry_size);
|
|
||||||
heap_dealloc(list->buffer_used_bitmap, list->buffer_size / 8);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/kernel/utility.h
|
/* Calcite, src/kernel/utility.h
|
||||||
* Copyright 2025-2026 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -24,34 +24,3 @@ int strequ(const char *str1, const char *str2);
|
||||||
|
|
||||||
void memcpy(void *to, const void *from, uint64_t bytes);
|
void memcpy(void *to, const void *from, uint64_t bytes);
|
||||||
void memzero(void *start, uint64_t bytes);
|
void memzero(void *start, uint64_t bytes);
|
||||||
|
|
||||||
//swaps the endianness of the value
|
|
||||||
uint32_t end_swap_u32(uint32_t value);
|
|
||||||
|
|
||||||
//1. allocates a new buffer with 2 * length * bytes_per_entry bytes
|
|
||||||
//2. copies length * bytes_per_entry bytes from old buffer to new buffer
|
|
||||||
//3. deallocates old buffer
|
|
||||||
//4. zeroes rest of new buffer
|
|
||||||
//5. sets buffer and length to new buffer and twice length
|
|
||||||
void double_buffer_zero(void **buffer, int *length, uint64_t bytes_per_entry);
|
|
||||||
|
|
||||||
void outb(uint16_t port, uint8_t byte);
|
|
||||||
uint8_t inb(uint16_t port);
|
|
||||||
void outsw(uint16_t port, const void *buffer, uint64_t words);
|
|
||||||
void insw(uint16_t port, void *buffer, uint64_t words);
|
|
||||||
|
|
||||||
struct handle_list {
|
|
||||||
uint64_t entry_size;
|
|
||||||
void *buffer;
|
|
||||||
uint8_t *buffer_used_bitmap;
|
|
||||||
//in entries, always a multiple of 8
|
|
||||||
uint64_t buffer_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
void create_handle_list(struct handle_list *list, uint64_t entry_size);
|
|
||||||
//add to list by calling this and writing into returned object
|
|
||||||
void *add_handle(struct handle_list *list, uint64_t *handle_out);
|
|
||||||
//zero if not in list
|
|
||||||
void *look_up_handle(struct handle_list *list, uint64_t handle);
|
|
||||||
void for_each_handle(struct handle_list *list, void (*callback)(void *object));
|
|
||||||
void destroy_handle_list(struct handle_list *list);
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* Calcite, include/kernel-public/ipc.h
|
/* Calcite, src/user-apps/hello/hello.c
|
||||||
* Copyright 2025 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -15,24 +15,21 @@
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#include <calcite/calcite.h>
|
||||||
|
|
||||||
#include <stdint.h>
|
[[noreturn]]
|
||||||
|
void _start() {
|
||||||
|
|
||||||
enum ipc_dgram_result {
|
struct framebuffer_info fb_info;
|
||||||
|
map_framebuffer(&fb_info);
|
||||||
|
|
||||||
IPR_SUCCESS,
|
for (int y = 0; y < fb_info.fb_height; ++y)
|
||||||
|
for (int x = 0; x < fb_info.fb_width; ++x) {
|
||||||
|
uint8_t *pixel = fb_info.fb_base + y * fb_info.fb_pitch + x * 4;
|
||||||
|
pixel[0] = x * 256 / fb_info.fb_width;
|
||||||
|
pixel[1] = y * 256 / fb_info.fb_height;
|
||||||
|
}
|
||||||
|
|
||||||
//on write, packet is bigger than kernel data structure.
|
end_thread();
|
||||||
//on read, packet is bigger than max requested size.
|
|
||||||
IPR_TOO_BIG,
|
|
||||||
|
|
||||||
IPR_BAD_HANDLE,
|
}
|
||||||
|
|
||||||
//on create receiver, someone is already receiving at that address.
|
|
||||||
IPR_IN_USE
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef uint64_t ipc_dgram_receiver_handle_t;
|
|
||||||
typedef uint64_t ipc_dgram_sender_handle_t;
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
/* Calcite, src/user-apps/init/init.c
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <kernel-public/process.h>
|
|
||||||
#include <calcite/file-streams.h>
|
|
||||||
#include <kernel-public/files.h>
|
|
||||||
#include <kernel-public/ipc.h>
|
|
||||||
#include <calcite/syscalls.h>
|
|
||||||
|
|
||||||
#define MAX_LINE_LENGTH 1023
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
|
|
||||||
struct file_stream *rc;
|
|
||||||
if (open_file_stream("root://calcite/init.rc", &rc) != FAR_SUCCESS)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int at_file_end = 0;
|
|
||||||
|
|
||||||
while (!at_file_end) {
|
|
||||||
|
|
||||||
char line[MAX_LINE_LENGTH + 1];
|
|
||||||
int line_length = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
char ch = read_file_stream_byte(rc);
|
|
||||||
|
|
||||||
if (ch == -1) {
|
|
||||||
at_file_end = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (ch == '\n')
|
|
||||||
break;
|
|
||||||
if (line_length == MAX_LINE_LENGTH) {
|
|
||||||
while (1) {
|
|
||||||
ch = read_file_stream_byte(rc);
|
|
||||||
if (ch == -1 || ch == '\n')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
line[line_length++] = ch;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line_length == 0 || line[0] == '#')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
line[line_length] = 0;
|
|
||||||
|
|
||||||
struct process_start_info psi;
|
|
||||||
psi.forwared_envvar_count = 0;
|
|
||||||
psi.set_envvar_count = 0;
|
|
||||||
|
|
||||||
start_elf(line, &psi);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
OUTPUT_FORMAT(elf64-x86-64)
|
|
||||||
OUTPUT_ARCH(i386:x86-64)
|
|
||||||
|
|
||||||
ENTRY(_start)
|
|
||||||
|
|
||||||
PHDRS {
|
|
||||||
rx PT_LOAD FLAGS(5);
|
|
||||||
ro PT_LOAD FLAGS(4);
|
|
||||||
rw PT_LOAD FLAGS(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTIONS {
|
|
||||||
|
|
||||||
. = 0x400000;
|
|
||||||
|
|
||||||
.text : {
|
|
||||||
*(.text .text.*)
|
|
||||||
} : rx
|
|
||||||
|
|
||||||
. = ALIGN(4096);
|
|
||||||
|
|
||||||
.rodata : {
|
|
||||||
*(.rodata .rodata.*)
|
|
||||||
} : ro
|
|
||||||
|
|
||||||
. = ALIGN(4096);
|
|
||||||
|
|
||||||
.data : {
|
|
||||||
*(.data .data.*)
|
|
||||||
} : rw
|
|
||||||
|
|
||||||
. = ALIGN(4096);
|
|
||||||
|
|
||||||
.bss : {
|
|
||||||
*(.bss .bss.*)
|
|
||||||
*(COMMON)
|
|
||||||
} : rw
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,218 +0,0 @@
|
||||||
/* Calcite, src/user-apps/shell/assembler.c
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "assembler.h"
|
|
||||||
|
|
||||||
#include <calcite/syscalls.h>
|
|
||||||
#include <calcite/memory.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
static uint8_t *function_buffer = 0;
|
|
||||||
static int function_buffer_length;
|
|
||||||
static int function_buffer_used;
|
|
||||||
|
|
||||||
struct relocation_info {
|
|
||||||
int offset_into_function;
|
|
||||||
int from_offset_into_function;
|
|
||||||
void *to;
|
|
||||||
struct relocation_info *previous;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct relocation_info *relocations = 0;
|
|
||||||
static char *function_name = 0;
|
|
||||||
static int function_name_length;
|
|
||||||
|
|
||||||
struct function_alloc {
|
|
||||||
void *start;
|
|
||||||
int bytes;
|
|
||||||
struct function_alloc *previous;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct function_alloc *allocs = 0;
|
|
||||||
|
|
||||||
#define FUNCTION_BUFFER_INITIAL_SIZE 4096
|
|
||||||
|
|
||||||
void start_function(const char *name, int name_length) {
|
|
||||||
|
|
||||||
if (function_buffer == 0) {
|
|
||||||
function_buffer_length = FUNCTION_BUFFER_INITIAL_SIZE;
|
|
||||||
function_buffer = heap_alloc(function_buffer_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
function_buffer_used = 0;
|
|
||||||
function_name_length = name_length;
|
|
||||||
|
|
||||||
char *name_copy = heap_alloc(name_length);
|
|
||||||
memcpy(name_copy, name, name_length);
|
|
||||||
function_name = name_copy;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroy_relocations() {
|
|
||||||
struct relocation_info *r = relocations;
|
|
||||||
while (r != 0) {
|
|
||||||
struct relocation_info *p = r->previous;
|
|
||||||
heap_dealloc(r, sizeof(struct relocation_info));
|
|
||||||
r = p;
|
|
||||||
}
|
|
||||||
relocations = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroy_function_allocs(int and_free) {
|
|
||||||
struct function_alloc *a = allocs;
|
|
||||||
while (a != 0) {
|
|
||||||
struct function_alloc *p = a->previous;
|
|
||||||
if (and_free)
|
|
||||||
heap_dealloc(a->start, a->bytes);
|
|
||||||
heap_dealloc(a, sizeof(struct function_alloc));
|
|
||||||
a = p;
|
|
||||||
}
|
|
||||||
allocs = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *get_function_name() {
|
|
||||||
return function_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_function_name_length() {
|
|
||||||
return function_name_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
//must be multiple of 4096.
|
|
||||||
#define MIN_RX_SPACE (1 << 20)
|
|
||||||
|
|
||||||
static void *rx_pointer;
|
|
||||||
static int rx_space = 0;
|
|
||||||
|
|
||||||
void (*end_function())() {
|
|
||||||
|
|
||||||
void *function_start = 0;
|
|
||||||
|
|
||||||
if (function_buffer_used <= rx_space) {
|
|
||||||
function_start = rx_pointer;
|
|
||||||
rx_pointer += function_buffer_used;
|
|
||||||
rx_space -= function_buffer_used;
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
int to_allocate = ((function_buffer_length - 1) / 4096 + 1) * 4096;
|
|
||||||
if (to_allocate < MIN_RX_SPACE)
|
|
||||||
to_allocate = MIN_RX_SPACE;
|
|
||||||
void *pages = map_pages(to_allocate / 4096);
|
|
||||||
for (int i = 0; i < to_allocate; i += 4096)
|
|
||||||
change_page_permissions(pages + i, 1, 1);
|
|
||||||
function_start = pages;
|
|
||||||
rx_pointer = pages + function_buffer_used;
|
|
||||||
rx_space = to_allocate - function_buffer_used;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(function_start, function_buffer, function_buffer_used);
|
|
||||||
|
|
||||||
for (struct relocation_info *r = relocations; r != 0; r = r->previous) {
|
|
||||||
int32_t *at = function_start + r->offset_into_function;
|
|
||||||
void *from = function_start + r->from_offset_into_function;
|
|
||||||
*at = r->to - from;
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy_relocations();
|
|
||||||
destroy_function_allocs(0);
|
|
||||||
function_name = 0;
|
|
||||||
|
|
||||||
return function_start;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void abort_function() {
|
|
||||||
destroy_relocations();
|
|
||||||
destroy_function_allocs(1);
|
|
||||||
if (function_name != 0) {
|
|
||||||
heap_dealloc(function_name, function_name_length);
|
|
||||||
function_name = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void *function_data_alloc(int bytes) {
|
|
||||||
void *start = heap_alloc(bytes);
|
|
||||||
struct function_alloc *a = heap_alloc(sizeof(struct function_alloc));
|
|
||||||
a->start = start;
|
|
||||||
a->bytes = bytes;
|
|
||||||
a->previous = allocs;
|
|
||||||
allocs = a;
|
|
||||||
return start;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void emit_bytes(const uint8_t *bytes, int count) {
|
|
||||||
|
|
||||||
if (function_buffer_used + count > function_buffer_length) {
|
|
||||||
int new_length = function_name_length * 2;
|
|
||||||
while (function_buffer_used + count > function_buffer_length)
|
|
||||||
new_length *= 2;
|
|
||||||
void *new_buffer = heap_alloc(new_length);
|
|
||||||
memcpy(new_buffer, function_buffer, function_buffer_used);
|
|
||||||
heap_dealloc(function_buffer, function_buffer_length);
|
|
||||||
function_buffer = new_buffer;
|
|
||||||
function_buffer_length = new_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(function_buffer + function_buffer_used, bytes, count);
|
|
||||||
function_buffer_used += count;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct relocation_info *new_relocation() {
|
|
||||||
struct relocation_info *r = heap_alloc(sizeof(struct relocation_info));
|
|
||||||
r->previous = relocations;
|
|
||||||
relocations = r;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void emit_ret() {
|
|
||||||
uint8_t byte = 0xc3;
|
|
||||||
emit_bytes(&byte, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void emit_call(void (*to)()) {
|
|
||||||
struct relocation_info *r = new_relocation();
|
|
||||||
r->offset_into_function = function_buffer_used + 1;
|
|
||||||
r->from_offset_into_function = function_buffer_used + 5;
|
|
||||||
r->to = to;
|
|
||||||
uint8_t bytes[5];
|
|
||||||
bytes[0] = 0xe8;
|
|
||||||
emit_bytes(bytes, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
void emit_mov_edi(int value) {
|
|
||||||
uint8_t bytes[5];
|
|
||||||
bytes[0] = 0xbf;
|
|
||||||
*(int32_t *)&bytes[1] = value;
|
|
||||||
emit_bytes(bytes, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
void emit_mov_esi(int value) {
|
|
||||||
uint8_t bytes[5];
|
|
||||||
bytes[0] = 0xbe;
|
|
||||||
*(int32_t *)&bytes[1] = value;
|
|
||||||
emit_bytes(bytes, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
void emit_mov_rdi(uint64_t value) {
|
|
||||||
uint8_t bytes[10];
|
|
||||||
bytes[0] = 0x48;
|
|
||||||
bytes[1] = 0xbf;
|
|
||||||
*(uint64_t *)&bytes[2] = value;
|
|
||||||
emit_bytes(bytes, 10);
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
/* Calcite, src/user-apps/shell/assembler.h
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
//name is copied
|
|
||||||
void start_function(const char *name, int name_length);
|
|
||||||
|
|
||||||
const char *get_function_name();
|
|
||||||
int get_function_name_length();
|
|
||||||
|
|
||||||
//returns interpret
|
|
||||||
void (*end_function())();
|
|
||||||
void abort_function();
|
|
||||||
|
|
||||||
void *function_data_alloc(int bytes);
|
|
||||||
|
|
||||||
void emit_ret();
|
|
||||||
void emit_call(void (*to)());
|
|
||||||
void emit_mov_edi(int value);
|
|
||||||
void emit_mov_esi(int value);
|
|
||||||
void emit_mov_rdi(uint64_t value);
|
|
||||||
|
|
@ -1,226 +0,0 @@
|
||||||
/* Calcite, src/user-apps/shell/builtin-words.c
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <calcite/terminal.h>
|
|
||||||
#include <calcite/memory.h>
|
|
||||||
|
|
||||||
#include "builtin-words.h"
|
|
||||||
#include "assembler.h"
|
|
||||||
#include "runtime.h"
|
|
||||||
|
|
||||||
static void words_interpret() {
|
|
||||||
print_all_words();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dup_interpret() {
|
|
||||||
int x = pop();
|
|
||||||
push(x);
|
|
||||||
push(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void drop_interpret() {
|
|
||||||
pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void swap_interpret() {
|
|
||||||
int y = pop();
|
|
||||||
int x = pop();
|
|
||||||
push(y);
|
|
||||||
push(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rot_interpret() {
|
|
||||||
int z = pop();
|
|
||||||
int y = pop();
|
|
||||||
int x = pop();
|
|
||||||
push(y);
|
|
||||||
push(z);
|
|
||||||
push(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void plus_interpret() {
|
|
||||||
int y = pop();
|
|
||||||
int x = pop();
|
|
||||||
push(x + y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void minus_interpret() {
|
|
||||||
int y = pop();
|
|
||||||
int x = pop();
|
|
||||||
push(x - y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void times_interpret() {
|
|
||||||
int y = pop();
|
|
||||||
int x = pop();
|
|
||||||
push(x * y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void divide_interpret() {
|
|
||||||
int y = pop();
|
|
||||||
int x = pop();
|
|
||||||
if (y == 0)
|
|
||||||
error("division by zero");
|
|
||||||
push(x / y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mod_interpret() {
|
|
||||||
int y = pop();
|
|
||||||
int x = pop();
|
|
||||||
if (y == 0)
|
|
||||||
error("division by zero");
|
|
||||||
push(x % y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void print_integer(int x) {
|
|
||||||
|
|
||||||
if (x == 0) {
|
|
||||||
terminal_write("0 ", 2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_negative;
|
|
||||||
if (x < 0) {
|
|
||||||
x = -x;
|
|
||||||
is_negative = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
is_negative = 0;
|
|
||||||
|
|
||||||
char text[12];
|
|
||||||
text[11] = ' ';
|
|
||||||
int text_ptr = 11;
|
|
||||||
while (x > 0) {
|
|
||||||
--text_ptr;
|
|
||||||
text[text_ptr] = '0' + x % 10;
|
|
||||||
x /= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_negative) {
|
|
||||||
--text_ptr;
|
|
||||||
text[text_ptr] = '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
terminal_write(&text[text_ptr], 12 - text_ptr);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dot_s_interpret() {
|
|
||||||
int *stack_pointer = get_data_stack_pointer();
|
|
||||||
int *stack_top = get_data_stack_top();
|
|
||||||
for (int *i = stack_top - 1; i >= stack_pointer; --i)
|
|
||||||
print_integer(*i);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dot_interpret() {
|
|
||||||
print_integer(pop());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dot_quote_interpret() {
|
|
||||||
const char *start;
|
|
||||||
int length;
|
|
||||||
read_until('"', &start, &length);
|
|
||||||
terminal_write(start, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dot_quote_compile() {
|
|
||||||
const char *start;
|
|
||||||
int length;
|
|
||||||
read_until('"', &start, &length);
|
|
||||||
void *address = function_data_alloc(length);
|
|
||||||
memcpy(address, start, length);
|
|
||||||
emit_mov_rdi((uint64_t)address);
|
|
||||||
emit_mov_esi(length);
|
|
||||||
emit_call((void *)&terminal_write);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cr_interpret() {
|
|
||||||
terminal_write("\n", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void colon_interpret() {
|
|
||||||
|
|
||||||
const char *name_start;
|
|
||||||
int name_length;
|
|
||||||
read_input_word(&name_start, &name_length);
|
|
||||||
if (name_length == 0)
|
|
||||||
error("colon at end of input");
|
|
||||||
|
|
||||||
start_function(name_start, name_length);
|
|
||||||
compiling = 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void semicolon_compile() {
|
|
||||||
|
|
||||||
emit_ret();
|
|
||||||
|
|
||||||
const char *function_name = get_function_name();
|
|
||||||
int function_name_length = get_function_name_length();
|
|
||||||
void (*interpret)() = end_function();
|
|
||||||
|
|
||||||
struct word *word = new_word();
|
|
||||||
word->name_length = function_name_length;
|
|
||||||
word->name = function_name;
|
|
||||||
word->interpret = interpret;
|
|
||||||
word->compile = COMPILE_EMIT_CALL;
|
|
||||||
|
|
||||||
compiling = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add(const char *name, void (*interpret)(), void (*compile)()) {
|
|
||||||
int name_length = 0;
|
|
||||||
while (name[name_length] != 0)
|
|
||||||
++name_length;
|
|
||||||
struct word *w = new_word();
|
|
||||||
w->name_length = name_length;
|
|
||||||
w->name = name;
|
|
||||||
w->interpret = interpret;
|
|
||||||
w->compile = compile;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_compile_only(const char *name, void (*compile)()) {
|
|
||||||
add(name, INTERPRET_ERROR, compile);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_interpret_only(const char *name, void (*interpret)()) {
|
|
||||||
add(name, interpret, COMPILE_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_emit_call(const char *name, void (*interpret)()) {
|
|
||||||
add(name, interpret, COMPILE_EMIT_CALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_builtin_words() {
|
|
||||||
add_emit_call( "words", &words_interpret);
|
|
||||||
add_emit_call( "dup", &dup_interpret);
|
|
||||||
add_emit_call( "drop", &drop_interpret);
|
|
||||||
add_emit_call( "swap", &swap_interpret);
|
|
||||||
add_emit_call( "rot", &rot_interpret);
|
|
||||||
add_emit_call( "+", &plus_interpret);
|
|
||||||
add_emit_call( "-", &minus_interpret);
|
|
||||||
add_emit_call( "*", ×_interpret);
|
|
||||||
add_emit_call( "/", ÷_interpret);
|
|
||||||
add_emit_call( "mod", &mod_interpret);
|
|
||||||
add_emit_call( ".s", &dot_s_interpret);
|
|
||||||
add_emit_call( ".", &dot_interpret);
|
|
||||||
add( ".\"", &dot_quote_interpret, &dot_quote_compile);
|
|
||||||
add_emit_call( "cr", &cr_interpret);
|
|
||||||
add_interpret_only(":", &colon_interpret);
|
|
||||||
add_compile_only( ";", &semicolon_compile);
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
/* Calcite, src/user-apps/shell/builtin-words.h
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
void add_builtin_words();
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
calcite
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
/* Calcite, src/user-apps/shell/main.c
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "builtin-words.h"
|
|
||||||
#include "runtime.h"
|
|
||||||
|
|
||||||
#include <calcite/terminal.h>
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
|
|
||||||
init_terminal();
|
|
||||||
save_return_stack();
|
|
||||||
create_data_stack();
|
|
||||||
add_builtin_words();
|
|
||||||
|
|
||||||
terminal_write("words: ", 7);
|
|
||||||
print_all_words();
|
|
||||||
terminal_write("\n", 1);
|
|
||||||
|
|
||||||
main_loop();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
; Calcite, src/user-apps/shell/runtime.asm
|
|
||||||
; Copyright 2026 Benji Dial
|
|
||||||
;
|
|
||||||
; This program is free software: you can redistribute it and/or modify
|
|
||||||
; it under the terms of the GNU General Public License as published by
|
|
||||||
; the Free Software Foundation, either version 3 of the License, or
|
|
||||||
; (at your option) any later version.
|
|
||||||
;
|
|
||||||
; This program is distributed in the hope that it will be useful, but
|
|
||||||
; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
; for more details.
|
|
||||||
;
|
|
||||||
; You should have received a copy of the GNU General Public License along
|
|
||||||
; with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
bits 64
|
|
||||||
default rel
|
|
||||||
|
|
||||||
section .bss
|
|
||||||
|
|
||||||
saved_return_stack:
|
|
||||||
resq 1
|
|
||||||
|
|
||||||
section .text
|
|
||||||
|
|
||||||
global save_return_stack
|
|
||||||
save_return_stack:
|
|
||||||
mov rax, rsp
|
|
||||||
and ax, 0xf000
|
|
||||||
add rax, 0x1000
|
|
||||||
mov qword [saved_return_stack], rax
|
|
||||||
ret
|
|
||||||
|
|
||||||
global restore_return_stack
|
|
||||||
restore_return_stack:
|
|
||||||
mov rsp, qword [saved_return_stack]
|
|
||||||
push qword 0
|
|
||||||
jmp rdi
|
|
||||||
|
|
@ -1,218 +0,0 @@
|
||||||
/* Calcite, src/user-apps/shell/runtime.c
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "assembler.h"
|
|
||||||
#include "runtime.h"
|
|
||||||
|
|
||||||
#include <calcite/terminal.h>
|
|
||||||
#include <calcite/memory.h>
|
|
||||||
|
|
||||||
static int *data_stack_bottom;
|
|
||||||
static int *data_stack_top;
|
|
||||||
static int *data_stack_pointer;
|
|
||||||
|
|
||||||
#define INITIAL_DATA_STACK_INTS 1024
|
|
||||||
|
|
||||||
void create_data_stack() {
|
|
||||||
data_stack_bottom = heap_alloc(INITIAL_DATA_STACK_INTS * sizeof(int));
|
|
||||||
data_stack_top = data_stack_bottom + INITIAL_DATA_STACK_INTS;
|
|
||||||
data_stack_pointer = data_stack_top;
|
|
||||||
}
|
|
||||||
|
|
||||||
void push(int value) {
|
|
||||||
|
|
||||||
if (data_stack_pointer == data_stack_bottom) {
|
|
||||||
int old_size = data_stack_top - data_stack_bottom;
|
|
||||||
int new_size = old_size * 2;
|
|
||||||
int *new_stack = heap_alloc(new_size * sizeof(int));
|
|
||||||
memcpy(new_stack + old_size, data_stack_bottom, old_size * sizeof(int));
|
|
||||||
heap_dealloc(data_stack_bottom, old_size * sizeof(int));
|
|
||||||
data_stack_bottom = new_stack;
|
|
||||||
data_stack_top = new_stack + new_size;
|
|
||||||
data_stack_pointer = new_stack + old_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
*--data_stack_pointer = value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int pop() {
|
|
||||||
|
|
||||||
if (data_stack_pointer == data_stack_top)
|
|
||||||
error("stack underflow");
|
|
||||||
|
|
||||||
return *(data_stack_pointer++);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int *get_data_stack_pointer() {
|
|
||||||
return data_stack_pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
int *get_data_stack_top() {
|
|
||||||
return data_stack_top;
|
|
||||||
}
|
|
||||||
|
|
||||||
//null-terminated
|
|
||||||
static const char *input_buffer;
|
|
||||||
static const char *input_pointer;
|
|
||||||
|
|
||||||
void set_input(const char *buffer) {
|
|
||||||
input_buffer = buffer;
|
|
||||||
input_pointer = buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int is_ws(char ch) {
|
|
||||||
return ch == ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_input_word(const char **start_out, int *length_out) {
|
|
||||||
const char *start = input_pointer;
|
|
||||||
while (start[0] != 0 && is_ws(start[0]))
|
|
||||||
++start;
|
|
||||||
int length = 0;
|
|
||||||
while (start[length] != 0 && !is_ws(start[length]))
|
|
||||||
++length;
|
|
||||||
*start_out = start;
|
|
||||||
*length_out = length;
|
|
||||||
if (start[length] == 0)
|
|
||||||
input_pointer = &start[length];
|
|
||||||
else
|
|
||||||
input_pointer = &start[length + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_until(char end, const char **start_out, int *length_out) {
|
|
||||||
const char *start = input_pointer;
|
|
||||||
int length = 0;
|
|
||||||
while (start[length] != 0 && start[length] != end)
|
|
||||||
++length;
|
|
||||||
if (start[length] == 0)
|
|
||||||
error("unexpected eof");
|
|
||||||
*start_out = start;
|
|
||||||
*length_out = length;
|
|
||||||
input_pointer = &start[length + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct word *dictionary = 0;
|
|
||||||
|
|
||||||
const struct word *look_up_word(int name_length, const char *name) {
|
|
||||||
for (const struct word *word = dictionary; word != 0; word = word->previous) {
|
|
||||||
if (word->name_length == name_length) {
|
|
||||||
for (int i = 0; i < name_length; ++i)
|
|
||||||
if (word->name[i] != name[i])
|
|
||||||
goto next_word;
|
|
||||||
return word;
|
|
||||||
}
|
|
||||||
next_word:
|
|
||||||
;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct word *new_word() {
|
|
||||||
struct word *word = heap_alloc(sizeof(struct word));
|
|
||||||
word->previous = dictionary;
|
|
||||||
dictionary = word;
|
|
||||||
return word;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_all_words() {
|
|
||||||
for (const struct word *w = dictionary; w != 0; w = w->previous) {
|
|
||||||
terminal_write(w->name, w->name_length);
|
|
||||||
terminal_write(" ", 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[noreturn]] void error(const char *message) {
|
|
||||||
terminal_write("*** error: ", 11);
|
|
||||||
int length = 0;
|
|
||||||
while (message[length] != 0)
|
|
||||||
++length;
|
|
||||||
terminal_write(message, length);
|
|
||||||
terminal_write("\n", 1);
|
|
||||||
abort_function();
|
|
||||||
restore_return_stack(&main_loop);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void interpret_all() {
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
const char *start;
|
|
||||||
int length;
|
|
||||||
read_input_word(&start, &length);
|
|
||||||
if (length == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const struct word *word = look_up_word(length, start);
|
|
||||||
if (word != 0) {
|
|
||||||
if (compiling) {
|
|
||||||
if (word->compile == COMPILE_ERROR)
|
|
||||||
error("interpret-only word in compile context");
|
|
||||||
else if (word->compile == COMPILE_EMIT_CALL)
|
|
||||||
emit_call(word->interpret);
|
|
||||||
else
|
|
||||||
(*word->compile)();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (word->interpret == INTERPRET_ERROR)
|
|
||||||
error("compile-only word in interpret context");
|
|
||||||
else
|
|
||||||
(*word->interpret)();
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_negative;
|
|
||||||
if (start[0] == '-') {
|
|
||||||
is_negative = 1;
|
|
||||||
++start;
|
|
||||||
--length;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
is_negative = 0;
|
|
||||||
|
|
||||||
int abs = 0;
|
|
||||||
for (int i = 0; i < length; ++i) {
|
|
||||||
if (start[i] < '0' || start[i] > '9')
|
|
||||||
error("unknown word");
|
|
||||||
abs = abs * 10 + start[i] - '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
int x = is_negative ? -abs : abs;
|
|
||||||
|
|
||||||
if (compiling) {
|
|
||||||
emit_mov_edi(x);
|
|
||||||
emit_call((void *)&push);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
push(x);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[[noreturn]] void main_loop() {
|
|
||||||
compiling = 0;
|
|
||||||
while (1) {
|
|
||||||
terminal_write("> ", 2);
|
|
||||||
set_input(terminal_readline());
|
|
||||||
interpret_all();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int compiling;
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
/* Calcite, src/user-apps/shell/runtime.h
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
void save_return_stack();
|
|
||||||
//and_call should not return.
|
|
||||||
[[noreturn]] void restore_return_stack(void (*and_call)());
|
|
||||||
|
|
||||||
void create_data_stack();
|
|
||||||
void push(int value);
|
|
||||||
int pop();
|
|
||||||
|
|
||||||
int *get_data_stack_pointer();
|
|
||||||
int *get_data_stack_top();
|
|
||||||
|
|
||||||
//null-terminated. not copied.
|
|
||||||
void set_input(const char *buffer);
|
|
||||||
|
|
||||||
//length_out is set to 0 on eof. skips all whitespace before and up to one character after.
|
|
||||||
void read_input_word(const char **start_out, int *length_out);
|
|
||||||
|
|
||||||
//errors on eof before end. skips end. end not included in length.
|
|
||||||
void read_until(char end, const char **start_out, int *length_out);
|
|
||||||
|
|
||||||
struct word {
|
|
||||||
int name_length;
|
|
||||||
const char *name;
|
|
||||||
void (*interpret)();
|
|
||||||
void (*compile)();
|
|
||||||
const struct word *previous;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define INTERPRET_ERROR ((void (*)())-1)
|
|
||||||
|
|
||||||
#define COMPILE_ERROR ((void (*)())-1)
|
|
||||||
#define COMPILE_EMIT_CALL ((void (*)())-2)
|
|
||||||
|
|
||||||
//returns 0 if not found. name does not need to be null-terminated.
|
|
||||||
const struct word *look_up_word(int name_length, const char *name);
|
|
||||||
struct word *new_word();
|
|
||||||
|
|
||||||
void print_all_words();
|
|
||||||
|
|
||||||
[[noreturn]] void error(const char *message);
|
|
||||||
|
|
||||||
extern int compiling;
|
|
||||||
|
|
||||||
[[noreturn]] void main_loop();
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
calcite
|
|
||||||
flanterm
|
|
||||||
|
|
@ -1,196 +0,0 @@
|
||||||
/* Calcite, src/user-apps/terminal/terminal.c
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <kernel-public/framebuffer.h>
|
|
||||||
#include <kernel-public/process.h>
|
|
||||||
#include <flanterm_backends/fb.h>
|
|
||||||
#include <kernel-public/files.h>
|
|
||||||
#include <kernel-public/input.h>
|
|
||||||
#include <kernel-public/ipc.h>
|
|
||||||
#include <calcite/dispatch.h>
|
|
||||||
#include <calcite/syscalls.h>
|
|
||||||
#include <calcite/terminal.h>
|
|
||||||
#include <calcite/format.h>
|
|
||||||
#include <calcite/memory.h>
|
|
||||||
#include <flanterm.h>
|
|
||||||
|
|
||||||
static struct flanterm_context *flanterm_context;
|
|
||||||
|
|
||||||
static ipc_dgram_receiver_handle_t input_handle;
|
|
||||||
static ipc_dgram_receiver_handle_t output_handle;
|
|
||||||
|
|
||||||
static char keytable[2048];
|
|
||||||
|
|
||||||
static struct dispatch_queue *queue;
|
|
||||||
|
|
||||||
struct got_output_param {
|
|
||||||
int dgram_bytes;
|
|
||||||
void *dgram;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void got_output(void *x) {
|
|
||||||
struct got_output_param *param = x;
|
|
||||||
flanterm_write(flanterm_context, param->dgram, param->dgram_bytes);
|
|
||||||
heap_dealloc(param->dgram, param->dgram_bytes);
|
|
||||||
heap_dealloc(param, sizeof(struct got_output_param));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[noreturn]] static void output_thread(uint64_t) {
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
int dgram_bytes = 0;
|
|
||||||
ipc_receive_dgram(output_handle, 0, &dgram_bytes);
|
|
||||||
|
|
||||||
void *dgram = heap_alloc(dgram_bytes);
|
|
||||||
ipc_receive_dgram(output_handle, dgram, &dgram_bytes);
|
|
||||||
|
|
||||||
struct got_output_param *param = heap_alloc(sizeof(struct got_output_param));
|
|
||||||
param->dgram_bytes = dgram_bytes;
|
|
||||||
param->dgram = dgram;
|
|
||||||
|
|
||||||
dispatch(queue, &got_output, param);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static int flags = 0;
|
|
||||||
static int entering_caps_lock = 0;
|
|
||||||
static int entering_num_lock = 0;
|
|
||||||
|
|
||||||
[[noreturn]] static void input_thread(uint64_t) {
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
int key = wait_for_keyboard_packet();
|
|
||||||
int shift = flags & (TKF_LEFT_SHIFT | TKF_RIGHT_SHIFT);
|
|
||||||
int caps = flags & TKF_CAPS_LOCK;
|
|
||||||
int num = flags & TKF_NUM_LOCK;
|
|
||||||
char printable = keytable[(key & 0xff) | (shift ? 0x100 : 0x0) | (caps ? 0x200 : 0x0) | (num ? 0x400 : 0x0)];
|
|
||||||
|
|
||||||
if (key == KEY_LEFT_SHIFT)
|
|
||||||
flags &= ~TKF_LEFT_SHIFT;
|
|
||||||
else if (key == KEY_RIGHT_SHIFT)
|
|
||||||
flags &= ~TKF_RIGHT_SHIFT;
|
|
||||||
else if (key == KEY_LEFT_CTRL)
|
|
||||||
flags &= ~TKF_LEFT_CTRL;
|
|
||||||
else if (key == KEY_RIGHT_CTRL)
|
|
||||||
flags &= ~TKF_RIGHT_CTRL;
|
|
||||||
else if (key == KEY_LEFT_ALT)
|
|
||||||
flags &= ~TKF_LEFT_ALT;
|
|
||||||
else if (key == KEY_RIGHT_ALT)
|
|
||||||
flags &= ~TKF_RIGHT_ALT;
|
|
||||||
else if (key == KEY_CAPS_LOCK) {
|
|
||||||
if (!entering_caps_lock)
|
|
||||||
flags &= ~TKF_CAPS_LOCK;
|
|
||||||
else
|
|
||||||
entering_caps_lock = 0;
|
|
||||||
}
|
|
||||||
else if (key == KEY_NUM_LOCK) {
|
|
||||||
if (!entering_num_lock)
|
|
||||||
flags &= ~TKF_NUM_LOCK;
|
|
||||||
else
|
|
||||||
entering_num_lock = 0;
|
|
||||||
}
|
|
||||||
else if (key == (KEY_FLAG_MAKE | KEY_LEFT_SHIFT))
|
|
||||||
flags |= TKF_LEFT_SHIFT;
|
|
||||||
else if (key == (KEY_FLAG_MAKE | KEY_RIGHT_SHIFT))
|
|
||||||
flags |= TKF_RIGHT_SHIFT;
|
|
||||||
else if (key == (KEY_FLAG_MAKE | KEY_LEFT_CTRL))
|
|
||||||
flags |= TKF_LEFT_CTRL;
|
|
||||||
else if (key == (KEY_FLAG_MAKE | KEY_RIGHT_CTRL))
|
|
||||||
flags |= TKF_RIGHT_CTRL;
|
|
||||||
else if (key == (KEY_FLAG_MAKE | KEY_LEFT_ALT))
|
|
||||||
flags |= TKF_LEFT_ALT;
|
|
||||||
else if (key == (KEY_FLAG_MAKE | KEY_RIGHT_ALT))
|
|
||||||
flags |= TKF_RIGHT_ALT;
|
|
||||||
else if (key == (KEY_FLAG_MAKE | KEY_CAPS_LOCK)) {
|
|
||||||
if (!(flags & TKF_CAPS_LOCK)) {
|
|
||||||
flags |= TKF_CAPS_LOCK;
|
|
||||||
entering_caps_lock = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (key == (KEY_FLAG_MAKE | KEY_NUM_LOCK)) {
|
|
||||||
if (!(flags & TKF_NUM_LOCK)) {
|
|
||||||
flags |= TKF_NUM_LOCK;
|
|
||||||
entering_num_lock = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int packet = flags | (key << 8) | printable;
|
|
||||||
ipc_send_dgram(input_handle, &packet, sizeof(int));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
|
|
||||||
file_handle_t keytable_handle;
|
|
||||||
if (open_file("root://calcite/qwerty.bin", &keytable_handle) == FAR_SUCCESS) {
|
|
||||||
struct read_file_parameter param;
|
|
||||||
param.handle = keytable_handle;
|
|
||||||
param.buffer = keytable;
|
|
||||||
param.start = 0;
|
|
||||||
param.bytes = 2048;
|
|
||||||
read_file(¶m);
|
|
||||||
close_file(keytable_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t output_pipe = ipc_create_private_dgram_pipe();
|
|
||||||
uint64_t input_pipe = ipc_create_private_dgram_pipe();
|
|
||||||
|
|
||||||
ipc_create_private_dgram_receiver(output_pipe, &output_handle);
|
|
||||||
ipc_create_private_dgram_sender(input_pipe, &input_handle);
|
|
||||||
|
|
||||||
struct framebuffer_info fb;
|
|
||||||
map_framebuffer(&fb);
|
|
||||||
|
|
||||||
flanterm_context =
|
|
||||||
flanterm_fb_init(
|
|
||||||
&heap_alloc, &heap_dealloc, (void *)fb.fb_base, fb.fb_width,
|
|
||||||
fb.fb_height, fb.fb_pitch, 8, 16, 8,
|
|
||||||
8, 8, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 1, 0, 0, 0);
|
|
||||||
|
|
||||||
queue = create_dispatch_queue();
|
|
||||||
|
|
||||||
create_thread(&output_thread, 0);
|
|
||||||
create_thread(&input_thread, 0);
|
|
||||||
|
|
||||||
char input_pipe_hex[17];
|
|
||||||
to_hex(input_pipe, 16, input_pipe_hex);
|
|
||||||
input_pipe_hex[16] = 0;
|
|
||||||
|
|
||||||
char output_pipe_hex[17];
|
|
||||||
to_hex(output_pipe, 16, output_pipe_hex);
|
|
||||||
output_pipe_hex[16] = 0;
|
|
||||||
|
|
||||||
const char *envvars[4] = {
|
|
||||||
"TERMINAL_INPUT_PIPE_ID", input_pipe_hex,
|
|
||||||
"TERMINAL_OUTPUT_PIPE_ID", output_pipe_hex };
|
|
||||||
|
|
||||||
struct process_start_info psi;
|
|
||||||
psi.forwared_envvar_count = 0;
|
|
||||||
psi.set_envvar_count = 2;
|
|
||||||
psi.set_envvars = envvars;
|
|
||||||
|
|
||||||
start_elf("root://calcite/apps/shell/shell.elf", &psi);
|
|
||||||
|
|
||||||
dispatch_loop(queue);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
/* Calcite, src/user-libs/calcite/dispatch.c
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <kernel-public/sync.h>
|
|
||||||
#include <calcite/syscalls.h>
|
|
||||||
#include <calcite/memory.h>
|
|
||||||
|
|
||||||
struct dispatch_callback {
|
|
||||||
void (*callback)(void *x);
|
|
||||||
void *x;
|
|
||||||
struct dispatch_callback *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dispatch_queue {
|
|
||||||
struct dispatch_callback *start;
|
|
||||||
struct dispatch_callback *end;
|
|
||||||
mutex_handle_t mutex;
|
|
||||||
event_handle_t event;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dispatch_queue *create_dispatch_queue() {
|
|
||||||
struct dispatch_queue *queue = heap_alloc(sizeof(struct dispatch_queue));
|
|
||||||
queue->start = 0;
|
|
||||||
queue->end = 0;
|
|
||||||
queue->mutex = create_mutex();
|
|
||||||
queue->event = create_event();
|
|
||||||
return queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dispatch(struct dispatch_queue *queue, void (*callback)(void *x), void *x) {
|
|
||||||
|
|
||||||
struct dispatch_callback *new = heap_alloc(sizeof(struct dispatch_callback));
|
|
||||||
new->callback = callback;
|
|
||||||
new->x = x;
|
|
||||||
new->next = 0;
|
|
||||||
|
|
||||||
acquire_mutex(queue->mutex);
|
|
||||||
|
|
||||||
if (queue->start == 0)
|
|
||||||
queue->start = new;
|
|
||||||
else
|
|
||||||
queue->end->next = new;
|
|
||||||
|
|
||||||
queue->end = new;
|
|
||||||
|
|
||||||
release_mutex(queue->mutex);
|
|
||||||
signal_event(queue->event);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[[noreturn]] void dispatch_loop(struct dispatch_queue *queue) {
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
wait_event(queue->event);
|
|
||||||
acquire_mutex(queue->mutex);
|
|
||||||
|
|
||||||
struct dispatch_callback *start = queue->start;
|
|
||||||
queue->start = 0;
|
|
||||||
queue->end = 0;
|
|
||||||
|
|
||||||
release_mutex(queue->mutex);
|
|
||||||
|
|
||||||
while (start != 0) {
|
|
||||||
(*start->callback)(start->x);
|
|
||||||
struct dispatch_callback *next = start->next;
|
|
||||||
heap_dealloc(start, sizeof(struct dispatch_callback));
|
|
||||||
start = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,150 +0,0 @@
|
||||||
/* Calcite, src/user-libs/calcite/file-streams.c
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <calcite/file-streams.h>
|
|
||||||
#include <kernel-public/files.h>
|
|
||||||
#include <calcite/syscalls.h>
|
|
||||||
#include <calcite/memory.h>
|
|
||||||
|
|
||||||
static uint64_t min(uint64_t a, uint64_t b) {
|
|
||||||
return a < b ? a : b;
|
|
||||||
}
|
|
||||||
|
|
||||||
//in bytes
|
|
||||||
#define FS_BUFFER_SIZE 8192
|
|
||||||
|
|
||||||
struct file_stream {
|
|
||||||
|
|
||||||
file_handle_t handle;
|
|
||||||
|
|
||||||
//both in bytes
|
|
||||||
uint64_t length;
|
|
||||||
uint64_t pointer;
|
|
||||||
|
|
||||||
int is_there_buffer;
|
|
||||||
//in bytes
|
|
||||||
uint64_t buffer_start;
|
|
||||||
uint8_t buffer[FS_BUFFER_SIZE];
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
enum fs_access_result open_file_stream(const char *path, struct file_stream **file_stream_out) {
|
|
||||||
|
|
||||||
file_handle_t handle;
|
|
||||||
enum fs_access_result result = open_file(path, &handle);
|
|
||||||
if (result != FAR_SUCCESS)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
uint64_t length;
|
|
||||||
result = get_file_size(handle, &length);
|
|
||||||
if (result != FAR_SUCCESS) {
|
|
||||||
close_file(handle);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
*file_stream_out = heap_alloc(sizeof(struct file_stream));
|
|
||||||
(*file_stream_out)->handle = handle;
|
|
||||||
(*file_stream_out)->length = length;
|
|
||||||
(*file_stream_out)->pointer = 0;
|
|
||||||
(*file_stream_out)->is_there_buffer = 0;
|
|
||||||
return FAR_SUCCESS;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void close_file_stream(struct file_stream *file_stream) {
|
|
||||||
close_file(file_stream->handle);
|
|
||||||
heap_dealloc(file_stream, sizeof(struct file_stream));
|
|
||||||
}
|
|
||||||
|
|
||||||
enum fs_access_result get_in_buffer(struct file_stream *file_stream, uint64_t pointer) {
|
|
||||||
|
|
||||||
uint64_t bstart = (pointer / FS_BUFFER_SIZE) * FS_BUFFER_SIZE;
|
|
||||||
uint64_t bbytes = min(FS_BUFFER_SIZE, file_stream->length - bstart);
|
|
||||||
|
|
||||||
if (file_stream->is_there_buffer && file_stream->buffer_start == bstart)
|
|
||||||
return FAR_SUCCESS;
|
|
||||||
|
|
||||||
enum fs_access_result result =
|
|
||||||
read_file_splat(file_stream->handle, file_stream->buffer, bstart, bbytes);
|
|
||||||
|
|
||||||
if (result == FAR_SUCCESS) {
|
|
||||||
file_stream->is_there_buffer = 1;
|
|
||||||
file_stream->buffer_start = bstart;
|
|
||||||
return FAR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
file_stream->is_there_buffer = 0;
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
enum fs_access_result read_file_stream(struct file_stream *file_stream, void *buffer, uint64_t bytes) {
|
|
||||||
|
|
||||||
if (bytes == 0)
|
|
||||||
return FAR_SUCCESS;
|
|
||||||
|
|
||||||
if (file_stream->pointer + bytes > file_stream->length)
|
|
||||||
return FAR_OUT_OF_BOUNDS;
|
|
||||||
|
|
||||||
enum fs_access_result result = get_in_buffer(file_stream, file_stream->pointer);
|
|
||||||
if (result != FAR_SUCCESS)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
uint64_t in_buffer = min(bytes, FS_BUFFER_SIZE - (file_stream->pointer - file_stream->buffer_start));
|
|
||||||
memcpy(
|
|
||||||
buffer,
|
|
||||||
file_stream->buffer + file_stream->pointer - file_stream->buffer_start,
|
|
||||||
in_buffer);
|
|
||||||
|
|
||||||
uint64_t pointer = file_stream->pointer + in_buffer;
|
|
||||||
uint64_t left = bytes - in_buffer;
|
|
||||||
void *buffer_pointer = buffer + in_buffer;
|
|
||||||
|
|
||||||
while (left >= FS_BUFFER_SIZE) {
|
|
||||||
enum fs_access_result result =
|
|
||||||
read_file_splat(file_stream->handle, buffer_pointer, pointer, FS_BUFFER_SIZE);
|
|
||||||
if (result != FAR_SUCCESS)
|
|
||||||
return result;
|
|
||||||
pointer += FS_BUFFER_SIZE;
|
|
||||||
left -= FS_BUFFER_SIZE;
|
|
||||||
buffer_pointer += FS_BUFFER_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (left > 0) {
|
|
||||||
enum fs_access_result result = get_in_buffer(file_stream, pointer);
|
|
||||||
if (result != FAR_SUCCESS)
|
|
||||||
return result;
|
|
||||||
memcpy(buffer_pointer, file_stream->buffer, left);
|
|
||||||
}
|
|
||||||
|
|
||||||
file_stream->pointer += bytes;
|
|
||||||
return FAR_SUCCESS;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int read_file_stream_byte(struct file_stream *file_stream) {
|
|
||||||
|
|
||||||
if (file_stream->pointer == file_stream->length)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
enum fs_access_result result = get_in_buffer(file_stream, file_stream->pointer);
|
|
||||||
if (result != FAR_SUCCESS)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return file_stream->buffer[file_stream->pointer++ - file_stream->buffer_start];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
/* Calcite, src/user-libs/calcite/format.c
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <calcite/format.h>
|
|
||||||
|
|
||||||
void to_hex(uint64_t value, int digit_count, char *buffer) {
|
|
||||||
for (int i = 0; i < digit_count; ++i) {
|
|
||||||
char *digit = &buffer[digit_count - 1 - i];
|
|
||||||
uint8_t place_value = (value >> (i * 4)) & 0xf;
|
|
||||||
if (place_value < 10)
|
|
||||||
*digit = '0' + place_value;
|
|
||||||
else
|
|
||||||
*digit = 'a' - 10 + place_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t from_hex(int digit_count, char *buffer) {
|
|
||||||
uint64_t value = 0;
|
|
||||||
for (int i = 0; i < digit_count; ++i) {
|
|
||||||
uint64_t place_value = 0;
|
|
||||||
if (buffer[i] >= '0' && buffer[i] <= '9')
|
|
||||||
place_value = buffer[i] - '0';
|
|
||||||
else if (buffer[i] >= 'a' && buffer[i] <= 'f')
|
|
||||||
place_value = buffer[i] - 'a' + 10;
|
|
||||||
value |= place_value << ((digit_count - 1 - i) * 4);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
@ -1,197 +0,0 @@
|
||||||
/* Calcite, src/user-libs/calcite/memory.c
|
|
||||||
* Copyright 2025-2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <calcite/syscalls.h>
|
|
||||||
#include <calcite/memory.h>
|
|
||||||
|
|
||||||
struct block_list_entry {
|
|
||||||
struct block_list_entry *prev;
|
|
||||||
struct block_list_entry *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct block_list {
|
|
||||||
struct block_list_entry *first_entry;
|
|
||||||
struct block_list_entry *last_entry;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define BASE_BLOCK_SIZE 16ULL
|
|
||||||
#define BLOCK_LEVELS 32
|
|
||||||
//BASE_BLOCK_SIZE << MIN_MAP_BLOCK_LEVEL must be at least 4096
|
|
||||||
#define MIN_MAP_BLOCK_LEVEL 16
|
|
||||||
|
|
||||||
//block_lists[n] is a list of free blocks of size BASE_BLOCK_SIZE * 2^n bytes
|
|
||||||
static struct block_list block_lists[BLOCK_LEVELS];
|
|
||||||
|
|
||||||
void *heap_alloc(uint64_t bytes) {
|
|
||||||
|
|
||||||
bytes = ((bytes - 1) / BASE_BLOCK_SIZE + 1) * BASE_BLOCK_SIZE;
|
|
||||||
|
|
||||||
//TODO: handle this case?
|
|
||||||
if (bytes > (BASE_BLOCK_SIZE << (BLOCK_LEVELS - 1)))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
int block_n_minimum = 0;
|
|
||||||
uint64_t block_size_minimum = BASE_BLOCK_SIZE;
|
|
||||||
while (block_size_minimum < bytes) {
|
|
||||||
++block_n_minimum;
|
|
||||||
block_size_minimum *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int block_n = block_n_minimum;
|
|
||||||
uint64_t block_size = block_size_minimum;
|
|
||||||
while (1) {
|
|
||||||
if (block_n == BLOCK_LEVELS) {
|
|
||||||
block_n = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (block_lists[block_n].first_entry != 0)
|
|
||||||
break;
|
|
||||||
++block_n;
|
|
||||||
block_size *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block_n == -1) {
|
|
||||||
block_n = block_n_minimum > MIN_MAP_BLOCK_LEVEL ? block_n_minimum : MIN_MAP_BLOCK_LEVEL;
|
|
||||||
block_size = BASE_BLOCK_SIZE << block_n;
|
|
||||||
void *start = map_pages(block_size / 4096);
|
|
||||||
if (block_size != bytes)
|
|
||||||
heap_dealloc(start + bytes, block_size - bytes);
|
|
||||||
return start;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct block_list_entry *e = block_lists[block_n].first_entry;
|
|
||||||
if (e->next)
|
|
||||||
e->next->prev = 0;
|
|
||||||
else
|
|
||||||
block_lists[block_n].last_entry = 0;
|
|
||||||
block_lists[block_n].first_entry = e->next;
|
|
||||||
|
|
||||||
void *start = e;
|
|
||||||
if (block_size != bytes)
|
|
||||||
heap_dealloc(start + bytes, block_size - bytes);
|
|
||||||
|
|
||||||
return start;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void heap_dealloc_aligned(void *start, uint64_t block_n, uint64_t block_size) {
|
|
||||||
recurse:
|
|
||||||
|
|
||||||
if (block_size != BLOCK_LEVELS - 1) {
|
|
||||||
|
|
||||||
void *buddy = (void *)((uint64_t)start ^ block_size);
|
|
||||||
|
|
||||||
for (struct block_list_entry *e = block_lists[block_n].first_entry; e != 0; e = e->next)
|
|
||||||
if (e == buddy) {
|
|
||||||
if (e->next)
|
|
||||||
e->next->prev = e->prev;
|
|
||||||
else
|
|
||||||
block_lists[block_n].last_entry = e->prev;
|
|
||||||
if (e->prev)
|
|
||||||
e->prev->next = e->next;
|
|
||||||
else
|
|
||||||
block_lists[block_n].first_entry = e->next;
|
|
||||||
start = (void *)((uint64_t)start & ~block_size);
|
|
||||||
++block_n;
|
|
||||||
block_size *= 2;
|
|
||||||
goto recurse;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block_lists[block_n].first_entry == 0) {
|
|
||||||
struct block_list_entry *e = start;
|
|
||||||
e->next = 0;
|
|
||||||
e->prev = 0;
|
|
||||||
block_lists[block_n].first_entry = e;
|
|
||||||
block_lists[block_n].last_entry = e;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start < (void *)block_lists[block_n].first_entry) {
|
|
||||||
struct block_list_entry *e = start;
|
|
||||||
e->next = block_lists[block_n].first_entry;
|
|
||||||
e->prev = 0;
|
|
||||||
block_lists[block_n].first_entry->prev = e;
|
|
||||||
block_lists[block_n].first_entry = e;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start > (void *)block_lists[block_n].last_entry) {
|
|
||||||
struct block_list_entry *e = start;
|
|
||||||
e->next = 0;
|
|
||||||
e->prev = block_lists[block_n].last_entry;
|
|
||||||
block_lists[block_n].last_entry->next = e;
|
|
||||||
block_lists[block_n].last_entry = e;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct block_list_entry *after = block_lists[block_n].first_entry;
|
|
||||||
while ((void *)after < start)
|
|
||||||
after = after->next;
|
|
||||||
struct block_list_entry *before = after->prev;
|
|
||||||
struct block_list_entry *e = start;
|
|
||||||
|
|
||||||
e->next = after;
|
|
||||||
e->prev = before;
|
|
||||||
after->prev = e;
|
|
||||||
before->next = e;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void heap_dealloc(void *start, uint64_t bytes) {
|
|
||||||
|
|
||||||
bytes = ((bytes - 1) / BASE_BLOCK_SIZE + 1) * BASE_BLOCK_SIZE;
|
|
||||||
//bytes is guaranteed to be at most BASE_BLOCK_SIZE << (BLOCK_LEVELS - 1)
|
|
||||||
|
|
||||||
int block_n = 0;
|
|
||||||
uint64_t block_size = BASE_BLOCK_SIZE;
|
|
||||||
|
|
||||||
while (block_size <= bytes) {
|
|
||||||
if ((uint64_t)start & block_size) {
|
|
||||||
heap_dealloc_aligned(start, block_n, block_size);
|
|
||||||
start += block_size;
|
|
||||||
bytes -= block_size;
|
|
||||||
}
|
|
||||||
++block_n;
|
|
||||||
block_size *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (block_n >= 0) {
|
|
||||||
if (block_size <= bytes) {
|
|
||||||
heap_dealloc_aligned(start, block_n, block_size);
|
|
||||||
start += block_size;
|
|
||||||
bytes -= block_size;
|
|
||||||
}
|
|
||||||
--block_n;
|
|
||||||
block_size /= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void memcpy(void *to, const void *from, uint64_t bytes) {
|
|
||||||
uint8_t *to8 = to;
|
|
||||||
const uint8_t *from8 = from;
|
|
||||||
for (uint64_t i = 0; i < bytes; ++i)
|
|
||||||
to8[i] = from8[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
void memset(void *start, uint8_t value, uint64_t length) {
|
|
||||||
uint8_t *start8 = start;
|
|
||||||
for (uint64_t i = 0; i < length; ++i)
|
|
||||||
start8[i] = value;
|
|
||||||
}
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
|
|
||||||
bits 64
|
bits 64
|
||||||
default rel
|
|
||||||
|
|
||||||
section .text
|
section .text
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Calcite, src/user-libs/calcite/syscalls.c
|
/* Calcite, src/user-libs/calcite/syscalls.c
|
||||||
* Copyright 2025-2026 Benji Dial
|
* Copyright 2025 Benji Dial
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -16,11 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <kernel-public/syscall-numbers.h>
|
#include <kernel-public/syscall-numbers.h>
|
||||||
#include <kernel-public/process.h>
|
#include <calcite/calcite.h>
|
||||||
#include <kernel-public/files.h>
|
|
||||||
#include <kernel-public/sync.h>
|
|
||||||
#include <kernel-public/ipc.h>
|
|
||||||
#include <calcite/syscalls.h>
|
|
||||||
|
|
||||||
//defined in syscalls.asm
|
//defined in syscalls.asm
|
||||||
uint64_t do_syscall
|
uint64_t do_syscall
|
||||||
|
|
@ -34,122 +30,3 @@ uint64_t do_syscall
|
||||||
void map_framebuffer(struct framebuffer_info *info_out) {
|
void map_framebuffer(struct framebuffer_info *info_out) {
|
||||||
do_syscall((uint64_t)info_out, 0, 0, SYSCALL_MAP_FRAMEBUFFER);
|
do_syscall((uint64_t)info_out, 0, 0, SYSCALL_MAP_FRAMEBUFFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum fs_access_result open_file(const char *path, file_handle_t *handle_out) {
|
|
||||||
return do_syscall((uint64_t)path, (uint64_t)handle_out, 1, SYSCALL_OPEN_FILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void close_file(file_handle_t handle) {
|
|
||||||
do_syscall(handle, 0, 0, SYSCALL_CLOSE_FILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum fs_access_result get_file_size(file_handle_t handle, uint64_t *bytes_out) {
|
|
||||||
return do_syscall(handle, (uint64_t)bytes_out, 0, SYSCALL_GET_FILE_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum fs_access_result read_file(struct read_file_parameter *parameter) {
|
|
||||||
return do_syscall((uint64_t)parameter, 0, 0, SYSCALL_READ_FILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum fs_access_result read_file_splat(file_handle_t handle, void *buffer, uint64_t start, uint64_t bytes) {
|
|
||||||
struct read_file_parameter parameter = { .handle = handle, .buffer = buffer, .start = start, .bytes = bytes };
|
|
||||||
return do_syscall((uint64_t)¶meter, 0, 0, SYSCALL_READ_FILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_for_mouse_packet(struct mouse_packet *packet_out) {
|
|
||||||
do_syscall((uint64_t)packet_out, 0, 0, SYSCALL_WAIT_FOR_MOUSE_PACKET);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *map_pages(uint64_t count) {
|
|
||||||
return (void *)do_syscall(count, 0, 0, SYSCALL_MAP_PAGES);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sleep_ms(uint64_t ms_to_sleep) {
|
|
||||||
do_syscall(ms_to_sleep, 0, 0, SYSCALL_SLEEP_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
int start_elf(const char *path, const struct process_start_info *info) {
|
|
||||||
return do_syscall((uint64_t)path, (uint64_t)info, 0, SYSCALL_START_ELF);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ipc_dgram_result ipc_create_dgram_receiver(const char *address, ipc_dgram_receiver_handle_t *handle_out) {
|
|
||||||
return do_syscall(
|
|
||||||
(uint64_t)address, (uint64_t)handle_out, 0, SYSCALL_IPC_CREATE_DGRAM_RECEIVER);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ipc_dgram_result ipc_create_dgram_sender(const char *address, ipc_dgram_sender_handle_t *handle_out) {
|
|
||||||
return do_syscall(
|
|
||||||
(uint64_t)address, (uint64_t)handle_out, 0, SYSCALL_IPC_CREATE_DGRAM_SENDER);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ipc_dgram_result ipc_receive_dgram(ipc_dgram_receiver_handle_t handle, void *buffer, int *bytes) {
|
|
||||||
return do_syscall(
|
|
||||||
handle, (uint64_t)buffer, (uint64_t)bytes, SYSCALL_IPC_RECEIVE_DGRAM);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ipc_dgram_result ipc_send_dgram(ipc_dgram_sender_handle_t handle, const void *data, int bytes) {
|
|
||||||
return do_syscall(handle, (uint64_t)data, bytes, SYSCALL_IPC_SEND_DGRAM);
|
|
||||||
}
|
|
||||||
|
|
||||||
void create_thread(void (*f)(uint64_t x), uint64_t x) {
|
|
||||||
do_syscall((uint64_t)f, x, 0, SYSCALL_CREATE_THREAD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_envvar(const char *key, char *value_out, int *value_space) {
|
|
||||||
do_syscall(
|
|
||||||
(uint64_t)key, (uint64_t)value_out, (uint64_t)value_space, SYSCALL_GET_ENVVAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_handle_t create_mutex() {
|
|
||||||
return do_syscall(0, 0, 0, SYSCALL_CREATE_MUTEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
void acquire_mutex(mutex_handle_t handle) {
|
|
||||||
do_syscall(handle, 0, 0, SYSCALL_ACQUIRE_MUTEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
void release_mutex(mutex_handle_t handle) {
|
|
||||||
do_syscall(handle, 0, 0, SYSCALL_RELEASE_MUTEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
event_handle_t create_event() {
|
|
||||||
return do_syscall(0, 0, 0, SYSCALL_CREATE_EVENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void signal_event(event_handle_t handle) {
|
|
||||||
do_syscall(handle, 0, 0, SYSCALL_SIGNAL_EVENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_event(event_handle_t handle) {
|
|
||||||
do_syscall(handle, 0, 0, SYSCALL_WAIT_EVENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t ipc_create_private_dgram_pipe() {
|
|
||||||
return do_syscall(0, 0, 0, SYSCALL_IPC_CREATE_PRIVATE_DGRAM_PIPE);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ipc_dgram_result ipc_create_private_dgram_receiver(uint64_t id, ipc_dgram_receiver_handle_t *handle_out) {
|
|
||||||
return do_syscall(
|
|
||||||
id, (uint64_t)handle_out, 0, SYSCALL_IPC_CREATE_PRIVATE_DGRAM_RECEIVER);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ipc_dgram_result ipc_create_private_dgram_sender(uint64_t id, ipc_dgram_sender_handle_t *handle_out) {
|
|
||||||
return do_syscall(
|
|
||||||
id, (uint64_t)handle_out, 0, SYSCALL_IPC_CREATE_PRIVATE_DGRAM_SENDER);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t get_ms_since_boot() {
|
|
||||||
return do_syscall(0, 0, 0, SYSCALL_GET_MS_SINCE_BOOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sleep_until_ms_since_boot(uint64_t ms) {
|
|
||||||
do_syscall(ms, 0, 0, SYSCALL_SLEEP_UNTIL_MS_SINCE_BOOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void change_page_permissions(void *vma, int writable, int executable) {
|
|
||||||
do_syscall((uint64_t)vma, writable, executable, SYSCALL_CHANGE_PAGE_PERMISSIONS);
|
|
||||||
}
|
|
||||||
|
|
||||||
int wait_for_keyboard_packet() {
|
|
||||||
return do_syscall(0, 0, 0, SYSCALL_WAIT_FOR_KEYBOARD_PACKET);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,271 +0,0 @@
|
||||||
/* Calcite, src/user-libs/calcite/terminal.c
|
|
||||||
* Copyright 2026 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <kernel-public/files.h>
|
|
||||||
#include <kernel-public/input.h>
|
|
||||||
#include <kernel-public/ipc.h>
|
|
||||||
#include <calcite/syscalls.h>
|
|
||||||
#include <calcite/terminal.h>
|
|
||||||
#include <calcite/format.h>
|
|
||||||
#include <calcite/memory.h>
|
|
||||||
|
|
||||||
static ipc_dgram_receiver_handle_t input_handle;
|
|
||||||
static ipc_dgram_sender_handle_t output_handle;
|
|
||||||
|
|
||||||
static int set_up_terminal_input = 0;
|
|
||||||
static int set_up_terminal_output = 0;
|
|
||||||
|
|
||||||
static uint8_t *output_buffer;
|
|
||||||
static int output_buffer_size;
|
|
||||||
static int output_buffer_used;
|
|
||||||
|
|
||||||
static void output_buffer_size_at_least(int bytes) {
|
|
||||||
|
|
||||||
if (output_buffer_size >= bytes)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int new_size = output_buffer_size * 2;
|
|
||||||
while (new_size < bytes)
|
|
||||||
new_size *= 2;
|
|
||||||
|
|
||||||
uint8_t *new_buffer = heap_alloc(new_size);
|
|
||||||
memcpy(new_buffer, output_buffer, output_buffer_used);
|
|
||||||
heap_dealloc(output_buffer, output_buffer_size);
|
|
||||||
output_buffer = new_buffer;
|
|
||||||
output_buffer_size = new_size;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAX_PACKET_LENGTH 500
|
|
||||||
|
|
||||||
static void send_output(const void *buffer, int bytes) {
|
|
||||||
|
|
||||||
if (set_up_terminal_output == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (bytes >= MAX_PACKET_LENGTH) {
|
|
||||||
ipc_send_dgram(output_handle, buffer, MAX_PACKET_LENGTH);
|
|
||||||
buffer += MAX_PACKET_LENGTH;
|
|
||||||
bytes -= MAX_PACKET_LENGTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytes > 0)
|
|
||||||
ipc_send_dgram(output_handle, buffer, bytes);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_terminal() {
|
|
||||||
|
|
||||||
output_buffer_size = 512;
|
|
||||||
output_buffer_used = 0;
|
|
||||||
output_buffer = heap_alloc(output_buffer_size);
|
|
||||||
|
|
||||||
char hex[17];
|
|
||||||
int bytes = 17;
|
|
||||||
get_envvar("TERMINAL_INPUT_PIPE_ID", hex, &bytes);
|
|
||||||
|
|
||||||
if (bytes == 17 &&
|
|
||||||
ipc_create_private_dgram_receiver(
|
|
||||||
from_hex(16, hex),
|
|
||||||
&input_handle) == IPR_SUCCESS)
|
|
||||||
set_up_terminal_input = 1;
|
|
||||||
|
|
||||||
bytes = 17;
|
|
||||||
get_envvar("TERMINAL_OUTPUT_PIPE_ID", hex, &bytes);
|
|
||||||
|
|
||||||
if (bytes == 17 &&
|
|
||||||
ipc_create_private_dgram_sender(
|
|
||||||
from_hex(16, hex),
|
|
||||||
&output_handle) == IPR_SUCCESS)
|
|
||||||
set_up_terminal_output = 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void terminal_write(const void *data, int bytes) {
|
|
||||||
|
|
||||||
if (bytes == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const uint8_t *data8 = (const uint8_t *)data;
|
|
||||||
|
|
||||||
int last_newline = bytes - 1;
|
|
||||||
while (last_newline >= 0 && data8[last_newline] != '\n')
|
|
||||||
--last_newline;
|
|
||||||
|
|
||||||
if (last_newline == -1) {
|
|
||||||
output_buffer_size_at_least(output_buffer_used + bytes);
|
|
||||||
memcpy(&output_buffer[output_buffer_used], data, bytes);
|
|
||||||
output_buffer_used += bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
if (output_buffer_used != 0) {
|
|
||||||
send_output(output_buffer, output_buffer_used);
|
|
||||||
output_buffer_used = 0;
|
|
||||||
}
|
|
||||||
send_output(data, last_newline + 1);
|
|
||||||
if (last_newline != bytes - 1) {
|
|
||||||
output_buffer_size_at_least(bytes - last_newline - 1);
|
|
||||||
memcpy(output_buffer, data + last_newline + 1, bytes - last_newline - 1);
|
|
||||||
output_buffer_used = bytes - last_newline - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void terminal_flush() {
|
|
||||||
if (output_buffer_used != 0) {
|
|
||||||
send_output(output_buffer, output_buffer_used);
|
|
||||||
output_buffer_used = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int terminal_readkey() {
|
|
||||||
|
|
||||||
if (!set_up_terminal_input)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
terminal_flush();
|
|
||||||
int result;
|
|
||||||
int bytes = sizeof(int);
|
|
||||||
ipc_receive_dgram(input_handle, &result, &bytes);
|
|
||||||
//TODO: handle case where terminal misbehaves and sends more than sizeof(int)
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *readline_buffer = 0;
|
|
||||||
static int readline_buffer_length = 0;
|
|
||||||
static int readline_buffer_used = 0;
|
|
||||||
static int readline_cursor = 0;
|
|
||||||
|
|
||||||
static void room_for_one_more() {
|
|
||||||
|
|
||||||
if (readline_buffer == 0) {
|
|
||||||
readline_buffer_length = 1024;
|
|
||||||
readline_buffer = heap_alloc(readline_buffer_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (readline_buffer_used == readline_buffer_length) {
|
|
||||||
char *new_buffer = heap_alloc(readline_buffer_length * 2);
|
|
||||||
memcpy(new_buffer, readline_buffer, readline_buffer_length);
|
|
||||||
heap_dealloc(readline_buffer, readline_buffer_length);
|
|
||||||
readline_buffer = new_buffer;
|
|
||||||
readline_buffer_length *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *terminal_readline() {
|
|
||||||
|
|
||||||
readline_buffer_used = 0;
|
|
||||||
readline_cursor = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
int key_packet = terminal_readkey();
|
|
||||||
if (!(key_packet & (KEY_FLAG_MAKE << 8)) ||
|
|
||||||
(key_packet & (TKF_LEFT_CTRL | TKF_RIGHT_CTRL | TKF_LEFT_ALT | TKF_RIGHT_ALT)))
|
|
||||||
continue;
|
|
||||||
int key = (key_packet >> 8) & 0xff;
|
|
||||||
int num_lock = key_packet & TKF_NUM_LOCK;
|
|
||||||
|
|
||||||
if (key == KEY_LEFT || (!num_lock && key == KEY_NUM_4)) {
|
|
||||||
if (readline_cursor != 0) {
|
|
||||||
--readline_cursor;
|
|
||||||
terminal_write("\x1b[D", 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_RIGHT || (!num_lock && key == KEY_NUM_6)) {
|
|
||||||
if (readline_cursor != readline_buffer_used) {
|
|
||||||
++readline_cursor;
|
|
||||||
terminal_write("\x1b[C", 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_BACKSPACE) {
|
|
||||||
if (readline_cursor != 0) {
|
|
||||||
for (int i = readline_cursor; i < readline_buffer_used; ++i)
|
|
||||||
readline_buffer[i - 1] = readline_buffer[i];
|
|
||||||
--readline_cursor;
|
|
||||||
--readline_buffer_used;
|
|
||||||
terminal_write("\x1b[D", 3);
|
|
||||||
terminal_write(&readline_buffer[readline_cursor], readline_buffer_used - readline_cursor);
|
|
||||||
terminal_write(" ", 1);
|
|
||||||
for (int i = readline_cursor; i < readline_buffer_used + 1; ++i)
|
|
||||||
terminal_write("\x1b[D", 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_DELETE || (!num_lock && key == KEY_NUM_PERIOD)) {
|
|
||||||
if (readline_cursor != readline_buffer_used) {
|
|
||||||
++readline_cursor;
|
|
||||||
for (int i = readline_cursor; i < readline_buffer_used; ++i)
|
|
||||||
readline_buffer[i - 1] = readline_buffer[i];
|
|
||||||
--readline_cursor;
|
|
||||||
--readline_buffer_used;
|
|
||||||
terminal_write(&readline_buffer[readline_cursor], readline_buffer_used - readline_cursor);
|
|
||||||
terminal_write(" ", 1);
|
|
||||||
for (int i = readline_cursor; i < readline_buffer_used + 1; ++i)
|
|
||||||
terminal_write("\x1b[D", 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_ENTER || key == KEY_NUM_ENTER) {
|
|
||||||
room_for_one_more();
|
|
||||||
readline_buffer[readline_buffer_used] = 0;
|
|
||||||
for (int i = readline_cursor; i < readline_buffer_used; ++i)
|
|
||||||
terminal_write("\x1b[C", 3);
|
|
||||||
terminal_write("\n", 1);
|
|
||||||
return readline_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_TAB)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
else if (key == KEY_HOME || (!num_lock && key == KEY_NUM_7)) {
|
|
||||||
for (int i = 0; i < readline_cursor; ++i)
|
|
||||||
terminal_write("\x1b[D", 3);
|
|
||||||
readline_cursor = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_END || (!num_lock && key == KEY_NUM_1)) {
|
|
||||||
for (int i = readline_cursor; i < readline_buffer_used; ++i)
|
|
||||||
terminal_write("\x1b[C", 3);
|
|
||||||
readline_cursor = readline_buffer_used;
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
char value = key_packet & 0xff;
|
|
||||||
if (value != 0) {
|
|
||||||
room_for_one_more();
|
|
||||||
for (int i = readline_buffer_used; i > readline_cursor; --i)
|
|
||||||
readline_buffer[i] = readline_buffer[i - 1];
|
|
||||||
readline_buffer[readline_cursor] = value;
|
|
||||||
++readline_buffer_used;
|
|
||||||
terminal_write(&readline_buffer[readline_cursor], readline_buffer_used - readline_cursor);
|
|
||||||
++readline_cursor;
|
|
||||||
for (int i = readline_buffer_used; i > readline_cursor; --i)
|
|
||||||
terminal_write("\x1b[D", 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
/* Calcite, src/user-libs/silver/image.c
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <calcite/memory.h>
|
|
||||||
#include <silver/image.h>
|
|
||||||
|
|
||||||
void create_image(int width, int height, struct image **image_out) {
|
|
||||||
*image_out = heap_alloc(sizeof(struct image));
|
|
||||||
(*image_out)->width = width;
|
|
||||||
(*image_out)->height = height;
|
|
||||||
(*image_out)->pixels = heap_alloc(width * height * sizeof(struct pixel));
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_image(struct image *image) {
|
|
||||||
heap_dealloc(image->pixels, image->width * image->height * sizeof(struct pixel));
|
|
||||||
heap_dealloc(image, sizeof(struct image));
|
|
||||||
}
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
/* Calcite, src/user-libs/silver/pam.c
|
|
||||||
* Copyright 2025 Benji Dial
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <calcite/file-streams.h>
|
|
||||||
#include <silver/pam.h>
|
|
||||||
|
|
||||||
static int starts_with(const char *big_string, const char *little_string) {
|
|
||||||
while (*little_string) {
|
|
||||||
if (*big_string != *little_string)
|
|
||||||
return 0;
|
|
||||||
++big_string;
|
|
||||||
++little_string;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int strequ(const char *str1, const char *str2) {
|
|
||||||
while (1) {
|
|
||||||
if (!*str1 && !*str2)
|
|
||||||
return 1;
|
|
||||||
if (*str1 != *str2)
|
|
||||||
return 0;
|
|
||||||
++str1;
|
|
||||||
++str2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int parse_uint(const char *str) {
|
|
||||||
int i = 0;
|
|
||||||
while (*str)
|
|
||||||
i = i * 10 + *(str++) - '0';
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum pam_tupltype {
|
|
||||||
PTT_RGB_ALPHA
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MAX_LINE_LENGTH 1023
|
|
||||||
|
|
||||||
int load_pam(const char *path, struct image **image_out) {
|
|
||||||
|
|
||||||
struct file_stream *stream;
|
|
||||||
if (open_file_stream(path, &stream) != FAR_SUCCESS)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (read_file_stream_byte(stream) != 'P' ||
|
|
||||||
read_file_stream_byte(stream) != '7' ||
|
|
||||||
read_file_stream_byte(stream) != '\n') {
|
|
||||||
close_file_stream(stream);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int width = -1, height = -1, depth = -1, maxval = -1, tupltype = -1;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
char line[MAX_LINE_LENGTH + 1];
|
|
||||||
int line_length = 0;
|
|
||||||
while (1) {
|
|
||||||
int c = read_file_stream_byte(stream);
|
|
||||||
if (c == -1) {
|
|
||||||
close_file_stream(stream);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (c == '\n') {
|
|
||||||
line[line_length] = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (line_length == MAX_LINE_LENGTH) {
|
|
||||||
close_file_stream(stream);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
line[line_length++] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line[0] == '#')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (starts_with(line, "WIDTH "))
|
|
||||||
width = parse_uint(&line[6]);
|
|
||||||
else if (starts_with(line, "HEIGHT "))
|
|
||||||
height = parse_uint(&line[7]);
|
|
||||||
else if (starts_with(line, "DEPTH "))
|
|
||||||
depth = parse_uint(&line[6]);
|
|
||||||
else if (starts_with(line, "MAXVAL "))
|
|
||||||
maxval = parse_uint(&line[7]);
|
|
||||||
else if (strequ(line, "TUPLTYPE RGB_ALPHA"))
|
|
||||||
tupltype = PTT_RGB_ALPHA;
|
|
||||||
else if (strequ(line, "ENDHDR"))
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (width == -1 || height == -1 || depth != 4 || maxval != 255 || tupltype != PTT_RGB_ALPHA) {
|
|
||||||
close_file_stream(stream);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
create_image(width, height, image_out);
|
|
||||||
|
|
||||||
if (read_file_stream(
|
|
||||||
stream,
|
|
||||||
(*image_out)->pixels,
|
|
||||||
width * height * 4) != FAR_SUCCESS) {
|
|
||||||
close_file_stream(stream);
|
|
||||||
destroy_image(*image_out);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
close_file_stream(stream);
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue