// The kernel depends on libj6 for some shared code, // but should not include the user-specific code. #ifndef __j6kernel #include #include #include #include #include #include #include #include #include #include #include #include namespace j6 { struct channel_memory_area { j6::mutex mutex; j6::condition waiting; util::bip_buffer buf; }; static bool check_channel_size(size_t s) { if (s < arch::frame_size || (s & (s - 1)) != 0) { syslog(j6::logs::ipc, j6::log_level::error, "Bad channel size: %lx", s); return false; } return true; } static uintptr_t create_channel_vma(j6_handle_t &vma, size_t size) { uintptr_t addr = 0; j6_status_t result = j6_vma_create_map(&vma, size, &addr, j6_vm_flag_write); if (result != j6_status_ok) { syslog(j6::logs::ipc, j6::log_level::error, "Failed to create channel VMA. Error: %lx", result); return 0; } syslog(j6::logs::ipc, j6::log_level::verbose, "Created channel VMA at 0x%lx size 0x%lx", addr, size); return addr; } static void init_channel_memory_area(channel_memory_area *area, size_t size) { new (&area->mutex) j6::mutex; new (&area->waiting) j6::condition; new (&area->buf) util::bip_buffer {size - sizeof(*area)}; } channel * channel::create(size_t tx_size, size_t rx_size) { if (!rx_size) rx_size = tx_size; if (!check_channel_size(tx_size) || !check_channel_size(rx_size)) return nullptr; j6_status_t result; j6_handle_t tx_vma = j6_handle_invalid; j6_handle_t rx_vma = j6_handle_invalid; uintptr_t tx_addr = create_channel_vma(tx_vma, tx_size); if (!tx_addr) return nullptr; uintptr_t rx_addr = create_channel_vma(rx_vma, rx_size); if (!rx_addr) { j6_vma_unmap(tx_vma, 0); j6_handle_close(tx_vma); return nullptr; } channel_memory_area *tx = reinterpret_cast(tx_addr); channel_memory_area *rx = reinterpret_cast(rx_addr); init_channel_memory_area(tx, tx_size); init_channel_memory_area(rx, rx_size); j6::syslog(logs::ipc, log_level::info, "Created new channel with handles {%x, %x}", tx_vma, rx_vma); return new channel {{tx_vma, rx_vma}, *tx, *rx}; } channel * channel::open(const channel_def &def) { j6_status_t result; uintptr_t tx_addr = 0; result = j6_vma_map(def.tx, 0, &tx_addr, 0); if (result != j6_status_ok) { syslog(j6::logs::ipc, j6::log_level::error, "Failed to map channel VMA. Error: %lx", result); return nullptr; } uintptr_t rx_addr = 0; result = j6_vma_map(def.rx, 0, &rx_addr, 0); if (result != j6_status_ok) { j6_vma_unmap(def.tx, 0); j6_handle_close(def.tx); syslog(j6::logs::ipc, j6::log_level::error, "Failed to map channel VMA. Error: %lx", result); return nullptr; } channel_memory_area *tx = reinterpret_cast(tx_addr); channel_memory_area *rx = reinterpret_cast(rx_addr); j6::syslog(logs::ipc, log_level::info, "Opening existing channel with handles {%x:0x%lx, %x:0x%lx}", def.tx, tx, def.rx, rx); return new channel { def, *tx, *rx }; } channel::channel( const channel_def &def, channel_memory_area &tx, channel_memory_area &rx) : m_def {def}, m_tx {tx}, m_rx {rx} { } size_t channel::reserve(size_t size, uint8_t **area, bool block) { if (size > m_tx.buf.buffer_size()) return j6_err_insufficient; j6::scoped_lock lock {m_tx.mutex}; while (m_tx.buf.write_available() < size) { if (!block) return 0; lock.release(); m_tx.waiting.wait(); lock.acquire(); } return m_tx.buf.reserve(size, area); } void channel::commit(size_t size) { j6::scoped_lock lock {m_tx.mutex}; m_tx.buf.commit(size); if (!size) return; m_tx.waiting.wake(); j6::syslog(logs::ipc, log_level::spam, "Sending %d bytes to channel on {%x}", size, m_def.tx); } size_t channel::get_block(uint8_t const **area, bool block) const { j6::scoped_lock lock {m_rx.mutex}; while (!m_rx.buf.size()) { if (!block) return 0; lock.release(); m_rx.waiting.wait(); lock.acquire(); } return m_rx.buf.get_block(area); } void channel::consume(size_t size) { j6::scoped_lock lock {m_tx.mutex}; m_rx.buf.consume(size); if (!size) return; m_rx.waiting.wake(); j6::syslog(logs::ipc, log_level::spam, "Read %d bytes from channel on {%x}", size, m_def.rx); } } // namespace j6 #endif // __j6kernel