virtio-gpud: Handle multiple VT's
The code will become simpler once handoff is implemented such that virtio-gpud no longer needs to support reset and reinitialization of the virtio gpu device.
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use event::{user_data, EventQueue};
|
||||
@@ -225,6 +226,18 @@ impl Default for GetDisplayInfo {
|
||||
}
|
||||
}
|
||||
|
||||
static RESOURCE_ALLOC: AtomicU32 = AtomicU32::new(1); // XXX: 0 is reserved for whatever that takes `resource_id`.
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct ResourceId(u32);
|
||||
|
||||
impl ResourceId {
|
||||
fn alloc() -> Self {
|
||||
ResourceId(RESOURCE_ALLOC.fetch_add(1, Ordering::SeqCst))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(u32)]
|
||||
pub enum ResourceFormat {
|
||||
@@ -239,14 +252,16 @@ pub enum ResourceFormat {
|
||||
pub struct ResourceCreate2d {
|
||||
pub header: ControlHeader,
|
||||
|
||||
resource_id: VolatileCell<u32>,
|
||||
// FIXME we can likely use regular loads and stores as the ring buffer should provide the
|
||||
// necessary synchronization.
|
||||
resource_id: VolatileCell<ResourceId>,
|
||||
format: VolatileCell<ResourceFormat>,
|
||||
width: VolatileCell<u32>,
|
||||
height: VolatileCell<u32>,
|
||||
}
|
||||
|
||||
impl ResourceCreate2d {
|
||||
make_getter_setter!(resource_id: u32, format: ResourceFormat, width: u32, height: u32);
|
||||
make_getter_setter!(resource_id: ResourceId, format: ResourceFormat, width: u32, height: u32);
|
||||
}
|
||||
|
||||
impl Default for ResourceCreate2d {
|
||||
@@ -257,7 +272,7 @@ impl Default for ResourceCreate2d {
|
||||
..Default::default()
|
||||
},
|
||||
|
||||
resource_id: VolatileCell::new(0),
|
||||
resource_id: VolatileCell::new(ResourceId(0)),
|
||||
format: VolatileCell::new(ResourceFormat::Unknown),
|
||||
width: VolatileCell::new(0),
|
||||
height: VolatileCell::new(0),
|
||||
@@ -277,12 +292,12 @@ pub struct MemEntry {
|
||||
#[repr(C)]
|
||||
pub struct AttachBacking {
|
||||
pub header: ControlHeader,
|
||||
pub resource_id: u32,
|
||||
pub resource_id: ResourceId,
|
||||
pub num_entries: u32,
|
||||
}
|
||||
|
||||
impl AttachBacking {
|
||||
pub fn new(resource_id: u32, num_entries: u32) -> Self {
|
||||
pub fn new(resource_id: ResourceId, num_entries: u32) -> Self {
|
||||
Self {
|
||||
header: ControlHeader::with_ty(CommandTy::ResourceAttachBacking),
|
||||
resource_id,
|
||||
@@ -295,12 +310,12 @@ impl AttachBacking {
|
||||
#[repr(C)]
|
||||
pub struct DetachBacking {
|
||||
pub header: ControlHeader,
|
||||
pub resource_id: u32,
|
||||
pub resource_id: ResourceId,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
impl DetachBacking {
|
||||
pub fn new(resource_id: u32) -> Self {
|
||||
pub fn new(resource_id: ResourceId) -> Self {
|
||||
Self {
|
||||
header: ControlHeader::with_ty(CommandTy::ResourceDetachBacking),
|
||||
resource_id,
|
||||
@@ -314,12 +329,12 @@ impl DetachBacking {
|
||||
pub struct ResourceFlush {
|
||||
pub header: ControlHeader,
|
||||
pub rect: GpuRect,
|
||||
pub resource_id: u32,
|
||||
pub resource_id: ResourceId,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
impl ResourceFlush {
|
||||
pub fn new(resource_id: u32, rect: GpuRect) -> Self {
|
||||
pub fn new(resource_id: ResourceId, rect: GpuRect) -> Self {
|
||||
Self {
|
||||
header: ControlHeader::with_ty(CommandTy::ResourceFlush),
|
||||
rect,
|
||||
@@ -333,12 +348,12 @@ impl ResourceFlush {
|
||||
#[repr(C)]
|
||||
pub struct ResourceUnref {
|
||||
pub header: ControlHeader,
|
||||
pub resource_id: u32,
|
||||
pub resource_id: ResourceId,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
impl ResourceUnref {
|
||||
pub fn new(resource_id: u32) -> Self {
|
||||
pub fn new(resource_id: ResourceId) -> Self {
|
||||
Self {
|
||||
header: ControlHeader::with_ty(CommandTy::ResourceUnref),
|
||||
resource_id,
|
||||
@@ -353,11 +368,11 @@ pub struct SetScanout {
|
||||
pub header: ControlHeader,
|
||||
pub rect: GpuRect,
|
||||
pub scanout_id: u32,
|
||||
pub resource_id: u32,
|
||||
pub resource_id: ResourceId,
|
||||
}
|
||||
|
||||
impl SetScanout {
|
||||
pub fn new(scanout_id: u32, resource_id: u32, rect: GpuRect) -> Self {
|
||||
pub fn new(scanout_id: u32, resource_id: ResourceId, rect: GpuRect) -> Self {
|
||||
Self {
|
||||
header: ControlHeader::with_ty(CommandTy::SetScanout),
|
||||
|
||||
@@ -374,12 +389,12 @@ pub struct XferToHost2d {
|
||||
pub header: ControlHeader,
|
||||
pub rect: GpuRect,
|
||||
pub offset: u64,
|
||||
pub resource_id: u32,
|
||||
pub resource_id: ResourceId,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
impl XferToHost2d {
|
||||
pub fn new(resource_id: u32, rect: GpuRect) -> Self {
|
||||
pub fn new(resource_id: ResourceId, rect: GpuRect) -> Self {
|
||||
Self {
|
||||
header: ControlHeader {
|
||||
ty: VolatileCell::new(CommandTy::TransferToHost2d),
|
||||
@@ -455,7 +470,7 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
let event_queue: EventQueue<Source> =
|
||||
EventQueue::new().expect("vesad: failed to create event queue");
|
||||
EventQueue::new().expect("virtio-gpud: failed to create event queue");
|
||||
event_queue
|
||||
.subscribe(
|
||||
scheme.inputd_handle.inner().as_raw_fd() as usize,
|
||||
@@ -477,14 +492,14 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
|
||||
let all = [Source::Input, Source::Scheme];
|
||||
for event in all
|
||||
.into_iter()
|
||||
.chain(event_queue.map(|e| e.expect("vesad: failed to get next event").user_data))
|
||||
.chain(event_queue.map(|e| e.expect("virtio-gpud: failed to get next event").user_data))
|
||||
{
|
||||
match event {
|
||||
Source::Input => {
|
||||
while let Some(vt_event) = scheme
|
||||
.inputd_handle
|
||||
.read_vt_event()
|
||||
.expect("vesad: failed to read display handle")
|
||||
.expect("virtio-gpud: failed to read display handle")
|
||||
{
|
||||
scheme.handle_vt_event(vt_event);
|
||||
}
|
||||
@@ -508,7 +523,7 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
|
||||
call_request.handle_scheme_mut(&mut scheme),
|
||||
SignalBehavior::Restart,
|
||||
)
|
||||
.expect("vesad: failed to write display scheme");
|
||||
.expect("virtio-gpud: failed to write display scheme");
|
||||
}
|
||||
RequestKind::Cancellation(_cancellation_request) => {
|
||||
// FIXME handle this
|
||||
@@ -522,23 +537,7 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
let Some(request) = socket.next_request(SignalBehavior::Restart)? else {
|
||||
// Scheme likely got unmounted
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
match request.kind() {
|
||||
RequestKind::Call(call_request) => {
|
||||
socket.write_response(
|
||||
call_request.handle_scheme_mut(&mut scheme),
|
||||
SignalBehavior::Restart,
|
||||
)?;
|
||||
}
|
||||
RequestKind::Cancellation(_cancellation_request) => {}
|
||||
RequestKind::MsyncMsg | RequestKind::MunmapMsg | RequestKind::MmapMsg => unreachable!(),
|
||||
}
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
fn daemon_runner(redox_daemon: redox_daemon::Daemon) -> ! {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::cell::OnceCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use common::{dma::Dma, sgl};
|
||||
use inputd::{Damage, VtEvent, VtEventKind};
|
||||
|
||||
use redox_scheme::SchemeMut;
|
||||
use syscall::{Error as SysError, MapFlags, EAGAIN, EINVAL, PAGE_SIZE};
|
||||
use syscall::{Error as SysError, MapFlags, EINVAL, PAGE_SIZE};
|
||||
|
||||
use virtio_core::spec::{Buffer, ChainBuilder, DescriptorFlags};
|
||||
use virtio_core::transport::{Error, Queue, Transport};
|
||||
@@ -16,8 +16,6 @@ use virtio_core::utils::VolatileCell;
|
||||
|
||||
use crate::*;
|
||||
|
||||
static RESOURCE_ALLOC: AtomicU32 = AtomicU32::new(1); // XXX: 0 is reserved for whatever that takes `resource_id`.
|
||||
|
||||
impl Into<GpuRect> for &Damage {
|
||||
fn into(self) -> GpuRect {
|
||||
GpuRect {
|
||||
@@ -34,12 +32,13 @@ pub struct Display<'a> {
|
||||
cursor_queue: Arc<Queue<'a>>,
|
||||
transport: Arc<dyn Transport>,
|
||||
|
||||
mapped: OnceCell<sgl::Sgl>,
|
||||
active_vt: RefCell<usize>,
|
||||
vts_map: RefCell<HashMap<usize, sgl::Sgl>>,
|
||||
vts_res: RefCell<HashMap<usize, ResourceId>>,
|
||||
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
||||
resource_id: u32,
|
||||
id: usize,
|
||||
|
||||
is_reseted: AtomicBool,
|
||||
@@ -56,22 +55,24 @@ impl<'a> Display<'a> {
|
||||
control_queue,
|
||||
cursor_queue,
|
||||
|
||||
mapped: OnceCell::new(),
|
||||
active_vt: RefCell::new(0),
|
||||
vts_map: RefCell::new(HashMap::new()),
|
||||
vts_res: RefCell::new(HashMap::new()),
|
||||
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
transport,
|
||||
|
||||
id,
|
||||
resource_id: RESOURCE_ALLOC.fetch_add(1, Ordering::SeqCst),
|
||||
|
||||
is_reseted: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
async fn init(&self) -> Result<(), Error> {
|
||||
async fn init(&self, vt: usize) -> Result<(), Error> {
|
||||
if !self.is_reseted.load(Ordering::SeqCst) {
|
||||
// The device is already initialized.
|
||||
self.set_scanout(vt).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -80,7 +81,7 @@ impl<'a> Display<'a> {
|
||||
log::info!("virtio-gpu: initializing GPU after a reset");
|
||||
|
||||
crate::reinit(self.control_queue.clone(), self.cursor_queue.clone())?;
|
||||
self.remap_screen().await?;
|
||||
self.set_scanout(vt).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -111,20 +112,9 @@ impl<'a> Display<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Is this a no-op?
|
||||
async fn remap_screen(&self) -> Result<*mut u8, Error> {
|
||||
let bpp = 32;
|
||||
|
||||
let fb_size = self.width as usize * self.height as usize * bpp / 8;
|
||||
|
||||
let mapped = self.mapped.get().unwrap();
|
||||
self.map_screen_with(0, fb_size, mapped.as_ptr(), mapped.chunks())
|
||||
.await
|
||||
}
|
||||
|
||||
async fn map_screen(&self, offset: usize) -> Result<*mut u8, Error> {
|
||||
if let Some(mapped) = self.mapped.get() {
|
||||
return Ok(mapped.as_ptr().wrapping_add(offset));
|
||||
async fn mmap_screen(&self, vt: usize, offset: usize) -> Result<*mut u8, Error> {
|
||||
if let Some(sgl) = self.vts_map.borrow().get(&vt) {
|
||||
return Ok(sgl.as_ptr().wrapping_add(offset));
|
||||
}
|
||||
|
||||
let bpp = 32;
|
||||
@@ -134,35 +124,40 @@ impl<'a> Display<'a> {
|
||||
unsafe {
|
||||
core::ptr::write_bytes(mapped.as_ptr() as *mut u8, 255, fb_size);
|
||||
}
|
||||
let _ = self.mapped.set(mapped);
|
||||
let mapped = self.mapped.get().unwrap();
|
||||
|
||||
self.map_screen_with(offset, fb_size, mapped.as_ptr(), mapped.chunks())
|
||||
.await
|
||||
let mut mapped_vts = self.vts_map.borrow_mut();
|
||||
let sgl = mapped_vts.entry(vt).or_insert(mapped);
|
||||
Ok(sgl.as_ptr().wrapping_add(offset))
|
||||
}
|
||||
|
||||
async fn map_screen_with(
|
||||
&self,
|
||||
offset: usize,
|
||||
_size: usize,
|
||||
virt: *mut u8,
|
||||
chunks: &[sgl::Chunk],
|
||||
) -> Result<*mut u8, Error> {
|
||||
async fn create_res_for_screen(&self, vt: usize) -> Result<ResourceId, Error> {
|
||||
if let Some(&res_id) = self.vts_res.borrow().get(&vt) {
|
||||
return Ok(res_id);
|
||||
}
|
||||
|
||||
self.mmap_screen(vt, 0).await?;
|
||||
|
||||
let vts_map = self.vts_map.borrow();
|
||||
let mapped = &vts_map.get(&vt).unwrap();
|
||||
|
||||
let res_id = ResourceId::alloc();
|
||||
|
||||
// Create a host resource using `VIRTIO_GPU_CMD_RESOURCE_CREATE_2D`.
|
||||
let mut request = Dma::new(ResourceCreate2d::default())?;
|
||||
|
||||
request.set_width(self.width);
|
||||
request.set_height(self.height);
|
||||
request.set_format(ResourceFormat::Bgrx);
|
||||
request.set_resource_id(self.resource_id);
|
||||
request.set_resource_id(res_id);
|
||||
|
||||
self.send_request(request).await?;
|
||||
let header = self.send_request(request).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
|
||||
// Use the allocated framebuffer from tthe guest ram, and attach it as backing
|
||||
// storage to the resource just created, using `VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING`.
|
||||
|
||||
let mut mem_entries = unsafe { Dma::zeroed_slice(chunks.len())?.assume_init() };
|
||||
for (entry, chunk) in mem_entries.iter_mut().zip(chunks.iter()) {
|
||||
let mut mem_entries = unsafe { Dma::zeroed_slice(mapped.chunks().len())?.assume_init() };
|
||||
for (entry, chunk) in mem_entries.iter_mut().zip(mapped.chunks().iter()) {
|
||||
*entry = MemEntry {
|
||||
address: chunk.phys as u64,
|
||||
length: chunk.length.next_multiple_of(PAGE_SIZE) as u32,
|
||||
@@ -170,10 +165,7 @@ impl<'a> Display<'a> {
|
||||
};
|
||||
}
|
||||
|
||||
let attach_request = Dma::new(AttachBacking::new(
|
||||
self.resource_id,
|
||||
mem_entries.len() as u32,
|
||||
))?;
|
||||
let attach_request = Dma::new(AttachBacking::new(res_id, mem_entries.len() as u32))?;
|
||||
let header = Dma::new(ControlHeader::default())?;
|
||||
let command = ChainBuilder::new()
|
||||
.chain(Buffer::new(&attach_request))
|
||||
@@ -184,21 +176,33 @@ impl<'a> Display<'a> {
|
||||
self.control_queue.send(command).await;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
|
||||
let mut mapped_vts = self.vts_res.borrow_mut();
|
||||
mapped_vts.insert(vt, res_id);
|
||||
Ok(res_id)
|
||||
}
|
||||
|
||||
async fn set_scanout(&self, vt: usize) -> Result<(), Error> {
|
||||
let res_id = self.create_res_for_screen(vt).await?;
|
||||
|
||||
let scanout_request = Dma::new(SetScanout::new(
|
||||
self.id as u32,
|
||||
self.resource_id,
|
||||
res_id,
|
||||
GpuRect::new(0, 0, self.width, self.height),
|
||||
))?;
|
||||
let header = self.send_request(scanout_request).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
|
||||
self.flush(None).await?;
|
||||
self.flush(vt, None).await?;
|
||||
|
||||
Ok(virt.wrapping_add(offset))
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If `damage` is `None`, the entire screen is flushed.
|
||||
async fn flush(&self, damage: Option<&Damage>) -> Result<(), Error> {
|
||||
async fn flush(&self, vt: usize, damage: Option<&Damage>) -> Result<(), Error> {
|
||||
if vt != *self.active_vt.borrow() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let damage = if let Some(damage) = damage {
|
||||
damage.into()
|
||||
} else {
|
||||
@@ -211,7 +215,7 @@ impl<'a> Display<'a> {
|
||||
};
|
||||
|
||||
let req = Dma::new(XferToHost2d::new(
|
||||
self.resource_id,
|
||||
self.vts_res.borrow()[&vt],
|
||||
GpuRect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
@@ -222,21 +226,26 @@ impl<'a> Display<'a> {
|
||||
let header = self.send_request(req).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
|
||||
self.flush_resource(ResourceFlush::new(self.resource_id, damage.clone()))
|
||||
.await?;
|
||||
self.flush_resource(ResourceFlush::new(
|
||||
self.vts_res.borrow()[&vt],
|
||||
damage.clone(),
|
||||
))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This detaches any backing pages from the display and unrefs the resource. Also resets the
|
||||
/// device, which is required to go back to legacy mode.
|
||||
async fn detach(&self) -> Result<(), Error> {
|
||||
let request = Dma::new(DetachBacking::new(self.resource_id))?;
|
||||
let header = self.send_request(request).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
for (_vt, res_id) in self.vts_res.borrow_mut().drain() {
|
||||
let request = Dma::new(DetachBacking::new(res_id))?;
|
||||
let header = self.send_request(request).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
|
||||
let request = Dma::new(ResourceUnref::new(self.resource_id))?;
|
||||
let header = self.send_request(request).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
let request = Dma::new(ResourceUnref::new(res_id))?;
|
||||
let header = self.send_request(request).await?;
|
||||
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
|
||||
}
|
||||
|
||||
// Go back to legacy mode.
|
||||
self.transport.reset();
|
||||
@@ -310,6 +319,11 @@ impl<'a> Scheme<'a> {
|
||||
transport.clone(),
|
||||
id,
|
||||
);
|
||||
// FIXME this is a hack to avoid breaking things while we need to co-exist with vesad
|
||||
// Somehow necessary to ensure that creating a resource on the first reinitialization
|
||||
// after this detach doesn't fail.
|
||||
display.init(1).await?;
|
||||
display.detach().await?;
|
||||
|
||||
result.push(Arc::new(display));
|
||||
}
|
||||
@@ -340,14 +354,12 @@ impl<'a> Scheme<'a> {
|
||||
VtEventKind::Activate => {
|
||||
log::info!("activate {}", vt_event.vt);
|
||||
|
||||
for handle in self.handles.values() {
|
||||
if handle.vt != vt_event.vt {
|
||||
continue;
|
||||
}
|
||||
|
||||
for display in &self.displays {
|
||||
log::warn!("virtio-gpu: activating");
|
||||
|
||||
futures::executor::block_on(handle.display.init()).unwrap();
|
||||
futures::executor::block_on(display.init(vt_event.vt)).unwrap();
|
||||
|
||||
*display.active_vt.borrow_mut() = vt_event.vt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,7 +420,7 @@ impl<'a> SchemeMut for Scheme<'a> {
|
||||
|
||||
fn fsync(&mut self, id: usize) -> syscall::Result<usize> {
|
||||
let handle = self.handles.get(&id).ok_or(SysError::new(EINVAL))?;
|
||||
futures::executor::block_on(handle.display.flush(None)).unwrap();
|
||||
futures::executor::block_on(handle.display.flush(handle.vt, None)).unwrap();
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
@@ -433,10 +445,11 @@ impl<'a> SchemeMut for Scheme<'a> {
|
||||
) -> syscall::Result<usize> {
|
||||
let handle = self.handles.get(&id).ok_or(SysError::new(EINVAL))?;
|
||||
|
||||
// The VT is not active and the device is reseted. Ask them to try
|
||||
// again later.
|
||||
// The VT is not active and the device is reseted. Ignore the damage. We will recreate the
|
||||
// backing storage from scratch next time we initialize, which is equivalent to damaging the
|
||||
// entire buffer.
|
||||
if handle.display.is_reseted.load(Ordering::SeqCst) {
|
||||
return Err(SysError::new(EAGAIN));
|
||||
return Ok(buf.len());
|
||||
}
|
||||
|
||||
let damages = unsafe {
|
||||
@@ -447,7 +460,7 @@ impl<'a> SchemeMut for Scheme<'a> {
|
||||
};
|
||||
|
||||
for damage in damages {
|
||||
futures::executor::block_on(handle.display.flush(Some(damage))).unwrap();
|
||||
futures::executor::block_on(handle.display.flush(handle.vt, Some(damage))).unwrap();
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
@@ -465,7 +478,9 @@ impl<'a> SchemeMut for Scheme<'a> {
|
||||
) -> syscall::Result<usize> {
|
||||
log::info!("KSMSG MMAP {} {:?} {} {}", id, flags, offset, size);
|
||||
let handle = self.handles.get(&id).ok_or(SysError::new(EINVAL))?;
|
||||
let ptr = futures::executor::block_on(handle.display.map_screen(offset as usize)).unwrap();
|
||||
let ptr =
|
||||
futures::executor::block_on(handle.display.mmap_screen(handle.vt, offset as usize))
|
||||
.unwrap();
|
||||
Ok(ptr as usize)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user