diff --git a/doc/devices.md b/doc/devices.md new file mode 100644 index 000000000..ea889d16b --- /dev/null +++ b/doc/devices.md @@ -0,0 +1,210 @@ +# MOROS Devices + +Creating the devices in the file system: + + write /dev/ + write /dev/ata/ + write /dev/ata/0/ + write /dev/ata/0/0 -d ata-0-0 + write /dev/ata/0/1 -d ata-0-1 + write /dev/ata/1/ + write /dev/ata/1/0 -d ata-1-0 + write /dev/ata/1/1 -d ata-1-1 + write /dev/clk/ + write /dev/clk/boot -d clk-boot + write /dev/clk/epoch -d clk-epoch + write /dev/clk/rtc -d clk-rtc + write /dev/console -d console + write /dev/net/ + write /dev/net/tcp -d net-tcp + write /dev/net/udp -d net-udp + write /dev/net/gw -d net-gw + write /dev/net/ip -d net-ip + write /dev/net/mac -d net-mac + write /dev/net/usage -d net-usage + write /dev/null -d null + write /dev/random -d random + write /dev/speaker -d speaker + write /dev/vga/ + write /dev/vga/buffer -d vga-buffer + write /dev/vga/font -d vga-font + write /dev/vga/mode -d vga-mode + write /dev/vga/palette -d vga-palette + +## Clock Devices + +Reading the number of seconds since boot: + + > read /dev/clk/boot + 89.570360 + +Reading the number of seconds since Unix Epoch: + + > read /dev/clk/epoch + 1730398385.175973 + +Reading the real time clock (RTC): + + > read /dev/clk/rtc + 2024-10-31 18:20:02 + +Changing the system time: + + > print 2025-01-01 00:00:00 => /dev/clk/rtc + [580.327629] RTC 2025-01-01 00:00:00 +0000 + +## Console Device + +Reading `/dev/console` with a 4 bytes buffer will return a character from the +keyboard or the serial interface. Reading with a larger buffer will return a +complete line. + +## Network Devices + +### Network Config Devices + +The prefered way to setup the network is to use the `dhcp` command: + + > dhcp + [958.810995] NET IP 10.0.2.15/24 + [958.812995] NET GW 10.0.2.2 + [958.818994] NET DNS 10.0.2.3 + +But it is possible to do it manually with the `/dev/net/ip` and `/dev/net/gw` +device files, and the `/ini/dns` configuration file: + + > print 10.0.2.15/24 => /dev/net/ip + [975.123511] NET IP 10.0.2.15/24 + + > print 10.0.2.2 => /dev/net/gw + [985.646908] NET GW 10.0.2.2 + + > print 10.0.2.3 => /ini/dns + +Reading `/dev/net/mac` will return the MAC address: + + > read /dev/net/mac + 52-54-00-12-34-56 + +### Network Usage Device + +Reading `/dev/net/usage` will return the network usage: + + > read /dev/net/usage + 0 0 0 0 + + > dhcp + [7.910795] NET IP 10.0.2.15/24 + [7.911795] NET GW 10.0.2.2 + [7.915795] NET DNS 10.0.2.3 + + > read /dev/net/usage + 2 1180 2 620 + + > http example.com => /dev/null + + > read /dev/net/usage + 10 3306 10 1151 + +The output format is: + + + +### Network Socket Devices + +Opening `/dev/net/tcp` or `/dev/net/udp` with the `OPEN` syscall and the device +flag will return a file handle for a TCP or UDP socket supporting the standard +`READ` and `WRITE` syscalls after establishing a connection using the +`CONNECT`, or `LISTEN` and `ACCEPT` syscalls. + +The size of those files give the maximum size of the buffer that can be used +when reading or writing to a socket: + + > list /dev/net + 1446 2024-09-28 09:57:55 tcp + 1458 2024-09-28 09:57:55 udp + +Reading a socket with a 1 byte buffer will return the status of the socket: + + +-----+--------------+ + | Bit | Status | + +-----+--------------+ + | 0 | Is Listening | + | 1 | Is Active | + | 2 | Is Open | + | 3 | Can Send | + | 4 | May Send | + | 5 | Can Recv | + | 6 | May Recv | + | 7 | Reserved | + +-----+--------------+ + +## Speaker Device + +Playing a 440 Hz sound on the PC speaker: + + > print 440 => /dev/speaker + +Stopping the sound: + + > print 0 => /dev/speaker + +## Null Device + +Writing to `/dev/null` will discard any data sent to it: + + > print hello + hello + + > print hello => /dev/null + +It can be used to suppress errors: + + > copy none.txt some.txt + Error: Could not read file 'none.txt' + + > copy none.txt some.txt [2]=> /dev/null + +## Random Device + +Reading from `/dev/random` will return bytes from a cryptographically secure +random number generator that uses the [HC-128][1] algorithm seeded from the +[RDRAND][2] instruction when available. + +[1]: https://en.wikipedia.org/wiki/HC-256 +[2]: https://en.wikipedia.org/wiki/RDRAND + +## VGA Devices + +### VGA Font Device + +Changing the VGA font: + + > copy /ini/fonts/zap-light-8x16.psf /dev/vga/font + +### VGA Mode Device + +Changing the VGA mode: + + > print 320x200 => /dev/vga/mode + +The accepted modes are: + +- `80x25` for the primary text mode with 16 colors +- `320x200` for the primary graphics mode with 256 colors +- `640x480` for the secondary graphics mode with 16 colors + +It is possible to read the current mode from this device file. + +### VGA Palette Device + +Changing the VGA palette is done by writting a 768 bytes buffer to +`/dev/vga/palette` containing the RGB values of 256 colors. + +It is possible to read the current palette from this device file. + +### VGA Buffer Device + +Changing the VGA framebuffer is done by writting a 64 KB buffer to +`/dev/vga/buffer` containing the color index of each pixel on the screen while +in `320x200` mode. diff --git a/doc/filesystem.md b/doc/filesystem.md index a94e627f5..7d183a620 100644 --- a/doc/filesystem.md +++ b/doc/filesystem.md @@ -57,6 +57,9 @@ The next step during setup is to create the directory structure: > write /usr/ # User directories > write /var/ # Variable files +See the [devices](devices.md) documentation to create the device files in the +`/dev` directory. + Then the following should be added to the boot script with the command `edit /ini/boot.sh` to allow MOROS to finish booting: @@ -157,7 +160,7 @@ Structure: A directory entry represents a file or a directory contained inside a directory. Each entry use a variable number of bytes that must fit inside the data of one block. Those bytes represent the kind of entry (file or dir), the -address of the first block, the filesize (max 4GB), the last modified time in +address of the first block, the filesize (max 4 GB), the last modified time in seconds since Unix Epoch, the length of the filename, and the filename (max 255 chars) of the entry. @@ -176,8 +179,9 @@ Structure: ### FileInfo -The `info` syscall on a file or directory and the `read` syscall on a directory -return a subset of a directory entry for userspace programs. +The `INFO` syscall on a file or directory and the `READ` syscall on a directory +return a subset of a directory entry for userspace programs. See the +[syscalls](syscalls.md) documentation for more information. Structure: diff --git a/doc/images/moros.png b/doc/images/moros.png index 7259236c4..f27db8801 100644 Binary files a/doc/images/moros.png and b/doc/images/moros.png differ diff --git a/doc/manual.md b/doc/manual.md index 6ad1caf10..4677c3821 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -5,37 +5,40 @@ During boot MOROS will display its version followed by the memory layout, memory size, processor, devices, network cards, disks, and the real time clock. - [0.250962] MOROS v0.10.4 - [0.256961] MEM [0x00000000000000-0x00000000000FFF] FrameZero - [0.256961] MEM [0x00000000001000-0x00000000004FFF] PageTable - [0.256961] MEM [0x00000000005000-0x00000000016FFF] Bootloader - [0.256961] MEM [0x00000000017000-0x00000000017FFF] BootInfo - [0.256961] MEM [0x00000000018000-0x0000000009EFFF] Kernel - [0.256961] MEM [0x0000000009F000-0x0000000009FFFF] Reserved - [0.256961] MEM [0x000000000F0000-0x000000000FFFFF] Reserved - [0.256961] MEM [0x00000000100000-0x00000000104FFF] Kernel - [0.256961] MEM [0x00000000105000-0x00000000304FFF] KernelStack - [0.256961] MEM [0x00000000305000-0x000000003FFFFF] Usable - [0.256961] MEM [0x00000000400000-0x000000005EEFFF] Kernel - [0.256961] MEM [0x000000005EF000-0x000000005FFFFF] PageTable - [0.256961] MEM [0x00000000600000-0x00000001FDFFFF] Usable - [0.256961] MEM [0x00000001FE0000-0x00000001FFFFFF] Reserved - [0.256961] MEM [0x000000FFFC0000-0x000000FFFFFFFF] Reserved - [0.256961] MEM 32704 KB - [0.282957] CPU GenuineIntel - [0.283957] CPU Intel(R) Core(TM)2 Duo CPU T7700 @ 2.40GHz - [0.284957] RNG RDRAND unavailable - [0.291956] PCI 0000:00:00 [8086:1237] - [0.292955] PCI 0000:01:00 [8086:7000] - [0.292955] PCI 0000:01:01 [8086:7010] - [0.292955] PCI 0000:01:03 [8086:7113] - [0.293955] PCI 0000:02:00 [1234:1111] - [0.293955] PCI 0000:03:00 [8086:100E] - [0.301954] NET DRV E1000 - [0.303954] NET MAC 52-54-00-12-34-56 - [0.308953] ATA 0:0 QEMU HARDDISK QM00001 (32 MB) - [0.310953] MFS Superblock found in ATA 0:0 - [0.315952] RTC 2024-06-19 11:54:13 +0000 + [0.250962] SYS MOROS v0.10.4-89-g6b3f825 + [0.254961] MEM [0x00000000000000-0x00000000000FFF] FrameZero + [0.255961] MEM [0x00000000001000-0x00000000004FFF] PageTable + [0.256961] MEM [0x00000000005000-0x00000000014FFF] Bootloader + [0.256961] MEM [0x00000000015000-0x00000000015FFF] BootInfo + [0.257961] MEM [0x00000000016000-0x0000000009EFFF] Kernel + [0.258961] MEM [0x0000000009F000-0x0000000009FFFF] Reserved + [0.259960] MEM [0x000000000A0000-0x000000000EFFFF] Unmapped + [0.260960] MEM [0x000000000F0000-0x000000000FFFFF] Reserved + [0.261960] MEM [0x00000000100000-0x0000000010BFFF] Kernel + [0.261960] MEM [0x0000000010C000-0x0000000030BFFF] KernelStack + [0.262960] MEM [0x0000000030C000-0x000000003FFFFF] Usable + [0.262960] MEM [0x00000000400000-0x000000005FDFFF] Kernel + [0.263960] MEM [0x000000005FE000-0x0000000060EFFF] PageTable + [0.264960] MEM [0x0000000060F000-0x00000001FDFFFF] Usable + [0.264960] MEM [0x00000001FE0000-0x00000001FFFFFF] Reserved + [0.265960] MEM [0x00000002000000-0x000000FFFBFFFF] Unmapped + [0.265960] MEM [0x000000FFFC0000-0x000000FFFFFFFF] Reserved + [0.266959] RAM 32 MB + [0.363945] CPU GenuineIntel + [0.363945] CPU Intel(R) Core(TM)2 Duo CPU T7700 @ 2.40GHz + [0.368944] CPU BP:0 running + [0.369944] CPU AP:1 waiting + [0.398939] RNG RDRAND unavailable + [0.413937] PCI 0000:00:00 [8086:1237] + [0.414937] PCI 0000:01:00 [8086:7000] + [0.414937] PCI 0000:01:01 [8086:7010] + [0.414937] PCI 0000:01:03 [8086:7113] + [0.415937] PCI 0000:02:00 [1234:1111] + [0.415937] PCI 0000:03:00 [8086:100E] + [0.422936] NET DRV E1000 + [0.424935] NET MAC 52-54-00-12-34-56 + [0.431934] ATA 0:0 QEMU HARDDISK QM00001 (32 MB) + [0.436933] RTC 2024-11-02 18:18:38 +0000 ## Installation @@ -62,82 +65,74 @@ commands to test the system or `install` to setup the MFS is now mounted to '/' Populating filesystem... - Created '/bin' - Created '/dev' - Created '/ini' - Created '/lib' - Created '/net' - Created '/src' - Created '/tmp' - Created '/usr' - Created '/var' - Fetched '/bin/clear' - Fetched '/bin/halt' - Fetched '/bin/ntp' - Fetched '/bin/print' - Fetched '/bin/reboot' - Fetched '/bin/sleep' - Created '/dev/ata' - Created '/dev/ata/0' - Created '/dev/ata/0/0' - Created '/dev/ata/0/1' - Created '/dev/ata/1' - Created '/dev/ata/1/0' - Created '/dev/ata/1/1' - Created '/dev/clk' - Created '/dev/clk/boot' - Created '/dev/clk/epoch' - Created '/dev/clk/rtc' - Created '/dev/null' - Created '/dev/random' - Created '/dev/console' - Created '/dev/net' - Created '/dev/net/tcp' - Created '/dev/net/udp' - Fetched '/ini/banner.txt' - Fetched '/ini/boot.sh' - Fetched '/ini/lisp.lsp' - Fetched '/ini/shell.sh' - Fetched '/ini/version.txt' - Created '/ini/palettes' - Fetched '/ini/palettes/gruvbox-dark.sh' - Fetched '/ini/palettes/gruvbox-light.sh' - Created '/ini/fonts' - Fetched '/ini/fonts/zap-light-8x16.psf' - Fetched '/ini/fonts/zap-vga-8x16.psf' - Created '/lib/lisp' - Fetched '/lib/lisp/alias.lsp' - Fetched '/lib/lisp/core.lsp' - Fetched '/lib/lisp/file.lsp' - Fetched '/tmp/alice.txt' - Fetched '/tmp/machines.txt' - Created '/tmp/lisp' - Fetched '/tmp/lisp/colors.lsp' - Fetched '/tmp/lisp/doc.lsp' - Fetched '/tmp/lisp/factorial.lsp' - Fetched '/tmp/lisp/fibonacci.lsp' - Fetched '/tmp/lisp/geotime.lsp' - Fetched '/tmp/lisp/pi.lsp' - Fetched '/tmp/lisp/sum.lsp' - Created '/tmp/life' - Fetched '/tmp/life/centinal.cells' - Fetched '/tmp/life/flower-of-eden.cells' - Fetched '/tmp/life/garden-of-eden.cells' - Fetched '/tmp/life/glider-gun.cells' - Fetched '/tmp/life/pentadecathlon.cells' - Fetched '/tmp/life/queen-bee-shuttle.cells' - Fetched '/tmp/life/ship-in-a-bottle.cells' - Fetched '/tmp/life/thunderbird.cells' - Fetched '/tmp/life/wing.cells' - Created '/tmp/beep' - Fetched '/tmp/beep/tetris.sh' - Fetched '/tmp/beep/starwars.sh' - Fetched '/tmp/beep/mario.sh' - Created '/var/log' - Created '/var/www' - Fetched '/var/www/index.html' - Fetched '/var/www/moros.css' - Fetched '/var/www/moros.png' + Creating '/bin' + Creating '/dev' + Creating '/ini' + Creating '/lib' + Creating '/net' + Creating '/src' + Creating '/tmp' + Creating '/usr' + Creating '/var' + Fetching '/bin/clear' + Fetching '/bin/get' + Fetching '/bin/halt' + Fetching '/bin/ntp' + Fetching '/bin/pkg' + Fetching '/bin/print' + Fetching '/bin/reboot' + Fetching '/bin/sleep' + Creating '/dev/ata' + Creating '/dev/ata/0' + Creating '/dev/ata/1' + Creating '/dev/clk' + Creating '/dev/net' + Creating '/dev/vga' + Creating '/dev/ata/0/0' + Creating '/dev/ata/0/1' + Creating '/dev/ata/1/0' + Creating '/dev/ata/1/1' + Creating '/dev/clk/boot' + Creating '/dev/clk/epoch' + Creating '/dev/clk/rtc' + Creating '/dev/console' + Creating '/dev/net/tcp' + Creating '/dev/net/udp' + Creating '/dev/net/gw' + Creating '/dev/net/ip' + Creating '/dev/net/mac' + Creating '/dev/net/usage' + Creating '/dev/null' + Creating '/dev/random' + Creating '/dev/speaker' + Creating '/dev/vga/buffer' + Creating '/dev/vga/font' + Creating '/dev/vga/mode' + Creating '/dev/vga/palette' + Fetching '/ini/banner.txt' + Fetching '/ini/boot.sh' + Fetching '/ini/lisp.lsp' + Fetching '/ini/shell.sh' + Fetching '/ini/version.txt' + Creating '/ini/palettes' + Fetching '/ini/palettes/default.sh' + Fetching '/ini/palettes/gruvbox-dark.sh' + Fetching '/ini/palettes/gruvbox-light.sh' + Creating '/ini/fonts' + Fetching '/ini/fonts/zap-light-8x16.psf' + Creating '/lib/lisp' + Fetching '/lib/lisp/alias.lsp' + Fetching '/lib/lisp/core.lsp' + Fetching '/lib/lisp/file.lsp' + Fetching '/lib/lisp/math.lsp' + Fetching '/tmp/alice.txt' + Fetching '/tmp/machines.txt' + Creating '/var/log' + Creating '/var/www' + Fetching '/var/www/index.html' + Fetching '/var/www/moros.css' + Fetching '/var/www/moros.png' + Creating '/var/pkg' Creating user... Username: vinc @@ -270,7 +265,7 @@ You can print the date with `date`: You can update the real time clock by writing the correct time to its device file: - > print "2023-03-21 10:00:00" => /dev/clk/rtc + > print 2023-03-21 10:00:00 => /dev/clk/rtc > date 2023-03-21 10:00:00 +0000 @@ -343,8 +338,8 @@ for example you can use `e` instead of `edit` to edit a file. ## Network -You can setup the [network](network.md) manually with `net` or automatically -with `dhcp`: +You can setup the [network](network.md) manually with some network +[device files](devices.md) or automatically with the `dhcp` command: > dhcp [8.801660] NET IP 10.0.2.15/24 diff --git a/doc/network.md b/doc/network.md index c4906ab00..29777028a 100644 --- a/doc/network.md +++ b/doc/network.md @@ -1,6 +1,9 @@ # MOROS Network -## NET +See the [devices](devices.md) documentation to manually setup the network using +device files. + +## NET (deprecated) Display the network configuration: diff --git a/doc/shell.md b/doc/shell.md index 19636e885..f73abb2f8 100644 --- a/doc/shell.md +++ b/doc/shell.md @@ -82,7 +82,7 @@ When executed without arguments, this command will print the current directory. > read foo.txt or read bar.txt -## Pipes and redirections (WIP) +## Pipes and Redirections (WIP) A thin arrow `->` can be used for piping the output from one command to the input of another command (TODO): diff --git a/doc/syscalls.md b/doc/syscalls.md index 2b641552b..d785ffd63 100644 --- a/doc/syscalls.md +++ b/doc/syscalls.md @@ -2,112 +2,287 @@ This list is unstable and subject to change between versions of MOROS. -## EXIT (0x1) +Each syscall is documented with its high-level Rust API wrapper and details +of the raw interface when needed. + +Any reference to a slice in the arguments (like `&str` or `&[u8]`) will need to +be converted into a pointer and a length for the raw syscall. + +Any negative number returned by a raw syscall indicates that an error has +occurred. In the high-level API, this will be typically converted to an +`Option` or a `Result` type. + +At the lowest level a syscall follows the System V ABI convention with its +number set in the `RAX` register, and its arguments in the `RDI`, `RSI`, `RDX`, +and `R8` registers. The `RAX` register is reused for the return value. + +Hello world example in assembly using the `WRITE` and `EXIT` syscalls: + +```nasm +[bits 64] + +section .data +msg: db "Hello, World!", 10 +len: equ $-msg + +global _start +section .text +_start: + mov rax, 4 ; syscall number for WRITE + mov rdi, 1 ; standard output + mov rsi, msg ; addr of string + mov rdx, len ; size of string + int 0x80 + + mov rax, 1 ; syscall number for EXIT + mov rdi, 0 ; no error + int 0x80 +``` + +## EXIT (0x01) + +```rust +fn exit(code: ExitCode) +``` + +Terminate the calling process. + +The code can be one of the following: ```rust -pub fn exit(code: usize) -> usize +pub enum ExitCode { + Success = 0, + Failure = 1, + UsageError = 64, + DataError = 65, + OpenError = 128, + ReadError = 129, + ExecError = 130, + PageFaultError = 200, + ShellExit = 255, +} ``` -## SPAWN (0x2) +The `ExitCode` is converted to a `usize` for the raw syscall. + +## SPAWN (0x02) ```rust -pub fn spawn(path: &str) -> isize +fn spawn(path: &str, args: &[&str]) -> ExitCode ``` -## READ (0x3) +Spawn a process with the given list of arguments. + +This syscall will block until the child process is terminated. It will return +the `ExitCode` passed by the child process to the `EXIT` syscall. + +## READ (0x03) ```rust -pub fn read(handle: usize, buf: &mut [u8]) -> isize +fn read(handle: usize, buf: &mut [u8]) -> Option ``` -## WRITE (0x4) +Read from a file handle to a buffer. + +Return the number of bytes read on success. + +## WRITE (0x04) ```rust -pub fn write(handle: usize, buf: &mut [u8]) -> isize +fn write(handle: usize, buf: &[u8]) -> Option ``` -## OPEN (0x5) +Write from a buffer to a file handle. + +Return the number of bytes written on success. + +## OPEN (0x05) ```rust -pub fn open(path: &str, flags: usize) -> isize +fn open(path: &str, flags: u8) -> Option ``` -## CLOSE (0x6) +Open a file and return a file handle. + +The flags can be one or more of the following: ```rust -pub fn close(handle: usize) +enum OpenFlag { + Read = 1, + Write = 2, + Append = 4, + Create = 8, + Truncate = 16, + Dir = 32, + Device = 64, +} ``` -## INFO (0x7) +The flags `OpenFlag::Create | OpenFlag::Dir` can be used to create a directory. + +Reading a directory opened with `OpenFlag::Read | OpenFlag::Dir` will return a +list of `FileInfo`, one for each file in the directory. + +## CLOSE (0x06) ```rust -pub fn info(path: &str, info: &mut FileInfo) -> isize +fn close(handle: usize) ``` -## DUP (0x8) +Close a file handle. + +## INFO (0x07) ```rust -pub fn dup(old_handle: usize, new_handle: usize) -> isize +fn info(path: &str) -> Option ``` -## DELETE (0x9) +Get information on a file. + +A `FileInfo` will be returned when successful: ```rust -pub fn delete(path: &str) -> isize +struct FileInfo { + kind: FileType, + size: u32, + time: u64, + name: String, +} ``` -## STOP (0xA) +The raw syscall takes the pointer and the length of a mutable reference to a +`FileInfo` that will be overwritten on success and returns a `isize` to +indicate the result of the operation. + +## DUP (0x08) ```rust -pub fn stop(code: usize) +fn dup(old_handle: usize, new_handle: usize) -> Result<(), ()> +``` + +Duplicate a file handle. + +## DELETE (0x09) + +```rust +fn delete(path: &str) -> Result<(), ()> +``` + +Delete a file. + +## STOP (0x0A) + +```rust +fn stop(code: usize) ``` The system will reboot with `0xCAFE` and halt with `0xDEAD`. -## SLEEP (0xB) +## SLEEP (0x0B) + +```rust +fn sleep(seconds: f64) +``` + +The system will sleep for the given amount of seconds. + +## POLL (0x0C) ```rust -pub fn sleep(seconds: f64) +fn poll(list: &[(usize, IO)]) -> Option<(usize, IO)> ``` -## POLL (0xC) +Given a list of file handles and `IO` operations: ```rust -pub fn poll(list: &[(usize, IO)]) -> isize +enum IO { + Read, + Write, +} ``` -## CONNECT (0xD) +The index of the first file handle in the list that is ready for the given `IO` +operation is returned by the raw syscall on success or a negative number if no +operations are available for any file handles. The syscall is not blocking and +will return immediately. + +For example polling the console will show when a line is ready to be read, +or polling a socket will show when it can receive or send data. + +## CONNECT (0x0D) ```rust -pub fn connect(handle, usize, addr: &str, port: u16) -> isize +fn connect(handle: usize, addr: IpAddress, port: u16) -> Result<(), ()> ``` -## LISTEN (0xE) +Connect a socket to an endpoint at the given `IpAddress` and port: ```rust -pub fn listen(handle, usize, port: u16) -> isize +struct Ipv4Address([u8; 4]); + +struct Ipv6Address([u8; 16]); + +enum IpAddress { + Ipv4(Ipv4Address), + Ipv6(Ipv6Address), +} ``` -## ACCEPT (0xF) +NOTE: Only IPv4 is currently supported. + +## LISTEN (0x0E) ```rust -pub fn accept(handle, usize, addr: &str) -> isize +fn listen(handle: usize, port: u16) -> Result<(), ()> ``` +Listen for incoming connections to a socket. + +## ACCEPT (0x0F) + +```rust +fn accept(handle: usize) -> Result +``` + +Accept an incoming connection to a socket. + +The raw syscall takes the pointer and the length of a mutable reference to an +`IpAddress` that will be overwritten on success and returns a `isize` +indicating the result of the operation. + ## ALLOC (0x10) ```rust -pub fn alloc(size: usize, align: usize) -> *mut u8 +fn alloc(size: usize, align: usize) -> *mut u8 ``` +Allocate memory. + ## FREE (0x11) ```rust -pub fn free(ptr: *mut u8, size: usize, align: usize) +fn free(ptr: *mut u8, size: usize, align: usize) ``` +Free memory. + ## KIND (0x12) ```rust -pub fn kind(handle: usize) -> isize +fn kind(handle: usize) -> Option ``` + +Return the file type of a file handle. + +A `FileType` will be returned when successful: + +```rust +enum FileType { + Dir = 0, + File = 1, + Device = 2, +} +``` + +The raw syscall returns a `isize` that will be converted a `FileType` if the +number is positive. diff --git a/dsk/bin/beep b/dsk/bin/beep index 1ef1561db..6ed3e33a5 100644 Binary files a/dsk/bin/beep and b/dsk/bin/beep differ diff --git a/dsk/bin/blank b/dsk/bin/blank index 1482b366e..f67191f97 100644 Binary files a/dsk/bin/blank and b/dsk/bin/blank differ diff --git a/dsk/bin/exec b/dsk/bin/exec index 32ac6247e..bda86dfe5 100644 Binary files a/dsk/bin/exec and b/dsk/bin/exec differ diff --git a/dsk/bin/geocal b/dsk/bin/geocal index 373e708d4..11d656ebc 100644 Binary files a/dsk/bin/geocal and b/dsk/bin/geocal differ diff --git a/dsk/bin/geodate b/dsk/bin/geodate index aa6866541..fabf53fa8 100644 Binary files a/dsk/bin/geodate and b/dsk/bin/geodate differ diff --git a/dsk/bin/mandelbrot b/dsk/bin/mandelbrot index d0069b78f..dc697c2f9 100644 Binary files a/dsk/bin/mandelbrot and b/dsk/bin/mandelbrot differ diff --git a/dsk/bin/print b/dsk/bin/print index 1c90b6f34..dc34c289b 100644 Binary files a/dsk/bin/print and b/dsk/bin/print differ diff --git a/dsk/ini/banner.txt b/dsk/ini/banner.txt index 6b2171f2f..b4425e0c4 100644 --- a/dsk/ini/banner.txt +++ b/dsk/ini/banner.txt @@ -1,5 +1,5 @@ - _M_ + (\_/) (o o) +-------------------ooO--(_)--Ooo------------------+ | | diff --git a/dsk/tmp/lisp/doc.lsp b/dsk/tmp/lisp/doc.lsp index cde52fdf5..74e4378bd 100644 --- a/dsk/tmp/lisp/doc.lsp +++ b/dsk/tmp/lisp/doc.lsp @@ -6,7 +6,8 @@ (print (str "(" (if (function? (eval f)) "\e[96m" "\e[92m") f "\e[0m" # name - (str " " (if (list? s) (str/join s " ") s)) # args + (if (nil? s) "" + (str " " (if (list? s) (str/join s " ") s))) # args ")" "\e[90m" (if (empty? d) "" " # ") d "\e[0m")))) # desc diff --git a/src/api/fs.rs b/src/api/fs.rs index b4ee6f78c..e5b16753c 100644 --- a/src/api/fs.rs +++ b/src/api/fs.rs @@ -103,33 +103,33 @@ pub fn open_file(path: &str) -> Option { } pub fn append_file(path: &str) -> Option { - let flags = OpenFlag::Append as usize; + let flags = OpenFlag::Append as u8; syscall::open(path, flags) } pub fn create_file(path: &str) -> Option { - let flags = OpenFlag::Create as usize; + let flags = OpenFlag::Create as u8; syscall::open(path, flags) } pub fn open_dir(path: &str) -> Option { - let flags = OpenFlag::Dir as usize; + let flags = OpenFlag::Dir as u8; syscall::open(path, flags) } pub fn create_dir(path: &str) -> Option { - let flags = OpenFlag::Create as usize | OpenFlag::Dir as usize; + let flags = OpenFlag::Create | OpenFlag::Dir; syscall::open(path, flags) } pub fn open_device(path: &str) -> Option { - let flags = OpenFlag::Device as usize; + let flags = OpenFlag::Device as u8; syscall::open(path, flags) } pub fn create_device(path: &str, name: &str) -> Option { if let Ok(buf) = device_buffer(name) { - let flags = OpenFlag::Create as usize | OpenFlag::Device as usize; + let flags = OpenFlag::Create | OpenFlag::Device; if let Some(handle) = syscall::open(path, flags) { syscall::write(handle, &buf); return Some(handle); @@ -252,9 +252,10 @@ pub fn reopen(path: &str, handle: usize, append: bool) -> Result { create_file(path) }; if let Some(old_handle) = res { - syscall::dup(old_handle, handle); - syscall::close(old_handle); - return Ok(handle); + if syscall::dup(old_handle, handle).is_ok() { + syscall::close(old_handle); + return Ok(handle); + } } Err(()) } diff --git a/src/api/mod.rs b/src/api/mod.rs index 7c778fb65..df2ecbb1a 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -89,6 +89,7 @@ pub mod console; pub mod font; pub mod fs; pub mod io; +pub mod power; pub mod process; pub mod prompt; pub mod rng; diff --git a/src/api/power.rs b/src/api/power.rs new file mode 100644 index 000000000..1dc6612a6 --- /dev/null +++ b/src/api/power.rs @@ -0,0 +1,9 @@ +use crate::api::syscall; + +pub fn reboot() { + syscall::stop(0xCAFE); +} + +pub fn halt() { + syscall::stop(0xDEAD); +} diff --git a/src/api/process.rs b/src/api/process.rs index baeadd82e..60c631a5f 100644 --- a/src/api/process.rs +++ b/src/api/process.rs @@ -1,6 +1,6 @@ use crate::api::syscall; -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(u8)] pub enum ExitCode { Success = 0, @@ -32,7 +32,10 @@ impl From for ExitCode { pub fn spawn(path: &str, args: &[&str]) -> Result<(), ExitCode> { if syscall::info(path).is_some() { - syscall::spawn(path, args) + match syscall::spawn(path, args) { + ExitCode::Success => Ok(()), + code => Err(code), + } } else { Err(ExitCode::OpenError) } diff --git a/src/api/syscall.rs b/src/api/syscall.rs index 667837117..9241a569d 100644 --- a/src/api/syscall.rs +++ b/src/api/syscall.rs @@ -5,8 +5,8 @@ use crate::sys::syscall::number::*; use crate::syscall; use core::convert::TryFrom; -use smoltcp::wire::IpAddress; -use smoltcp::wire::Ipv4Address; +use core::sync::atomic::{fence, Ordering}; +use smoltcp::wire::{IpAddress, Ipv4Address}; pub fn exit(code: ExitCode) { unsafe { syscall!(EXIT, code as usize) }; @@ -49,7 +49,7 @@ pub fn kind(handle: usize) -> Option { } } -pub fn open(path: &str, flags: usize) -> Option { +pub fn open(path: &str, flags: u8) -> Option { let ptr = path.as_ptr() as usize; let len = path.len(); let res = unsafe { syscall!(OPEN, ptr, len, flags) } as isize; @@ -60,12 +60,12 @@ pub fn open(path: &str, flags: usize) -> Option { } } -pub fn dup(old_handle: usize, new_handle: usize) -> Option { +pub fn dup(old_handle: usize, new_handle: usize) -> Result<(), ()> { let res = unsafe { syscall!(DUP, old_handle, new_handle) } as isize; if res >= 0 { - Some(res as usize) + Ok(()) } else { - None + Err(()) } } @@ -95,7 +95,7 @@ pub fn close(handle: usize) { unsafe { syscall!(CLOSE, handle) }; } -pub fn spawn(path: &str, args: &[&str]) -> Result<(), ExitCode> { +pub fn spawn(path: &str, args: &[&str]) -> ExitCode { let path_ptr = path.as_ptr() as usize; let args_ptr = args.as_ptr() as usize; let path_len = path.len(); @@ -103,23 +103,16 @@ pub fn spawn(path: &str, args: &[&str]) -> Result<(), ExitCode> { let res = unsafe { syscall!(SPAWN, path_ptr, path_len, args_ptr, args_len) }; - if res == 0 { - Ok(()) - } else { - Err(ExitCode::from(res)) - } -} -pub fn stop(code: usize) { - unsafe { syscall!(STOP, code) }; -} + // Without the fence `res` would always be `0` instead of the code passed + // to the `exit` syscall by the child process. + fence(Ordering::SeqCst); -pub fn reboot() { - stop(0xCAFE); + ExitCode::from(res) } -pub fn halt() { - stop(0xDEAD); +pub fn stop(code: usize) { + unsafe { syscall!(STOP, code) }; } pub fn poll(list: &[(usize, IO)]) -> Option<(usize, IO)> { @@ -180,7 +173,9 @@ pub fn free(ptr: *mut u8, size: usize, align: usize) { #[test_case] fn test_file() { use crate::sys::fs::{dismount, format_mem, mount_mem, OpenFlag}; + use alloc::string::ToString; use alloc::vec; + mount_mem(); format_mem(); @@ -188,7 +183,7 @@ fn test_file() { assert_eq!(open("/test", flags), None); // Write file - let flags = OpenFlag::Create as usize; + let flags = OpenFlag::Create as u8; assert_eq!(open("/test", flags), Some(4)); let input = "Hello, world!".as_bytes(); assert_eq!(write(4, &input), Some(input.len())); @@ -204,6 +199,9 @@ fn test_file() { close(5); assert_eq!(open("/test", flags), Some(4)); + assert_eq!(info("/test").map(|info| info.kind()), kind(4)); + assert_eq!(info("/test").map(|info| info.name()), Some("test".to_string())); + assert_eq!(info("/test").map(|info| info.size()), Some(input.len() as u32)); close(4); diff --git a/src/bin/halt.rs b/src/bin/halt.rs index 3ad32267b..0b4b7f5f9 100644 --- a/src/bin/halt.rs +++ b/src/bin/halt.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] +use moros::api::power; use moros::api::syscall; use moros::entry_point; @@ -13,7 +14,7 @@ fn main(_args: &[&str]) { syscall::write(1, b"\x1b[0m"); // Reset syscall::write(1, b"\n"); syscall::sleep(0.5); - syscall::halt(); + power::halt(); loop { syscall::sleep(1.0) } diff --git a/src/bin/print.rs b/src/bin/print.rs index 8d190d94d..2296d6fbb 100644 --- a/src/bin/print.rs +++ b/src/bin/print.rs @@ -7,12 +7,6 @@ use moros::entry_point; entry_point!(main); fn main(args: &[&str]) { - let n = args.len(); - for i in 1..n { - syscall::write(1, args[i].as_bytes()); - if i < n - 1 { - syscall::write(1, b" "); - } - } + syscall::write(1, args[1..].join(" ").as_bytes()); syscall::write(1, b"\n"); } diff --git a/src/bin/reboot.rs b/src/bin/reboot.rs index bbb0b4eb7..6906026a7 100644 --- a/src/bin/reboot.rs +++ b/src/bin/reboot.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] +use moros::api::power; use moros::api::syscall; use moros::entry_point; @@ -14,7 +15,7 @@ fn main(_args: &[&str]) { ); syscall::write(1, b"\x1b[0m"); // Reset syscall::sleep(0.5); - syscall::reboot(); + power::reboot(); loop { syscall::sleep(1.0) } diff --git a/src/sys/fs/mod.rs b/src/sys/fs/mod.rs index 53350f696..a3382fece 100644 --- a/src/sys/fs/mod.rs +++ b/src/sys/fs/mod.rs @@ -26,6 +26,7 @@ use super_block::SuperBlock; use alloc::string::{String, ToString}; use core::convert::TryFrom; +use core::ops::BitOr; pub const VERSION: u8 = 2; @@ -43,12 +44,20 @@ pub enum OpenFlag { } impl OpenFlag { - fn is_set(&self, flags: usize) -> bool { - flags & (*self as usize) != 0 + fn is_set(&self, flags: u8) -> bool { + flags & (*self as u8) != 0 } } -pub fn open(path: &str, flags: usize) -> Option { +impl BitOr for OpenFlag { + type Output = u8; + + fn bitor(self, rhs: Self) -> Self::Output { + (self as u8) | (rhs as u8) + } +} + +pub fn open(path: &str, flags: u8) -> Option { if OpenFlag::Dir.is_set(flags) { let res = Dir::open(path); if res.is_none() && OpenFlag::Create.is_set(flags) { diff --git a/src/sys/keyboard.rs b/src/sys/keyboard.rs index b7358493e..29f3b1839 100644 --- a/src/sys/keyboard.rs +++ b/src/sys/keyboard.rs @@ -1,4 +1,4 @@ -use crate::api::syscall; +use crate::api; use crate::sys; use core::sync::atomic::{AtomicBool, Ordering}; @@ -109,7 +109,7 @@ fn interrupt_handler() { match key { // Ctrl-Alt-Del DecodedKey::Unicode('\u{7f}') if is_alt && is_ctrl => { - syscall::reboot() + api::power::reboot() } DecodedKey::RawKey(KeyCode::PageUp) => send_csi("5~"), diff --git a/src/sys/mem/mod.rs b/src/sys/mem/mod.rs index 45c16e7bf..df753c5ef 100644 --- a/src/sys/mem/mod.rs +++ b/src/sys/mem/mod.rs @@ -37,6 +37,9 @@ pub fn init(boot_info: &'static BootInfo) { "MEM [{:#016X}-{:#016X}] {}", // "({} KB)" last_end_addr, start_addr - 1, "Unmapped" //, hole >> 10 ); + if start_addr < (1 << 20) { + memory_size += hole; // BIOS memory + } } log!( "MEM [{:#016X}-{:#016X}] {:?}", // "({} KB)" @@ -46,11 +49,10 @@ pub fn init(boot_info: &'static BootInfo) { last_end_addr = end_addr; } - // 0x000000000A0000-0x000000000EFFFF: + 320 KB of BIOS memory - // 0x000000FEFFC000-0x000000FEFFFFFF: - 256 KB of virtual memory - // 0x000000FFFC0000-0x000000FFFFFFFF: - 16 KB of virtual memory - memory_size += (320 - 256 - 16) << 10; - + // FIXME: There are two small reserved areas at the end of the physical + // memory that should be removed from the count to be fully accurate but + // their sizes and location vary depending on the amount of RAM on the + // system. It doesn't affect the count in megabytes. log!("RAM {} MB", memory_size >> 20); MEMORY_SIZE.store(memory_size as usize, Ordering::Relaxed); diff --git a/src/sys/process.rs b/src/sys/process.rs index a55468781..8fba4be10 100644 --- a/src/sys/process.rs +++ b/src/sys/process.rs @@ -320,7 +320,7 @@ impl Process { table[id].clone() }; proc.exec(args_ptr, args_len); - Ok(()) + unreachable!(); // The kernel switched to the child process } else { Err(ExitCode::ExecError) } diff --git a/src/sys/syscall/mod.rs b/src/sys/syscall/mod.rs index a7b7a2446..8d0286ef9 100644 --- a/src/sys/syscall/mod.rs +++ b/src/sys/syscall/mod.rs @@ -50,7 +50,7 @@ pub fn dispatcher( let ptr = sys::process::ptr_from_addr(arg1 as u64); let len = arg2; let path = utf8_from_raw_parts(ptr, len); - let flags = arg3; + let flags = arg3 as u8; service::open(path, flags) as usize } number::READ => { diff --git a/src/sys/syscall/service.rs b/src/sys/syscall/service.rs index 1bc6dd7a3..4c179a02a 100644 --- a/src/sys/syscall/service.rs +++ b/src/sys/syscall/service.rs @@ -49,7 +49,7 @@ pub fn kind(handle: usize) -> isize { } } -pub fn open(path: &str, flags: usize) -> isize { +pub fn open(path: &str, flags: u8) -> isize { let path = match sys::fs::canonicalize(path) { Ok(path) => path, Err(_) => return -1, @@ -65,7 +65,7 @@ pub fn open(path: &str, flags: usize) -> isize { pub fn dup(old_handle: usize, new_handle: usize) -> isize { if let Some(file) = sys::process::handle(old_handle) { sys::process::update_handle(new_handle, *file); - return new_handle as isize; + return 0; } -1 } @@ -109,7 +109,7 @@ pub fn spawn(path: &str, args_ptr: usize, args_len: usize) -> ExitCode { if let Err(code) = Process::spawn(&buf, args_ptr, args_len) { code } else { - ExitCode::Success + unreachable!(); // The kernel switched to the child process } } else { ExitCode::ReadError diff --git a/src/usr/dhcp.rs b/src/usr/dhcp.rs index 28e79be1b..c5488034d 100644 --- a/src/usr/dhcp.rs +++ b/src/usr/dhcp.rs @@ -90,6 +90,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> { log!("NET DNS {}", servers); } } + return Ok(()); } Err(ExitCode::Failure) diff --git a/src/usr/host.rs b/src/usr/host.rs index 0588213e3..75d82ade2 100644 --- a/src/usr/host.rs +++ b/src/usr/host.rs @@ -140,7 +140,7 @@ pub fn resolve(name: &str) -> Result { return Err(ResponseCode::NetworkError); }; - let flags = OpenFlag::Device as usize; + let flags = OpenFlag::Device as u8; if let Some(handle) = syscall::open(socket_path, flags) { if syscall::connect(handle, addr, port).is_err() { syscall::close(handle); diff --git a/src/usr/http.rs b/src/usr/http.rs index 67c97ded0..5e3037bd9 100644 --- a/src/usr/http.rs +++ b/src/usr/http.rs @@ -134,7 +134,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> { }; let mut code = None; - let flags = OpenFlag::Device as usize; + let flags = OpenFlag::Device as u8; if let Some(handle) = syscall::open(socket_path, flags) { if syscall::connect(handle, addr, port).is_err() { error!("Could not connect to {}:{}", addr, port); diff --git a/src/usr/lisp/primitive.rs b/src/usr/lisp/primitive.rs index b40a6800f..7a364f148 100644 --- a/src/usr/lisp/primitive.rs +++ b/src/usr/lisp/primitive.rs @@ -482,16 +482,16 @@ pub fn lisp_file_open(args: &[Exp]) -> Result { let mode = string(&args[1])?; let mut flags = match mode.as_ref() { - "a" => OpenFlag::Append as usize, - "r" => OpenFlag::Read as usize, - "w" => OpenFlag::Write as usize, + "a" => OpenFlag::Append as u8, + "r" => OpenFlag::Read as u8, + "w" => OpenFlag::Write as u8, _ => return expected!("valid mode"), }; flags |= match syscall::info(&path) { - Some(info) if info.is_device() => OpenFlag::Device as usize, - Some(info) if info.is_dir() => OpenFlag::Dir as usize, + Some(info) if info.is_device() => OpenFlag::Device as u8, + Some(info) if info.is_dir() => OpenFlag::Dir as u8, None if &mode == "r" => return could_not!("open file"), - None => OpenFlag::Create as usize, + None => OpenFlag::Create as u8, _ => 0, }; @@ -550,7 +550,7 @@ pub fn lisp_socket_connect(args: &[Exp]) -> Result { Err(()) => return expected!("valid IP address"), }; let port: usize = number(&args[2])?.try_into()?; - let flags = OpenFlag::Device as usize; + let flags = OpenFlag::Device as u8; if let Some(handle) = syscall::open(&format!("/dev/net/{}", kind), flags) { if syscall::connect(handle, addr, port as u16).is_ok() { return Ok(Exp::Num(Number::from(handle))); @@ -563,7 +563,7 @@ pub fn lisp_socket_listen(args: &[Exp]) -> Result { ensure_length_eq!(args, 2); let kind = string(&args[0])?; let port: usize = number(&args[1])?.try_into()?; - let flags = OpenFlag::Device as usize; + let flags = OpenFlag::Device as u8; if let Some(handle) = syscall::open(&format!("/dev/net/{}", kind), flags) { if syscall::listen(handle, port as u16).is_ok() { return Ok(Exp::Num(Number::from(handle))); diff --git a/src/usr/render.rs b/src/usr/render.rs index 6f8472398..7a6802af7 100644 --- a/src/usr/render.rs +++ b/src/usr/render.rs @@ -51,18 +51,18 @@ fn parse_bmp(data: &[u8]) -> Result { let bmp_header: &BmpHeader = unsafe { &*(data.as_ptr() as *const BmpHeader) }; if &bmp_header.signature != b"BM" { - return Err("Invalid BMP signature".to_string()); + return Err("Invalid signature".to_string()); } let dib_header: &DibHeader = unsafe { &*(data[size_of::()..].as_ptr() as *const DibHeader) }; if dib_header.bits_per_pixel != 8 { - return Err("Only 8-bit (256 color) BMPs are supported".to_string()); + return Err("Invalid bits per pixel".to_string()); } let pixels_offset = bmp_header.data_offset as usize; let palette_size = 256 * 4; // 256 colors, 4 bytes per color (BGRA) if pixels_offset < palette_size || data.len() <= pixels_offset { - return Err("Invalid BMP palette".to_string()); + return Err("Invalid palette".to_string()); } let palette_offset = pixels_offset - palette_size; @@ -76,7 +76,7 @@ fn parse_bmp(data: &[u8]) -> Result { let width = dib_header.width as u32; let height = dib_header.height.unsigned_abs(); if pixels.len() != (width * height) as usize { - return Err("Invalid BMP file: wrong pixels count".to_string()); + return Err("Invalid pixels count".to_string()); } Ok(BmpInfo { width, height, palette, pixels }) @@ -131,59 +131,62 @@ impl Config { fn render_bmp(path: &str, config: &mut Config) -> Result { if let Ok(buf) = fs::read_to_bytes(path) { - if let Ok(bmp) = parse_bmp(&buf) { - let width = bmp.width as usize; - let height = bmp.height as usize; - if width != WIDTH || height != HEIGHT { - config.text_mode(); - error!("Unsupported BMP size"); - return Err(ExitCode::Failure); - } - let size = width * height; - let mut img = Vec::with_capacity(size); + match parse_bmp(&buf) { + Ok(bmp) => { + let width = bmp.width as usize; + let height = bmp.height as usize; + if width != WIDTH || height != HEIGHT { + config.text_mode(); + error!("Unsupported BMP size"); + return Err(ExitCode::Failure); + } + let size = width * height; + let mut img = Vec::with_capacity(size); - // BMP rows are padded to multiples of 4 bytes - let row_padding = (4 - (width % 4)) % 4; + // BMP rows are padded to multiples of 4 bytes + let row_padding = (4 - (width % 4)) % 4; - for y in 0..height { - for x in 0..width { - // BMP stores images bottom-up - let bmp_y = height - 1 - y; + for y in 0..height { + for x in 0..width { + // BMP stores images bottom-up + let bmp_y = height - 1 - y; - let i = bmp_y * (width + row_padding) + x; - img.push(bmp.pixels[i]); + let i = bmp_y * (width + row_padding) + x; + img.push(bmp.pixels[i]); + } } - } - config.graphic_mode(); + config.graphic_mode(); - // Load palette - let mut palette = [0; 256 * 3]; - for (i, (r, g, b)) in bmp.palette.iter().enumerate() { - palette[i * 3 + 0] = *r; - palette[i * 3 + 1] = *g; - palette[i * 3 + 2] = *b; - } - let dev = "/dev/vga/palette"; - if !fs::is_device(dev) || fs::write(dev, &palette).is_err() { - config.text_mode(); - error!("Could not write to '{}'", dev); - return Err(ExitCode::Failure); - } + // Load palette + let mut palette = [0; 256 * 3]; + for (i, (r, g, b)) in bmp.palette.iter().enumerate() { + palette[i * 3 + 0] = *r; + palette[i * 3 + 1] = *g; + palette[i * 3 + 2] = *b; + } + let dev = "/dev/vga/palette"; + if !fs::is_device(dev) || fs::write(dev, &palette).is_err() { + config.text_mode(); + error!("Could not write to '{}'", dev); + return Err(ExitCode::Failure); + } + + // Display image + let dev = "/dev/vga/buffer"; + if !fs::is_device(dev) || fs::write(dev, &img).is_err() { + config.text_mode(); + error!("Could not write to '{}'", dev); + return Err(ExitCode::Failure); + } - // Display image - let dev = "/dev/vga/buffer"; - if !fs::is_device(dev) || fs::write(dev, &img).is_err() { + Ok(read_command()) + } + Err(msg) => { config.text_mode(); - error!("Could not write to '{}'", dev); - return Err(ExitCode::Failure); + error!("{}", msg); + Err(ExitCode::Failure) } - - Ok(read_command()) - } else { - config.text_mode(); - error!("Could not parse BMP"); - Err(ExitCode::Failure) } } else { config.text_mode(); diff --git a/src/usr/socket.rs b/src/usr/socket.rs index 25022c58a..cd3fd3e6e 100644 --- a/src/usr/socket.rs +++ b/src/usr/socket.rs @@ -77,7 +77,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> { let mut connected = false; let stdin = 0; let stdout = 1; - let flags = OpenFlag::Device as usize; + let flags = OpenFlag::Device as u8; if let Some(handle) = syscall::open(socket_path, flags) { if listen { if syscall::listen(handle, port).is_err() { diff --git a/src/usr/tcp.rs b/src/usr/tcp.rs index 3cd8659d8..04d920d4d 100644 --- a/src/usr/tcp.rs +++ b/src/usr/tcp.rs @@ -61,7 +61,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> { return Err(ExitCode::Failure); }; - let flags = OpenFlag::Device as usize; + let flags = OpenFlag::Device as u8; if let Some(handle) = syscall::open(socket_path, flags) { if syscall::connect(handle, addr, port).is_err() { error!("Could not connect to {}:{}", addr, port); diff --git a/www/devices.html b/www/devices.html new file mode 100644 index 000000000..e71c18c35 --- /dev/null +++ b/www/devices.html @@ -0,0 +1,238 @@ + + + + + MOROS Devices + + + +

MOROS Devices

+ +

Creating the devices in the file system:

+ +
write /dev/
+write /dev/ata/
+write /dev/ata/0/
+write /dev/ata/0/0 -d ata-0-0
+write /dev/ata/0/1 -d ata-0-1
+write /dev/ata/1/
+write /dev/ata/1/0 -d ata-1-0
+write /dev/ata/1/1 -d ata-1-1
+write /dev/clk/
+write /dev/clk/boot -d clk-boot
+write /dev/clk/epoch -d clk-epoch
+write /dev/clk/rtc -d clk-rtc
+write /dev/console -d console
+write /dev/net/
+write /dev/net/tcp -d net-tcp
+write /dev/net/udp -d net-udp
+write /dev/net/gw -d net-gw
+write /dev/net/ip -d net-ip
+write /dev/net/mac -d net-mac
+write /dev/net/usage -d net-usage
+write /dev/null -d null
+write /dev/random -d random
+write /dev/speaker -d speaker
+write /dev/vga/
+write /dev/vga/buffer -d vga-buffer
+write /dev/vga/font -d vga-font
+write /dev/vga/mode -d vga-mode
+write /dev/vga/palette -d vga-palette
+
+ +

Clock Devices

+ +

Reading the number of seconds since boot:

+ +
> read /dev/clk/boot
+89.570360
+
+ +

Reading the number of seconds since Unix Epoch:

+ +
> read /dev/clk/epoch
+1730398385.175973
+
+ +

Reading the real time clock (RTC):

+ +
> read /dev/clk/rtc
+2024-10-31 18:20:02
+
+ +

Changing the system time:

+ +
> print 2025-01-01 00:00:00 => /dev/clk/rtc
+[580.327629] RTC 2025-01-01 00:00:00 +0000
+
+ +

Console Device

+ +

Reading /dev/console with a 4 bytes buffer will return a character from the +keyboard or the serial interface. Reading with a larger buffer will return a +complete line.

+ +

Network Devices

+ +

Network Config Devices

+ +

The prefered way to setup the network is to use the dhcp command:

+ +
> dhcp
+[958.810995] NET IP 10.0.2.15/24
+[958.812995] NET GW 10.0.2.2
+[958.818994] NET DNS 10.0.2.3
+
+ +

But it is possible to do it manually with the /dev/net/ip and /dev/net/gw +device files, and the /ini/dns configuration file:

+ +
> print 10.0.2.15/24 => /dev/net/ip
+[975.123511] NET IP 10.0.2.15/24
+
+> print 10.0.2.2 => /dev/net/gw
+[985.646908] NET GW 10.0.2.2
+
+> print 10.0.2.3 => /ini/dns
+
+ +

Reading /dev/net/mac will return the MAC address:

+ +
> read /dev/net/mac
+52-54-00-12-34-56
+
+ +

Network Usage Device

+ +

Reading /dev/net/usage will return the network usage:

+ +
> read /dev/net/usage
+0 0 0 0
+
+> dhcp
+[7.910795] NET IP 10.0.2.15/24
+[7.911795] NET GW 10.0.2.2
+[7.915795] NET DNS 10.0.2.3
+
+> read /dev/net/usage
+2 1180 2 620
+
+> http example.com => /dev/null
+
+> read /dev/net/usage
+10 3306 10 1151
+
+ +

The output format is:

+ +
<recv packets> <recv bytes> <sent packets> <sent bytes>
+
+ +

Network Socket Devices

+ +

Opening /dev/net/tcp or /dev/net/udp with the OPEN syscall and the device +flag will return a file handle for a TCP or UDP socket supporting the standard + READ and WRITE syscalls after establishing a connection using the + CONNECT, or LISTEN and ACCEPT syscalls.

+ +

The size of those files give the maximum size of the buffer that can be used +when reading or writing to a socket:

+ +
> list /dev/net
+1446 2024-09-28 09:57:55 tcp
+1458 2024-09-28 09:57:55 udp
+
+ +

Reading a socket with a 1 byte buffer will return the status of the socket:

+ +
+-----+--------------+
+| Bit | Status       |
++-----+--------------+
+|  0  | Is Listening |
+|  1  | Is Active    |
+|  2  | Is Open      |
+|  3  | Can Send     |
+|  4  | May Send     |
+|  5  | Can Recv     |
+|  6  | May Recv     |
+|  7  | Reserved     |
++-----+--------------+
+
+ +

Speaker Device

+ +

Playing a 440 Hz sound on the PC speaker:

+ +
> print 440 => /dev/speaker
+
+ +

Stopping the sound:

+ +
> print 0 => /dev/speaker
+
+ +

Null Device

+ +

Writing to /dev/null will discard any data sent to it:

+ +
> print hello
+hello
+
+> print hello => /dev/null
+
+ +

It can be used to suppress errors:

+ +
> copy none.txt some.txt
+Error: Could not read file 'none.txt'
+
+> copy none.txt some.txt [2]=> /dev/null
+
+ +

Random Device

+ +

Reading from /dev/random will return bytes from a cryptographically secure +random number generator that uses the HC-128 algorithm seeded from the + RDRAND instruction when available.

+ +

VGA Devices

+ +

VGA Font Device

+ +

Changing the VGA font:

+ +
> copy /ini/fonts/zap-light-8x16.psf /dev/vga/font
+
+ +

VGA Mode Device

+ +

Changing the VGA mode:

+ +
> print 320x200 => /dev/vga/mode
+
+ +

The accepted modes are:

+ +
    +
  • 80x25 for the primary text mode with 16 colors
  • +
  • 320x200 for the primary graphics mode with 256 colors
  • +
  • 640x480 for the secondary graphics mode with 16 colors
  • +
+ +

It is possible to read the current mode from this device file.

+ +

VGA Palette Device

+ +

Changing the VGA palette is done by writting a 768 bytes buffer to + /dev/vga/palette containing the RGB values of 256 colors.

+ +

It is possible to read the current palette from this device file.

+ +

VGA Buffer Device

+ +

Changing the VGA framebuffer is done by writting a 64 KB buffer to + /dev/vga/buffer containing the color index of each pixel on the screen while +in 320x200 mode.

+ + + diff --git a/www/filesystem.html b/www/filesystem.html index c5b9dcd0f..ec074e6b3 100644 --- a/www/filesystem.html +++ b/www/filesystem.html @@ -68,6 +68,9 @@

Setup in diskless console

> write /var/ # Variable files +

See the devices documentation to create the device files in the + /dev directory.

+

Then the following should be added to the boot script with the command edit /ini/boot.sh to allow MOROS to finish booting:

@@ -167,7 +170,7 @@

DirEntry

A directory entry represents a file or a directory contained inside a directory. Each entry use a variable number of bytes that must fit inside the data of one block. Those bytes represent the kind of entry (file or dir), the -address of the first block, the filesize (max 4GB), the last modified time in +address of the first block, the filesize (max 4 GB), the last modified time in seconds since Unix Epoch, the length of the filename, and the filename (max 255 chars) of the entry.

@@ -186,8 +189,9 @@

DirEntry

FileInfo

-

The info syscall on a file or directory and the read syscall on a directory -return a subset of a directory entry for userspace programs.

+

The INFO syscall on a file or directory and the READ syscall on a directory +return a subset of a directory entry for userspace programs. See the + syscalls documentation for more information.

Structure:

diff --git a/www/manual.html b/www/manual.html index babe01f24..32399e632 100644 --- a/www/manual.html +++ b/www/manual.html @@ -13,37 +13,40 @@

Boot

During boot MOROS will display its version followed by the memory layout, memory size, processor, devices, network cards, disks, and the real time clock.

-
[0.250962] MOROS v0.10.4
-[0.256961] MEM [0x00000000000000-0x00000000000FFF] FrameZero
-[0.256961] MEM [0x00000000001000-0x00000000004FFF] PageTable
-[0.256961] MEM [0x00000000005000-0x00000000016FFF] Bootloader
-[0.256961] MEM [0x00000000017000-0x00000000017FFF] BootInfo
-[0.256961] MEM [0x00000000018000-0x0000000009EFFF] Kernel
-[0.256961] MEM [0x0000000009F000-0x0000000009FFFF] Reserved
-[0.256961] MEM [0x000000000F0000-0x000000000FFFFF] Reserved
-[0.256961] MEM [0x00000000100000-0x00000000104FFF] Kernel
-[0.256961] MEM [0x00000000105000-0x00000000304FFF] KernelStack
-[0.256961] MEM [0x00000000305000-0x000000003FFFFF] Usable
-[0.256961] MEM [0x00000000400000-0x000000005EEFFF] Kernel
-[0.256961] MEM [0x000000005EF000-0x000000005FFFFF] PageTable
-[0.256961] MEM [0x00000000600000-0x00000001FDFFFF] Usable
-[0.256961] MEM [0x00000001FE0000-0x00000001FFFFFF] Reserved
-[0.256961] MEM [0x000000FFFC0000-0x000000FFFFFFFF] Reserved
-[0.256961] MEM 32704 KB
-[0.282957] CPU GenuineIntel
-[0.283957] CPU Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz
-[0.284957] RNG RDRAND unavailable
-[0.291956] PCI 0000:00:00 [8086:1237]
-[0.292955] PCI 0000:01:00 [8086:7000]
-[0.292955] PCI 0000:01:01 [8086:7010]
-[0.292955] PCI 0000:01:03 [8086:7113]
-[0.293955] PCI 0000:02:00 [1234:1111]
-[0.293955] PCI 0000:03:00 [8086:100E]
-[0.301954] NET DRV E1000
-[0.303954] NET MAC 52-54-00-12-34-56
-[0.308953] ATA 0:0 QEMU HARDDISK QM00001 (32 MB)
-[0.310953] MFS Superblock found in ATA 0:0
-[0.315952] RTC 2024-06-19 11:54:13 +0000
+    
[0.250962] SYS MOROS v0.10.4-89-g6b3f825
+[0.254961] MEM [0x00000000000000-0x00000000000FFF] FrameZero
+[0.255961] MEM [0x00000000001000-0x00000000004FFF] PageTable
+[0.256961] MEM [0x00000000005000-0x00000000014FFF] Bootloader
+[0.256961] MEM [0x00000000015000-0x00000000015FFF] BootInfo
+[0.257961] MEM [0x00000000016000-0x0000000009EFFF] Kernel
+[0.258961] MEM [0x0000000009F000-0x0000000009FFFF] Reserved
+[0.259960] MEM [0x000000000A0000-0x000000000EFFFF] Unmapped
+[0.260960] MEM [0x000000000F0000-0x000000000FFFFF] Reserved
+[0.261960] MEM [0x00000000100000-0x0000000010BFFF] Kernel
+[0.261960] MEM [0x0000000010C000-0x0000000030BFFF] KernelStack
+[0.262960] MEM [0x0000000030C000-0x000000003FFFFF] Usable
+[0.262960] MEM [0x00000000400000-0x000000005FDFFF] Kernel
+[0.263960] MEM [0x000000005FE000-0x0000000060EFFF] PageTable
+[0.264960] MEM [0x0000000060F000-0x00000001FDFFFF] Usable
+[0.264960] MEM [0x00000001FE0000-0x00000001FFFFFF] Reserved
+[0.265960] MEM [0x00000002000000-0x000000FFFBFFFF] Unmapped
+[0.265960] MEM [0x000000FFFC0000-0x000000FFFFFFFF] Reserved
+[0.266959] RAM 32 MB
+[0.363945] CPU GenuineIntel
+[0.363945] CPU Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz
+[0.368944] CPU BP:0 running
+[0.369944] CPU AP:1 waiting
+[0.398939] RNG RDRAND unavailable
+[0.413937] PCI 0000:00:00 [8086:1237]
+[0.414937] PCI 0000:01:00 [8086:7000]
+[0.414937] PCI 0000:01:01 [8086:7010]
+[0.414937] PCI 0000:01:03 [8086:7113]
+[0.415937] PCI 0000:02:00 [1234:1111]
+[0.415937] PCI 0000:03:00 [8086:100E]
+[0.422936] NET DRV E1000
+[0.424935] NET MAC 52-54-00-12-34-56
+[0.431934] ATA 0:0 QEMU HARDDISK QM00001 (32 MB)
+[0.436933] RTC 2024-11-02 18:18:38 +0000
 

Installation

@@ -71,82 +74,74 @@

Installation

MFS is now mounted to '/' Populating filesystem... -Created '/bin' -Created '/dev' -Created '/ini' -Created '/lib' -Created '/net' -Created '/src' -Created '/tmp' -Created '/usr' -Created '/var' -Fetched '/bin/clear' -Fetched '/bin/halt' -Fetched '/bin/ntp' -Fetched '/bin/print' -Fetched '/bin/reboot' -Fetched '/bin/sleep' -Created '/dev/ata' -Created '/dev/ata/0' -Created '/dev/ata/0/0' -Created '/dev/ata/0/1' -Created '/dev/ata/1' -Created '/dev/ata/1/0' -Created '/dev/ata/1/1' -Created '/dev/clk' -Created '/dev/clk/boot' -Created '/dev/clk/epoch' -Created '/dev/clk/rtc' -Created '/dev/null' -Created '/dev/random' -Created '/dev/console' -Created '/dev/net' -Created '/dev/net/tcp' -Created '/dev/net/udp' -Fetched '/ini/banner.txt' -Fetched '/ini/boot.sh' -Fetched '/ini/lisp.lsp' -Fetched '/ini/shell.sh' -Fetched '/ini/version.txt' -Created '/ini/palettes' -Fetched '/ini/palettes/gruvbox-dark.sh' -Fetched '/ini/palettes/gruvbox-light.sh' -Created '/ini/fonts' -Fetched '/ini/fonts/zap-light-8x16.psf' -Fetched '/ini/fonts/zap-vga-8x16.psf' -Created '/lib/lisp' -Fetched '/lib/lisp/alias.lsp' -Fetched '/lib/lisp/core.lsp' -Fetched '/lib/lisp/file.lsp' -Fetched '/tmp/alice.txt' -Fetched '/tmp/machines.txt' -Created '/tmp/lisp' -Fetched '/tmp/lisp/colors.lsp' -Fetched '/tmp/lisp/doc.lsp' -Fetched '/tmp/lisp/factorial.lsp' -Fetched '/tmp/lisp/fibonacci.lsp' -Fetched '/tmp/lisp/geotime.lsp' -Fetched '/tmp/lisp/pi.lsp' -Fetched '/tmp/lisp/sum.lsp' -Created '/tmp/life' -Fetched '/tmp/life/centinal.cells' -Fetched '/tmp/life/flower-of-eden.cells' -Fetched '/tmp/life/garden-of-eden.cells' -Fetched '/tmp/life/glider-gun.cells' -Fetched '/tmp/life/pentadecathlon.cells' -Fetched '/tmp/life/queen-bee-shuttle.cells' -Fetched '/tmp/life/ship-in-a-bottle.cells' -Fetched '/tmp/life/thunderbird.cells' -Fetched '/tmp/life/wing.cells' -Created '/tmp/beep' -Fetched '/tmp/beep/tetris.sh' -Fetched '/tmp/beep/starwars.sh' -Fetched '/tmp/beep/mario.sh' -Created '/var/log' -Created '/var/www' -Fetched '/var/www/index.html' -Fetched '/var/www/moros.css' -Fetched '/var/www/moros.png' +Creating '/bin' +Creating '/dev' +Creating '/ini' +Creating '/lib' +Creating '/net' +Creating '/src' +Creating '/tmp' +Creating '/usr' +Creating '/var' +Fetching '/bin/clear' +Fetching '/bin/get' +Fetching '/bin/halt' +Fetching '/bin/ntp' +Fetching '/bin/pkg' +Fetching '/bin/print' +Fetching '/bin/reboot' +Fetching '/bin/sleep' +Creating '/dev/ata' +Creating '/dev/ata/0' +Creating '/dev/ata/1' +Creating '/dev/clk' +Creating '/dev/net' +Creating '/dev/vga' +Creating '/dev/ata/0/0' +Creating '/dev/ata/0/1' +Creating '/dev/ata/1/0' +Creating '/dev/ata/1/1' +Creating '/dev/clk/boot' +Creating '/dev/clk/epoch' +Creating '/dev/clk/rtc' +Creating '/dev/console' +Creating '/dev/net/tcp' +Creating '/dev/net/udp' +Creating '/dev/net/gw' +Creating '/dev/net/ip' +Creating '/dev/net/mac' +Creating '/dev/net/usage' +Creating '/dev/null' +Creating '/dev/random' +Creating '/dev/speaker' +Creating '/dev/vga/buffer' +Creating '/dev/vga/font' +Creating '/dev/vga/mode' +Creating '/dev/vga/palette' +Fetching '/ini/banner.txt' +Fetching '/ini/boot.sh' +Fetching '/ini/lisp.lsp' +Fetching '/ini/shell.sh' +Fetching '/ini/version.txt' +Creating '/ini/palettes' +Fetching '/ini/palettes/default.sh' +Fetching '/ini/palettes/gruvbox-dark.sh' +Fetching '/ini/palettes/gruvbox-light.sh' +Creating '/ini/fonts' +Fetching '/ini/fonts/zap-light-8x16.psf' +Creating '/lib/lisp' +Fetching '/lib/lisp/alias.lsp' +Fetching '/lib/lisp/core.lsp' +Fetching '/lib/lisp/file.lsp' +Fetching '/lib/lisp/math.lsp' +Fetching '/tmp/alice.txt' +Fetching '/tmp/machines.txt' +Creating '/var/log' +Creating '/var/www' +Fetching '/var/www/index.html' +Fetching '/var/www/moros.css' +Fetching '/var/www/moros.png' +Creating '/var/pkg' Creating user... Username: vinc @@ -290,7 +285,7 @@

Time

You can update the real time clock by writing the correct time to its device file:

-
> print "2023-03-21 10:00:00" => /dev/clk/rtc
+    
> print 2023-03-21 10:00:00 => /dev/clk/rtc
 
 > date
 2023-03-21 10:00:00 +0000
@@ -370,8 +365,8 @@ 

Aliases

Network

-

You can setup the network manually with net or automatically -with dhcp:

+

You can setup the network manually with some network + device files or automatically with the dhcp command:

> dhcp
 [8.801660] NET IP 10.0.2.15/24
diff --git a/www/moros.css b/www/moros.css
index 0447ff423..b8cc0958e 100644
--- a/www/moros.css
+++ b/www/moros.css
@@ -56,8 +56,15 @@ img {
   background: #28282878;
   padding: 2px;
 }
-
 footer {
   margin-top: 2em;
   text-align: center;
 }
+h2::before {
+  color: #B17286;
+  content: "# ";
+}
+h3::before {
+  color: #B17286;
+  content: "## ";
+}
diff --git a/www/network.html b/www/network.html
index 3cd7a81e2..6e8814a1c 100644
--- a/www/network.html
+++ b/www/network.html
@@ -8,7 +8,10 @@
   
     

MOROS Network

-

NET

+

See the devices documentation to manually setup the network using +device files.

+ +

NET (deprecated)

Display the network configuration:

diff --git a/www/shell.html b/www/shell.html index 31e27656c..6d59433ad 100644 --- a/www/shell.html +++ b/www/shell.html @@ -98,7 +98,7 @@

Combiners (TODO)

> read foo.txt or read bar.txt
 
-

Pipes and redirections (WIP)

+

Pipes and Redirections (WIP)

A thin arrow -> can be used for piping the output from one command to the input of another command (TODO):

diff --git a/www/syscalls.html b/www/syscalls.html index 9397fdcf6..5d70e471f 100644 --- a/www/syscalls.html +++ b/www/syscalls.html @@ -10,97 +10,265 @@

MOROS Syscalls

This list is unstable and subject to change between versions of MOROS.

-

EXIT (0x1)

+

Each syscall is documented with its high-level Rust API wrapper and details +of the raw interface when needed.

-
pub fn exit(code: usize) -> usize
+    

Any reference to a slice in the arguments (like &str or &[u8]) will need to +be converted into a pointer and a length for the raw syscall.

+ +

Any negative number returned by a raw syscall indicates that an error has +occurred. In the high-level API, this will be typically converted to an + Option or a Result type.

+ +

At the lowest level a syscall follows the System V ABI convention with its +number set in the RAX register, and its arguments in the RDI, RSI, RDX, +and R8 registers. The RAX register is reused for the return value.

+ +

Hello world example in assembly using the WRITE and EXIT syscalls:

+ +
[bits 64]
+
+section .data
+msg: db "Hello, World!", 10
+len: equ $-msg
+
+global _start
+section .text
+_start:
+  mov rax, 4                ; syscall number for WRITE
+  mov rdi, 1                ; standard output
+  mov rsi, msg              ; addr of string
+  mov rdx, len              ; size of string
+  int 0x80
+
+  mov rax, 1                ; syscall number for EXIT
+  mov rdi, 0                ; no error
+  int 0x80
 
-

SPAWN (0x2)

+

EXIT (0x01)

-
pub fn spawn(path: &str) -> isize
+    
fn exit(code: ExitCode)
 
-

READ (0x3)

+

Terminate the calling process.

+ +

The code can be one of the following:

+ +
pub enum ExitCode {
+    Success        =   0,
+    Failure        =   1,
+    UsageError     =  64,
+    DataError      =  65,
+    OpenError      = 128,
+    ReadError      = 129,
+    ExecError      = 130,
+    PageFaultError = 200,
+    ShellExit      = 255,
+}
+
+ +

The ExitCode is converted to a usize for the raw syscall.

+ +

SPAWN (0x02)

+ +
fn spawn(path: &str, args: &[&str]) -> ExitCode
+
+ +

Spawn a process with the given list of arguments.

+ +

This syscall will block until the child process is terminated. It will return +the ExitCode passed by the child process to the EXIT syscall.

-
pub fn read(handle: usize, buf: &mut [u8]) -> isize
+    

READ (0x03)

+ +
fn read(handle: usize, buf: &mut [u8]) -> Option<usize>
 
-

WRITE (0x4)

+

Read from a file handle to a buffer.

+ +

Return the number of bytes read on success.

-
pub fn write(handle: usize, buf: &mut [u8]) -> isize
+    

WRITE (0x04)

+ +
fn write(handle: usize, buf: &[u8]) -> Option<usize>
 
-

OPEN (0x5)

+

Write from a buffer to a file handle.

+ +

Return the number of bytes written on success.

-
pub fn open(path: &str, flags: usize) -> isize
+    

OPEN (0x05)

+ +
fn open(path: &str, flags: u8) -> Option<usize>
 
-

CLOSE (0x6)

+

Open a file and return a file handle.

+ +

The flags can be one or more of the following:

-
pub fn close(handle: usize)
+    
enum OpenFlag {
+    Read     = 1,
+    Write    = 2,
+    Append   = 4,
+    Create   = 8,
+    Truncate = 16,
+    Dir      = 32,
+    Device   = 64,
+}
 
-

INFO (0x7)

+

The flags OpenFlag::Create | OpenFlag::Dir can be used to create a directory.

+ +

Reading a directory opened with OpenFlag::Read | OpenFlag::Dir will return a +list of FileInfo, one for each file in the directory.

-
pub fn info(path: &str, info: &mut FileInfo) -> isize
+    

CLOSE (0x06)

+ +
fn close(handle: usize)
 
-

DUP (0x8)

+

Close a file handle.

+ +

INFO (0x07)

-
pub fn dup(old_handle: usize, new_handle: usize) -> isize
+    
fn info(path: &str) -> Option<FileInfo>
 
-

DELETE (0x9)

+

Get information on a file.

-
pub fn delete(path: &str) -> isize
+    

A FileInfo will be returned when successful:

+ +
struct FileInfo {
+    kind: FileType,
+    size: u32,
+    time: u64,
+    name: String,
+}
 
-

STOP (0xA)

+

The raw syscall takes the pointer and the length of a mutable reference to a + FileInfo that will be overwritten on success and returns a isize to +indicate the result of the operation.

+ +

DUP (0x08)

-
pub fn stop(code: usize)
+    
fn dup(old_handle: usize, new_handle: usize) -> Result<(), ()>
+
+ +

Duplicate a file handle.

+ +

DELETE (0x09)

+ +
fn delete(path: &str) -> Result<(), ()>
+
+ +

Delete a file.

+ +

STOP (0x0A)

+ +
fn stop(code: usize)
 

The system will reboot with 0xCAFE and halt with 0xDEAD.

-

SLEEP (0xB)

+

SLEEP (0x0B)

+ +
fn sleep(seconds: f64)
+
+ +

The system will sleep for the given amount of seconds.

+ +

POLL (0x0C)

-
pub fn sleep(seconds: f64)
+    
fn poll(list: &[(usize, IO)]) -> Option<(usize, IO)>
 
-

POLL (0xC)

+

Given a list of file handles and IO operations:

-
pub fn poll(list: &[(usize, IO)]) -> isize
+    
enum IO {
+    Read,
+    Write,
+}
 
-

CONNECT (0xD)

+

The index of the first file handle in the list that is ready for the given IO +operation is returned by the raw syscall on success or a negative number if no +operations are available for any file handles. The syscall is not blocking and +will return immediately.

+ +

For example polling the console will show when a line is ready to be read, +or polling a socket will show when it can receive or send data.

+ +

CONNECT (0x0D)

-
pub fn connect(handle, usize, addr: &str, port: u16) -> isize
+    
fn connect(handle: usize, addr: IpAddress, port: u16) -> Result<(), ()>
 
-

LISTEN (0xE)

+

Connect a socket to an endpoint at the given IpAddress and port:

-
pub fn listen(handle, usize, port: u16) -> isize
+    
struct Ipv4Address([u8; 4]);
+
+struct Ipv6Address([u8; 16]);
+
+enum IpAddress {
+    Ipv4(Ipv4Address),
+    Ipv6(Ipv6Address),
+}
 
-

ACCEPT (0xF)

+

NOTE: Only IPv4 is currently supported.

+ +

LISTEN (0x0E)

-
pub fn accept(handle, usize, addr: &str) -> isize
+    
fn listen(handle: usize, port: u16) -> Result<(), ()>
 
+

Listen for incoming connections to a socket.

+ +

ACCEPT (0x0F)

+ +
fn accept(handle: usize) -> Result<IpAddress, ()>
+
+ +

Accept an incoming connection to a socket.

+ +

The raw syscall takes the pointer and the length of a mutable reference to an + IpAddress that will be overwritten on success and returns a isize +indicating the result of the operation.

+

ALLOC (0x10)

-
pub fn alloc(size: usize, align: usize) -> *mut u8
+    
fn alloc(size: usize, align: usize) -> *mut u8
 
+

Allocate memory.

+

FREE (0x11)

-
pub fn free(ptr: *mut u8, size: usize, align: usize)
+    
fn free(ptr: *mut u8, size: usize, align: usize)
 
+

Free memory.

+

KIND (0x12)

-
pub fn kind(handle: usize) -> isize
+    
fn kind(handle: usize) -> Option<FileType>
+
+ +

Return the file type of a file handle.

+ +

A FileType will be returned when successful:

+ +
enum FileType {
+    Dir = 0,
+    File = 1,
+    Device = 2,
+}
 
+ +

The raw syscall returns a isize that will be converted a FileType if the +number is positive.