1
0
mirror of https://github.com/sgmarz/osblog.git synced 2024-11-23 18:06:20 +04:00

Added starting point for GPU code.

This commit is contained in:
Stephen Marz 2020-05-15 10:34:22 -04:00
parent 723963e1ec
commit ea33bfe629
2 changed files with 134 additions and 4 deletions

133
risc_v/src/gpu.rs Executable file
View File

@ -0,0 +1,133 @@
// gpu.rs
// Graphics stuff
// Stephen Marz
// 12 May 2020
#![allow(dead_code)]
use crate::{kmem::{kfree, kmalloc},
page::{zalloc, PAGE_SIZE},
virtio,
virtio::{Descriptor, MmioOffsets, Queue, StatusField, VIRTIO_RING_SIZE}};
use core::{mem::size_of, ptr::null_mut};
pub struct GpuDevice {
queue: *mut Queue,
dev: *mut u32,
idx: u16,
ack_used_idx: u16,
}
impl GpuDevice {
pub const fn new() -> Self {
Self { queue: null_mut(),
dev: null_mut(),
idx: 0,
ack_used_idx: 0, }
}
}
static mut GPU_DEVICES: [Option<GpuDevice>; 8] = [
None,
None,
None,
None,
None,
None,
None,
None,
];
pub fn setup_gpu_device(ptr: *mut u32) -> bool {
unsafe {
// We can get the index of the device based on its address.
// 0x1000_1000 is index 0
// 0x1000_2000 is index 1
// ...
// 0x1000_8000 is index 7
// To get the number that changes over, we shift right 12 places (3 hex digits)
let idx = (ptr as usize - virtio::MMIO_VIRTIO_START) >> 12;
// [Driver] Device Initialization
// 1. Reset the device (write 0 into status)
ptr.add(MmioOffsets::Status.scale32()).write_volatile(0);
let mut status_bits = StatusField::Acknowledge.val32();
// 2. Set ACKNOWLEDGE status bit
ptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);
// 3. Set the DRIVER status bit
status_bits |= StatusField::DriverOk.val32();
ptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);
// 4. Read device feature bits, write subset of feature
// bits understood by OS and driver to the device.
let host_features = ptr.add(MmioOffsets::HostFeatures.scale32()).read_volatile();
ptr.add(MmioOffsets::GuestFeatures.scale32()).write_volatile(host_features);
// 5. Set the FEATURES_OK status bit
status_bits |= StatusField::FeaturesOk.val32();
ptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);
// 6. Re-read status to ensure FEATURES_OK is still set.
// Otherwise, it doesn't support our features.
let status_ok = ptr.add(MmioOffsets::Status.scale32()).read_volatile();
// If the status field no longer has features_ok set,
// that means that the device couldn't accept
// the features that we request. Therefore, this is
// considered a "failed" state.
if false == StatusField::features_ok(status_ok) {
print!("features fail...");
ptr.add(MmioOffsets::Status.scale32()).write_volatile(StatusField::Failed.val32());
return false;
}
// 7. Perform device-specific setup.
// Set the queue num. We have to make sure that the
// queue size is valid because the device can only take
// a certain size.
let qnmax = ptr.add(MmioOffsets::QueueNumMax.scale32()).read_volatile();
ptr.add(MmioOffsets::QueueNum.scale32()).write_volatile(VIRTIO_RING_SIZE as u32);
if VIRTIO_RING_SIZE as u32 > qnmax {
print!("queue size fail...");
return false;
}
// First, if the block device array is empty, create it!
// We add 4095 to round this up and then do an integer
// divide to truncate the decimal. We don't add 4096,
// because if it is exactly 4096 bytes, we would get two
// pages, not one.
let num_pages = (size_of::<Queue>() + PAGE_SIZE - 1) / PAGE_SIZE;
// println!("np = {}", num_pages);
// We allocate a page for each device. This will the the
// descriptor where we can communicate with the block
// device. We will still use an MMIO register (in
// particular, QueueNotify) to actually tell the device
// we put something in memory. We also have to be
// careful with memory ordering. We don't want to
// issue a notify before all memory writes have
// finished. We will look at that later, but we need
// what is called a memory "fence" or barrier.
ptr.add(MmioOffsets::QueueSel.scale32()).write_volatile(0);
// Alignment is very important here. This is the memory address
// alignment between the available and used rings. If this is wrong,
// then we and the device will refer to different memory addresses
// and hence get the wrong data in the used ring.
// ptr.add(MmioOffsets::QueueAlign.scale32()).write_volatile(2);
let queue_ptr = zalloc(num_pages) as *mut Queue;
let queue_pfn = queue_ptr as u32;
ptr.add(MmioOffsets::GuestPageSize.scale32()).write_volatile(PAGE_SIZE as u32);
// QueuePFN is a physical page number, however it
// appears for QEMU we have to write the entire memory
// address. This is a physical memory address where we
// (the OS) and the block device have in common for
// making and receiving requests.
ptr.add(MmioOffsets::QueuePfn.scale32()).write_volatile(queue_pfn / PAGE_SIZE as u32);
// 8. Set the DRIVER_OK status bit. Device is now "live"
status_bits |= StatusField::DriverOk.val32();
ptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);
let rngdev = GpuDevice {
queue: queue_ptr,
dev: ptr,
idx: 0,
ack_used_idx: 0,
};
GPU_DEVICES[idx] = Some(rngdev);
true
}
}

View File

@ -5,6 +5,7 @@
use crate::{block, block::setup_block_device, page::PAGE_SIZE};
use crate::rng::setup_entropy_device;
use crate::gpu::setup_gpu_device;
use core::mem::size_of;
// Flags
@ -326,10 +327,6 @@ pub fn setup_network_device(_ptr: *mut u32) -> bool {
false
}
pub fn setup_gpu_device(_ptr: *mut u32) -> bool {
false
}
pub fn setup_input_device(_ptr: *mut u32) -> bool {
false
}