2021-01-02 14:56:36 -03:00
/*
2021-04-15 06:39:48 -03:00
* Copyright ( c ) 2021 , the SerenityOS developers .
2021-06-23 08:19:24 -03:00
* Copyright ( c ) 2021 , Kyle Pereira < hey @ xylepereira . me >
2021-01-02 14:56:36 -03:00
*
2021-04-22 05:24:48 -03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2021-01-02 14:56:36 -03:00
*/
2021-08-27 06:24:50 -03:00
# include <Kernel/Bus/VirtIO/Console.h>
2021-11-29 12:03:38 -03:00
# include <Kernel/Devices/DeviceManagement.h>
2021-06-22 12:40:16 -03:00
# include <Kernel/Sections.h>
2021-08-14 04:13:27 -03:00
# include <Kernel/WorkQueue.h>
2021-01-02 14:56:36 -03:00
2021-08-27 06:18:13 -03:00
namespace Kernel : : VirtIO {
2021-01-02 14:56:36 -03:00
2021-08-27 06:18:13 -03:00
unsigned Console : : next_device_id = 0 ;
2021-04-15 06:39:48 -03:00
2022-08-19 15:53:40 -03:00
UNMAP_AFTER_INIT NonnullLockRefPtr < Console > Console : : must_create ( PCI : : DeviceIdentifier const & pci_device_identifier )
2021-08-27 06:32:05 -03:00
{
2022-08-19 15:53:40 -03:00
return adopt_lock_ref_if_nonnull ( new Console ( pci_device_identifier ) ) . release_nonnull ( ) ;
2021-08-27 06:32:05 -03:00
}
2021-09-04 02:42:31 -03:00
UNMAP_AFTER_INIT void Console : : initialize ( )
2021-01-02 14:56:36 -03:00
{
2021-09-04 02:42:31 -03:00
Device : : initialize ( ) ;
2021-12-08 10:00:18 -03:00
if ( auto const * cfg = get_config ( ConfigurationType : : Device ) ) {
2021-01-02 14:56:36 -03:00
bool success = negotiate_features ( [ & ] ( u64 supported_features ) {
u64 negotiated = 0 ;
if ( is_feature_set ( supported_features , VIRTIO_CONSOLE_F_SIZE ) )
2021-08-27 06:18:13 -03:00
dbgln ( " VirtIO::Console: Console size is not yet supported! " ) ;
2021-01-02 14:56:36 -03:00
if ( is_feature_set ( supported_features , VIRTIO_CONSOLE_F_MULTIPORT ) )
2021-06-23 08:19:24 -03:00
negotiated | = VIRTIO_CONSOLE_F_MULTIPORT ;
2021-01-02 14:56:36 -03:00
return negotiated ;
} ) ;
if ( success ) {
u32 max_nr_ports = 0 ;
u16 cols = 0 , rows = 0 ;
read_config_atomic ( [ & ] ( ) {
if ( is_feature_accepted ( VIRTIO_CONSOLE_F_SIZE ) ) {
2021-04-15 06:12:06 -03:00
cols = config_read16 ( * cfg , 0x0 ) ;
rows = config_read16 ( * cfg , 0x2 ) ;
2021-01-02 14:56:36 -03:00
}
if ( is_feature_accepted ( VIRTIO_CONSOLE_F_MULTIPORT ) ) {
2021-04-15 06:12:06 -03:00
max_nr_ports = config_read32 ( * cfg , 0x4 ) ;
2021-06-23 08:19:24 -03:00
m_ports . resize ( max_nr_ports ) ;
2021-01-02 14:56:36 -03:00
}
} ) ;
2021-08-27 06:18:13 -03:00
dbgln ( " VirtIO::Console: cols: {}, rows: {}, max nr ports {} " , cols , rows , max_nr_ports ) ;
2021-06-23 08:19:24 -03:00
// Base receiveq/transmitq for port0 + optional control queues and 2 per every additional port
success = setup_queues ( 2 + max_nr_ports > 0 ? 2 + 2 * max_nr_ports : 0 ) ;
2021-01-02 14:56:36 -03:00
}
if ( success ) {
2021-04-15 06:39:48 -03:00
finish_init ( ) ;
2021-06-20 10:46:04 -03:00
2021-11-29 12:03:38 -03:00
if ( is_feature_accepted ( VIRTIO_CONSOLE_F_MULTIPORT ) ) {
2021-06-23 08:19:24 -03:00
setup_multiport ( ) ;
2021-11-29 12:03:38 -03:00
} else {
auto port = MUST ( DeviceManagement : : the ( ) . try_create_device < VirtIO : : ConsolePort > ( 0u , * this ) ) ;
2021-08-14 04:13:27 -03:00
port - > init_receive_buffer ( { } ) ;
m_ports . append ( port ) ;
}
2021-01-02 14:56:36 -03:00
}
}
}
2021-09-23 04:20:54 -03:00
UNMAP_AFTER_INIT Console : : Console ( PCI : : DeviceIdentifier const & pci_device_identifier )
: VirtIO : : Device ( pci_device_identifier )
2021-09-04 02:42:31 -03:00
, m_device_id ( next_device_id + + )
{
}
2021-08-27 06:18:13 -03:00
bool Console : : handle_device_config_change ( )
2021-01-02 14:56:36 -03:00
{
2021-08-27 06:18:13 -03:00
dbgln ( " VirtIO::Console: Handle device config change " ) ;
2021-04-15 06:12:06 -03:00
return true ;
2021-01-02 14:56:36 -03:00
}
2021-08-27 06:18:13 -03:00
void Console : : handle_queue_update ( u16 queue_index )
2021-04-15 06:39:48 -03:00
{
2021-08-27 06:18:13 -03:00
dbgln_if ( VIRTIO_DEBUG , " VirtIO::Console: Handle queue update {} " , queue_index ) ;
2021-06-23 08:19:24 -03:00
if ( queue_index = = CONTROL_RECEIVEQ ) {
2021-08-21 20:49:22 -03:00
SpinlockLocker ringbuffer_lock ( m_control_receive_buffer - > lock ( ) ) ;
2021-06-23 08:19:24 -03:00
auto & queue = get_queue ( CONTROL_RECEIVEQ ) ;
2021-08-21 20:49:22 -03:00
SpinlockLocker queue_lock ( queue . lock ( ) ) ;
2021-06-20 10:46:04 -03:00
size_t used ;
2021-08-27 06:18:13 -03:00
QueueChain popped_chain = queue . pop_used_buffer_chain ( used ) ;
2021-06-20 10:46:04 -03:00
2021-06-23 08:19:24 -03:00
while ( ! popped_chain . is_empty ( ) ) {
popped_chain . for_each ( [ & ] ( auto addr , auto ) {
auto offset = addr . as_ptr ( ) - m_control_receive_buffer - > start_of_region ( ) . as_ptr ( ) ;
auto * message = reinterpret_cast < ControlMessage * > ( m_control_receive_buffer - > vaddr ( ) . offset ( offset ) . as_ptr ( ) ) ;
process_control_message ( * message ) ;
} ) ;
2021-06-20 10:46:04 -03:00
2021-06-23 08:19:24 -03:00
supply_chain_and_notify ( CONTROL_RECEIVEQ , popped_chain ) ;
popped_chain = queue . pop_used_buffer_chain ( used ) ;
2021-06-20 10:46:04 -03:00
}
2021-06-23 08:19:24 -03:00
} else if ( queue_index = = CONTROL_TRANSMITQ ) {
2021-08-21 20:49:22 -03:00
SpinlockLocker ringbuffer_lock ( m_control_transmit_buffer - > lock ( ) ) ;
2021-06-23 08:19:24 -03:00
auto & queue = get_queue ( CONTROL_TRANSMITQ ) ;
2021-08-21 20:49:22 -03:00
SpinlockLocker queue_lock ( queue . lock ( ) ) ;
2021-04-23 22:05:51 -03:00
size_t used ;
2021-08-27 06:18:13 -03:00
QueueChain popped_chain = queue . pop_used_buffer_chain ( used ) ;
2021-06-23 08:19:24 -03:00
auto number_of_messages = 0 ;
2021-04-23 22:05:51 -03:00
do {
popped_chain . for_each ( [ this ] ( PhysicalAddress address , size_t length ) {
2021-06-23 08:19:24 -03:00
m_control_transmit_buffer - > reclaim_space ( address , length ) ;
2021-04-23 22:05:51 -03:00
} ) ;
popped_chain . release_buffer_slots_to_queue ( ) ;
popped_chain = queue . pop_used_buffer_chain ( used ) ;
2021-06-23 08:19:24 -03:00
number_of_messages + + ;
2021-04-23 22:05:51 -03:00
} while ( ! popped_chain . is_empty ( ) ) ;
2021-06-23 08:19:24 -03:00
m_control_wait_queue . wake_n ( number_of_messages ) ;
} else {
u32 port_index = queue_index < 2 ? 0 : ( queue_index - 2 ) / 2 ;
if ( port_index > = m_ports . size ( ) | | ! m_ports . at ( port_index ) ) {
dbgln ( " Invalid queue_index {} " , queue_index ) ;
return ;
}
m_ports . at ( port_index ) - > handle_queue_update ( { } , queue_index ) ;
2021-04-15 06:39:48 -03:00
}
}
2021-08-27 06:18:13 -03:00
void Console : : setup_multiport ( )
2021-01-02 14:56:36 -03:00
{
2022-01-20 16:10:46 -03:00
m_control_receive_buffer = Memory : : RingBuffer : : try_create ( " VirtIOConsole control receive queue " sv , CONTROL_BUFFER_SIZE ) . release_value_but_fixme_should_propagate_errors ( ) ;
m_control_transmit_buffer = Memory : : RingBuffer : : try_create ( " VirtIOConsole control transmit queue " sv , CONTROL_BUFFER_SIZE ) . release_value_but_fixme_should_propagate_errors ( ) ;
2021-06-23 08:19:24 -03:00
auto & queue = get_queue ( CONTROL_RECEIVEQ ) ;
2021-08-21 20:49:22 -03:00
SpinlockLocker queue_lock ( queue . lock ( ) ) ;
2021-08-27 06:18:13 -03:00
QueueChain chain ( queue ) ;
2021-06-23 08:19:24 -03:00
auto offset = 0ul ;
while ( offset < CONTROL_BUFFER_SIZE ) {
auto buffer_start = m_control_receive_buffer - > start_of_region ( ) . offset ( offset ) ;
auto did_add_buffer = chain . add_buffer_to_chain ( buffer_start , CONTROL_MESSAGE_SIZE , BufferType : : DeviceWritable ) ;
VERIFY ( did_add_buffer ) ;
offset + = CONTROL_MESSAGE_SIZE ;
supply_chain_and_notify ( CONTROL_RECEIVEQ , chain ) ;
}
ControlMessage ready_event {
. id = 0 , // Unused
. event = ( u16 ) ControlEvent : : DeviceReady ,
. value = ( u16 ) ControlMessage : : Status : : Success
} ;
write_control_message ( ready_event ) ;
2021-01-02 14:56:36 -03:00
}
2021-08-27 06:18:13 -03:00
void Console : : process_control_message ( ControlMessage message )
2021-01-02 14:56:36 -03:00
{
2021-06-23 08:19:24 -03:00
switch ( message . event ) {
case ( u16 ) ControlEvent : : DeviceAdd : {
2022-03-06 17:07:04 -03:00
// FIXME: Do something sanely here if we can't allocate a work queue?
MUST ( g_io_work - > try_queue ( [ message , this ] ( ) - > void {
2021-08-14 04:13:27 -03:00
u32 id = message . id ;
if ( id > = m_ports . size ( ) ) {
dbgln ( " Device provided an invalid port number {}. max_nr_ports: {} " , id , m_ports . size ( ) ) ;
return ;
2021-12-08 10:00:18 -03:00
}
if ( ! m_ports . at ( id ) . is_null ( ) ) {
2021-08-14 04:13:27 -03:00
dbgln ( " Device tried to add port {} which was already added! " , id ) ;
return ;
}
2022-02-20 14:05:14 -03:00
auto port = MUST ( DeviceManagement : : the ( ) . try_create_device < VirtIO : : ConsolePort > ( id , * this ) ) ;
port - > init_receive_buffer ( { } ) ;
m_ports . at ( id ) = port ;
2021-11-29 12:03:38 -03:00
2021-08-14 04:13:27 -03:00
ControlMessage ready_event {
. id = static_cast < u32 > ( id ) ,
. event = ( u16 ) ControlEvent : : PortReady ,
. value = ( u16 ) ControlMessage : : Status : : Success
} ;
write_control_message ( ready_event ) ;
2022-03-06 17:07:04 -03:00
} ) ) ;
2021-06-20 10:46:04 -03:00
2021-06-23 08:19:24 -03:00
break ;
}
case ( u16 ) ControlEvent : : ConsolePort :
case ( u16 ) ControlEvent : : PortOpen : {
if ( message . id > = m_ports . size ( ) ) {
dbgln ( " Device provided an invalid port number {}. max_nr_ports: {} " , message . id , m_ports . size ( ) ) ;
return ;
2021-12-08 10:00:18 -03:00
}
if ( m_ports . at ( message . id ) . is_null ( ) ) {
2021-06-23 08:19:24 -03:00
dbgln ( " Device tried to open port {} which was not added! " , message . id ) ;
return ;
}
2021-06-20 10:46:04 -03:00
2021-06-23 08:19:24 -03:00
if ( message . value = = ( u16 ) ControlMessage : : PortStatus : : Open ) {
auto is_open = m_ports . at ( message . id ) - > is_open ( ) ;
if ( ! is_open ) {
m_ports . at ( message . id ) - > set_open ( { } , true ) ;
send_open_control_message ( message . id , true ) ;
}
} else if ( message . value = = ( u16 ) ControlMessage : : PortStatus : : Close ) {
m_ports . at ( message . id ) - > set_open ( { } , false ) ;
} else {
dbgln ( " Device specified invalid value {}. Must be 0 or 1. " , message . value ) ;
}
break ;
}
default :
dbgln ( " Unhandled message event {}! " , message . event ) ;
}
2021-01-02 14:56:36 -03:00
}
2021-08-27 06:18:13 -03:00
void Console : : write_control_message ( ControlMessage message )
2021-01-02 14:56:36 -03:00
{
2021-08-21 20:49:22 -03:00
SpinlockLocker ringbuffer_lock ( m_control_transmit_buffer - > lock ( ) ) ;
2021-01-02 14:56:36 -03:00
2021-06-23 08:19:24 -03:00
PhysicalAddress start_of_chunk ;
size_t length_of_chunk ;
2021-01-02 14:56:36 -03:00
2021-06-23 08:19:24 -03:00
auto data = UserOrKernelBuffer : : for_kernel_buffer ( ( u8 * ) & message ) ;
while ( ! m_control_transmit_buffer - > copy_data_in ( data , 0 , sizeof ( message ) , start_of_chunk , length_of_chunk ) ) {
ringbuffer_lock . unlock ( ) ;
m_control_wait_queue . wait_forever ( ) ;
ringbuffer_lock . lock ( ) ;
}
2021-04-23 22:05:51 -03:00
2021-06-23 08:19:24 -03:00
auto & queue = get_queue ( CONTROL_TRANSMITQ ) ;
2021-08-21 20:49:22 -03:00
SpinlockLocker queue_lock ( queue . lock ( ) ) ;
2021-08-27 06:18:13 -03:00
QueueChain chain ( queue ) ;
2021-04-23 22:05:51 -03:00
2021-06-23 08:19:24 -03:00
bool did_add_buffer = chain . add_buffer_to_chain ( start_of_chunk , length_of_chunk , BufferType : : DeviceReadable ) ;
VERIFY ( did_add_buffer ) ;
2021-01-02 14:56:36 -03:00
2021-06-23 08:19:24 -03:00
supply_chain_and_notify ( CONTROL_TRANSMITQ , chain ) ;
2021-01-02 14:56:36 -03:00
}
2021-08-27 06:18:13 -03:00
void Console : : send_open_control_message ( unsigned port_number , bool open )
2021-06-23 08:19:24 -03:00
{
ControlMessage port_open {
. id = static_cast < u32 > ( port_number ) ,
. event = ( u16 ) ControlEvent : : PortOpen ,
. value = open
} ;
write_control_message ( port_open ) ;
}
2021-01-02 14:56:36 -03:00
}