diff options
author | Jelle Raaijmakers <jelle@gmta.nl> | 2021-11-23 12:49:37 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-11-23 14:08:14 +0100 |
commit | eb2b0d847ea1378b487074e31bb03f58b24a990e (patch) | |
tree | 10b491d9ffb31ee389dc5d472908ae934cd97a31 /Kernel/Devices/Audio | |
parent | 70ca8b24dc9eaaf7c6637daf15f445b31e890e25 (diff) | |
download | serenity-eb2b0d847ea1378b487074e31bb03f58b24a990e.zip |
Kernel: Allow writes larger than `PAGE_SIZE` to AC97 device
Previously, `cat /dev/random > /dev/audio` would crash Serenity. Fix
this by splitting up the written data into `PAGE_SIZE` chunks.
Diffstat (limited to 'Kernel/Devices/Audio')
-rw-r--r-- | Kernel/Devices/Audio/AC97.cpp | 23 | ||||
-rw-r--r-- | Kernel/Devices/Audio/AC97.h | 1 |
2 files changed, 18 insertions, 6 deletions
diff --git a/Kernel/Devices/Audio/AC97.cpp b/Kernel/Devices/Audio/AC97.cpp index 856250d953..f2e0302cf9 100644 --- a/Kernel/Devices/Audio/AC97.cpp +++ b/Kernel/Devices/Audio/AC97.cpp @@ -169,8 +169,6 @@ void AC97::set_pcm_output_volume(u8 left_channel, u8 right_channel, Muted mute) ErrorOr<size_t> AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const& data, size_t length) { - VERIFY(length <= PAGE_SIZE); - if (!m_output_buffer) { m_output_buffer = TRY(allocate_physical_buffer(m_output_buffer_page_count * PAGE_SIZE, "AC97 Output buffer"sv)); } @@ -179,9 +177,23 @@ ErrorOr<size_t> AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const& m_buffer_descriptor_list = TRY(allocate_physical_buffer(buffer_descriptor_list_size, "AC97 Buffer Descriptor List"sv)); } - cli(); + auto remaining = length; + size_t offset = 0; + while (remaining > 0) { + TRY(write_single_buffer(data, offset, min(remaining, PAGE_SIZE))); + offset += PAGE_SIZE; + remaining -= PAGE_SIZE; + } + + return length; +} + +ErrorOr<void> AC97::write_single_buffer(UserOrKernelBuffer const& data, size_t offset, size_t length) +{ + VERIFY(length <= PAGE_SIZE); // Block until we can write into an unused buffer + cli(); do { auto pcm_out_status = m_pcm_out_channel.reg(AC97Channel::Register::Status).in<u16>(); auto is_dma_controller_halted = (pcm_out_status & AudioStatusRegisterFlag::DMAControllerHalted) > 0; @@ -199,11 +211,10 @@ ErrorOr<size_t> AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const& m_irq_queue.wait_forever("AC97"sv); } while (m_pcm_out_channel.dma_running()); - sti(); // Copy data from userspace into one of our buffers - TRY(data.read(m_output_buffer->vaddr_from_page_index(m_output_buffer_page_index).as_ptr(), length)); + TRY(data.read(m_output_buffer->vaddr_from_page_index(m_output_buffer_page_index).as_ptr(), offset, length)); if (!m_pcm_out_channel.dma_running()) { reset_pcm_out(); @@ -226,7 +237,7 @@ ErrorOr<size_t> AC97::write(OpenFileDescription&, u64, UserOrKernelBuffer const& m_output_buffer_page_index = (m_output_buffer_page_index + 1) % m_output_buffer_page_count; m_buffer_descriptor_list_index = (m_buffer_descriptor_list_index + 1) % buffer_descriptor_list_max_entries; - return length; + return {}; } void AC97::AC97Channel::reset() diff --git a/Kernel/Devices/Audio/AC97.h b/Kernel/Devices/Audio/AC97.h index e6b60bf153..db85ad5452 100644 --- a/Kernel/Devices/Audio/AC97.h +++ b/Kernel/Devices/Audio/AC97.h @@ -152,6 +152,7 @@ private: void set_master_output_volume(u8, u8, Muted); void set_pcm_output_sample_rate(u16); void set_pcm_output_volume(u8, u8, Muted); + ErrorOr<void> write_single_buffer(UserOrKernelBuffer const&, size_t, size_t); OwnPtr<Memory::Region> m_buffer_descriptor_list; u8 m_buffer_descriptor_list_index = 0; |