diff --git a/Makefile b/Makefile index a9a7c722..e3bb4402 100644 --- a/Makefile +++ b/Makefile @@ -157,7 +157,7 @@ sysroot-system: sysroot-fsh sysroot-base-headers echo 'ID=sortix' && \ echo 'VERSION_ID="$(VERSION)"' && \ echo 'PRETTY_NAME="Sortix $(VERSION)"' && \ - echo 'SORTIX_ABI=0.0' && \ + echo 'SORTIX_ABI=0.1' && \ true) > "$(SYSROOT)/etc/sortix-release" echo /etc/sortix-release >> "$(SYSROOT)/tix/manifest/system" ln -sf sortix-release "$(SYSROOT)/etc/os-release" diff --git a/kernel/include/sortix/kernel/log.h b/kernel/include/sortix/kernel/log.h index 06e3cba5..27f1c6c7 100644 --- a/kernel/include/sortix/kernel/log.h +++ b/kernel/include/sortix/kernel/log.h @@ -45,6 +45,7 @@ extern size_t fallback_framebuffer_height; extern TextBufferHandle* device_textbufhandle; extern size_t (*device_callback)(void*, const char*, size_t); +extern size_t (*device_writeraw)(void*, const char*, size_t); extern size_t (*device_width)(void*); extern size_t (*device_height)(void*); extern void (*device_get_cursor)(void*, size_t*, size_t*); @@ -55,6 +56,7 @@ extern bool (*emergency_device_is_impaired)(void*); extern bool (*emergency_device_recoup)(void*); extern void (*emergency_device_reset)(void*); extern size_t (*emergency_device_callback)(void*, const char*, size_t); +extern size_t (*emergency_device_writeraw)(void*, const char*, size_t); extern size_t (*emergency_device_width)(void*); extern size_t (*emergency_device_height)(void*); extern void (*emergency_device_get_cursor)(void*, size_t*, size_t*); @@ -98,7 +100,7 @@ inline size_t Print(const char* str) inline size_t PrintData(const void* ptr, size_t size) { - return device_callback(device_pointer, (const char*) ptr, size); + return device_writeraw(device_pointer, (const char*) ptr, size); } __attribute__((format(printf, 1, 2))) diff --git a/kernel/include/sortix/termios.h b/kernel/include/sortix/termios.h index aa48d4dc..3feca977 100644 --- a/kernel/include/sortix/termios.h +++ b/kernel/include/sortix/termios.h @@ -50,6 +50,10 @@ #define PARMRK (1U << 11) #define OPOST (1U << 0) +#if __USE_SORTIX || __USE_XOPEN +#define ONLCR (1U << 1) +#define OCRNL (1U << 2) +#endif #define B0 0 #define B50 50 diff --git a/kernel/include/sortix/termmode.h b/kernel/include/sortix/termmode.h index 10c7722a..2e84aedc 100644 --- a/kernel/include/sortix/termmode.h +++ b/kernel/include/sortix/termmode.h @@ -29,6 +29,9 @@ #define TERMMODE_NONBLOCK (1U << 6) /* ISORTIX_NONBLOCK */ #define TERMMODE_TERMIOS (1U << 7) /* !ISORTIX_TERMMODE */ #define TERMMODE_DISABLE (1U << 8) /* !CREAD */ +#define TERMMODE_NOOPOST (1U << 9) /* !OPOST */ +#define TERMMODE_NOONLCR (1U << 10) /* !ONLCR */ +#define TERMMODE_OCRNL (1U << 11) /* OCRNL */ #define TERMMODE_NORMAL (TERMMODE_UNICODE | TERMMODE_SIGNAL | TERMMODE_UTF8 | \ TERMMODE_LINEBUFFER | TERMMODE_ECHO | TERMMODE_TERMIOS) diff --git a/kernel/log.cpp b/kernel/log.cpp index d6c63f0a..483c0a76 100644 --- a/kernel/log.cpp +++ b/kernel/log.cpp @@ -44,6 +44,7 @@ size_t fallback_framebuffer_height = 0; TextBufferHandle* device_textbufhandle = NULL; size_t (*device_callback)(void*, const char*, size_t) = NULL; +size_t (*device_writeraw)(void*, const char*, size_t) = NULL; size_t (*device_width)(void*) = NULL; size_t (*device_height)(void*) = NULL; void (*device_get_cursor)(void*, size_t*, size_t*) = NULL; @@ -54,6 +55,7 @@ bool (*emergency_device_is_impaired)(void*) = NULL; bool (*emergency_device_recoup)(void*) = NULL; void (*emergency_device_reset)(void*) = NULL; size_t (*emergency_device_callback)(void*, const char*, size_t) = NULL; +size_t (*emergency_device_writeraw)(void*, const char*, size_t) = NULL; size_t (*emergency_device_width)(void*) = NULL; void (*emergency_device_get_cursor)(void*, size_t*, size_t*) = NULL; size_t (*emergency_device_height)(void*) = NULL; @@ -65,6 +67,11 @@ static size_t PrintToTextTerminal(void* user, const char* str, size_t len) return ((TextTerminal*) user)->Print(str, len); } +static size_t PrintRawToTextTerminal(void* user, const char* str, size_t len) +{ + return ((TextTerminal*) user)->PrintRaw(str, len); +} + static size_t TextTermWidth(void* user) { return ((TextTerminal*) user)->Width(); @@ -111,6 +118,12 @@ size_t EmergencyPrintToTextTerminal(void* user, const char* str, size_t len) return ((TextTerminal*) user)->EmergencyPrint(str, len); } +static +size_t EmergencyPrintRawToTextTerminal(void* user, const char* str, size_t len) +{ + return ((TextTerminal*) user)->EmergencyPrintRaw(str, len); +} + static size_t EmergencyTextTermWidth(void* user) { return ((TextTerminal*) user)->EmergencyWidth(); @@ -192,6 +205,7 @@ void Init(multiboot_info_t* bootinfo) // Register the text terminal as the kernel log. Log::device_callback = PrintToTextTerminal; + Log::device_writeraw = PrintRawToTextTerminal; Log::device_width = TextTermWidth; Log::device_height = TextTermHeight; Log::device_get_cursor = TextTermGetCursor; @@ -204,6 +218,7 @@ void Init(multiboot_info_t* bootinfo) Log::emergency_device_recoup = EmergencyTextTermRecoup; Log::emergency_device_reset = EmergencyTextTermReset; Log::emergency_device_callback = EmergencyPrintToTextTerminal; + Log::emergency_device_writeraw = EmergencyPrintRawToTextTerminal; Log::emergency_device_width = EmergencyTextTermWidth; Log::emergency_device_height = EmergencyTextTermHeight; Log::emergency_device_get_cursor = EmergencyTextTermGetCursor; diff --git a/kernel/textterminal.cpp b/kernel/textterminal.cpp index cba5a624..9477f073 100644 --- a/kernel/textterminal.cpp +++ b/kernel/textterminal.cpp @@ -63,6 +63,21 @@ void TextTerminal::Reset() } size_t TextTerminal::Print(const char* string, size_t stringlen) +{ + ScopedLock lock(&termlock); + TextBuffer* textbuf = textbufhandle->Acquire(); + for ( size_t i = 0; i < stringlen; i++ ) + { + if ( string[i] == '\n' ) + PutChar(textbuf, '\r'); + PutChar(textbuf, string[i]); + } + UpdateCursor(textbuf); + textbufhandle->Release(textbuf); + return stringlen; +} + +size_t TextTerminal::PrintRaw(const char* string, size_t stringlen) { ScopedLock lock(&termlock); TextBuffer* textbuf = textbufhandle->Acquire(); @@ -185,6 +200,25 @@ size_t TextTerminal::EmergencyPrint(const char* string, size_t stringlen) // while it held the terminal lock. The best case is if the terminal lock is // currently unused, which would mean everything is safe. + TextBuffer* textbuf = textbufhandle->EmergencyAcquire(); + for ( size_t i = 0; i < stringlen; i++ ) + { + if ( string[i] == '\n' ) + PutChar(textbuf, '\r'); + PutChar(textbuf, string[i]); + } + UpdateCursor(textbuf); + textbufhandle->EmergencyRelease(textbuf); + return stringlen; +} + +size_t TextTerminal::EmergencyPrintRaw(const char* string, size_t stringlen) +{ + // This is during a kernel emergency where preemption has been disabled and + // this is the only thread running. Another thread may have been interrupted + // while it held the terminal lock. The best case is if the terminal lock is + // currently unused, which would mean everything is safe. + TextBuffer* textbuf = textbufhandle->EmergencyAcquire(); for ( size_t i = 0; i < stringlen; i++ ) PutChar(textbuf, string[i]); @@ -271,7 +305,10 @@ void TextTerminal::PutChar(TextBuffer* textbuf, char c) wc = L' '; if ( textbuf->Width() <= column ) + { + column = 0; Newline(textbuf); + } TextPos pos(column++, line); TextChar tc(wc, vgacolor, ATTR_CHAR | next_attr); textbuf->SetChar(pos, tc); @@ -286,7 +323,6 @@ void TextTerminal::UpdateCursor(TextBuffer* textbuf) void TextTerminal::Newline(TextBuffer* textbuf) { TextPos pos(column, line); - column = 0; if ( line < textbuf->Height()-1 ) line++; else @@ -330,7 +366,10 @@ void TextTerminal::Backspace(TextBuffer* textbuf) void TextTerminal::Tab(TextBuffer* textbuf) { if ( column == textbuf->Width() ) + { + column = 0; Newline(textbuf); + } unsigned int count = 8 - (column % 8); for ( unsigned int i = 0; i < count; i++ ) { diff --git a/kernel/textterminal.h b/kernel/textterminal.h index 198c4361..4e3ca014 100644 --- a/kernel/textterminal.h +++ b/kernel/textterminal.h @@ -35,6 +35,7 @@ public: TextTerminal(TextBufferHandle* textbufhandle); ~TextTerminal(); size_t Print(const char* string, size_t stringlen); + size_t PrintRaw(const char* string, size_t stringlen); size_t Width() const; size_t Height() const; void GetCursor(size_t* column, size_t* row) const; @@ -44,6 +45,7 @@ public: bool EmergencyRecoup(); void EmergencyReset(); size_t EmergencyPrint(const char* string, size_t stringlen); + size_t EmergencyPrintRaw(const char* string, size_t stringlen); size_t EmergencyWidth() const; size_t EmergencyHeight() const; void EmergencyGetCursor(size_t* column, size_t* row) const; diff --git a/kernel/tty.cpp b/kernel/tty.cpp index 6f6defd8..acb78cdc 100644 --- a/kernel/tty.cpp +++ b/kernel/tty.cpp @@ -63,7 +63,10 @@ static const unsigned int SUPPORTED_TERMMODES = TERMMODE_KBKEY | TERMMODE_ECHO | TERMMODE_NONBLOCK | TERMMODE_TERMIOS - | TERMMODE_DISABLE; + | TERMMODE_DISABLE + | TERMMODE_NOOPOST + | TERMMODE_NOONLCR + | TERMMODE_OCRNL; static inline bool IsByteUnescaped(unsigned char byte) { @@ -89,7 +92,7 @@ TTY::TTY(dev_t dev, mode_t mode, uid_t owner, gid_t group) this->stat_gid = group; memset(&tio, 0, sizeof(tio)); tio.c_iflag = BRKINT | ICRNL | IXANY | IXON; - tio.c_oflag = OPOST; + tio.c_oflag = OPOST | ONLCR; tio.c_cflag = CS8 /*| CREAD*/ | HUPCL; // CREAD unset for boot security. tio.c_lflag = ECHO | ECHOE | ECHOK | ICANON | IEXTEN | ISIG; tio.c_cc[VEOF] = CONTROL('D'); @@ -127,6 +130,8 @@ int TTY::settermmode(ioctx_t* /*ctx*/, unsigned int termmode) tcflag_t new_cflag = old_cflag; tcflag_t old_lflag = tio.c_lflag; tcflag_t new_lflag = old_lflag; + tcflag_t old_oflag = tio.c_oflag; + tcflag_t new_oflag = old_oflag; if ( termmode & TERMMODE_KBKEY ) new_lflag |= ISORTIX_KBKEY; else @@ -163,12 +168,25 @@ int TTY::settermmode(ioctx_t* /*ctx*/, unsigned int termmode) new_cflag |= CREAD; else new_cflag &= ~CREAD; + if ( !(termmode & TERMMODE_NOOPOST) ) + new_oflag |= OPOST; + else + new_oflag &= ~OPOST; + if ( !(termmode & TERMMODE_NOONLCR) ) + new_oflag |= ONLCR; + else + new_oflag &= ~ONLCR; + if ( termmode & TERMMODE_OCRNL ) + new_oflag |= OCRNL; + else + new_oflag &= ~OCRNL; bool oldnoutf8 = old_lflag & ISORTIX_32BIT; bool newnoutf8 = new_lflag & ISORTIX_32BIT; if ( oldnoutf8 != newnoutf8 ) memset(&read_ps, 0, sizeof(read_ps)); tio.c_cflag = new_cflag; tio.c_lflag = new_lflag; + tio.c_oflag = new_oflag; if ( !(tio.c_lflag & ICANON) ) CommitLineBuffer(); return 0; @@ -196,6 +214,12 @@ int TTY::gettermmode(ioctx_t* ctx, unsigned int* mode) termmode |= TERMMODE_TERMIOS; if ( !(tio.c_cflag & CREAD) ) termmode |= TERMMODE_DISABLE; + if ( !(tio.c_oflag & OPOST) ) + termmode |= TERMMODE_NOOPOST; + if ( !(tio.c_oflag & ONLCR) ) + termmode |= TERMMODE_NOONLCR; + if ( tio.c_oflag & OCRNL ) + termmode |= TERMMODE_OCRNL; if ( !ctx->copy_to_dest(mode, &termmode, sizeof(termmode)) ) return -1; return 0; @@ -418,7 +442,16 @@ void TTY::ProcessByte(unsigned char byte, uint32_t control_unicode) if ( tio.c_lflag & ECHO ) { - if ( IsByteUnescaped(byte) ) + if ( byte == '\n' ) + { + if ( tio.c_oflag & OPOST && tio.c_oflag & ONLCR ) + Log::PrintData("\r\n", 2); + else if ( tio.c_oflag & OPOST && tio.c_oflag & OCRNL ) + Log::PrintData("\r", 1); + else + Log::PrintData("\n", 1); + } + else if ( IsByteUnescaped(byte) ) Log::PrintData(&byte, 1); else Log::PrintF("^%c", CONTROL(byte)); @@ -556,26 +589,59 @@ ssize_t TTY::write(ioctx_t* ctx, const uint8_t* io_buffer, size_t count) if ( tio.c_lflag & TOSTOP && !RequireForeground(SIGTTOU) ) return errno = EINTR, -1; // TODO: Add support for ioctx to the kernel log. - const size_t BUFFER_SIZE = 64UL; - char buffer[BUFFER_SIZE]; + unsigned char buffer[256]; + size_t max_incoming = sizeof(buffer); + if ( tio.c_oflag & OPOST && tio.c_oflag & ONLCR ) + max_incoming = sizeof(buffer) / 2; size_t sofar = 0; + size_t rounds = 0; while ( sofar < count ) { - size_t amount = count - sofar; - if ( BUFFER_SIZE < amount ) - amount = BUFFER_SIZE; - if ( !ctx->copy_from_src(buffer, io_buffer + sofar, amount) ) + size_t incoming = count - sofar; + if ( max_incoming < incoming ) + incoming = max_incoming; + if ( !ctx->copy_from_src(buffer, io_buffer + sofar, incoming) ) return -1; - Log::PrintData(buffer, amount); - sofar += amount; - if ( sofar < count ) + size_t offset; + size_t outgoing; + if ( tio.c_oflag & OPOST && tio.c_oflag & ONLCR ) + { + offset = sizeof(buffer); + outgoing = incoming; + for ( size_t ii = incoming; ii; ii-- ) + { + size_t i = ii - 1; + if ( buffer[i] == '\n' ) + { + buffer[--offset] = '\n'; + buffer[--offset] = '\r'; + outgoing++; + } + else + buffer[--offset] = buffer[i]; + } + } + else + { + offset = 0; + outgoing = incoming; + if ( tio.c_oflag & OPOST && tio.c_oflag & OCRNL ) + { + for ( size_t i = 0; i < incoming; i++ ) + if ( buffer[i] == '\r' ) + buffer[i] = '\n'; + } + } + Log::PrintData(buffer + offset, outgoing); + sofar += incoming; + if ( ++rounds % 16 == 0 ) { kthread_mutex_unlock(&termlock); kthread_yield(); kthread_mutex_lock(&termlock); - if ( Signal::IsPending() ) - return sofar; } + if ( Signal::IsPending() ) + return sofar; } return (ssize_t) sofar; }