genode/repos/dde_linux/src/drivers/framebuffer/intel/lx_emul.cc

1730 lines
35 KiB
C++

/*
* \brief Emulation of Linux kernel interfaces
* \author Norman Feske
* \author Stefan Kalkowski
* \date 2015-08-19
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/* Genode includes */
#include <util/bit_allocator.h>
#include <base/log.h>
#include <base/attached_io_mem_dataspace.h>
#include <os/reporter.h>
/* local includes */
#include <component.h>
/* DRM-specific includes */
#include <lx_emul.h>
#include <lx_emul_c.h>
#include <lx_emul/extern_c_begin.h>
#include <drm/drmP.h>
#include <drm/drm_gem.h>
#include <lx_emul/extern_c_end.h>
#include <lx_emul/impl/kernel.h>
#include <lx_emul/impl/delay.h>
#include <lx_emul/impl/slab.h>
#include <lx_emul/impl/gfp.h>
#include <lx_emul/impl/io.h>
#include <lx_emul/impl/pci.h>
#include <lx_emul/impl/work.h>
#include <lx_emul/impl/spinlock.h>
#include <lx_emul/impl/mutex.h>
#include <lx_emul/impl/sched.h>
#include <lx_emul/impl/timer.h>
#include <lx_emul/impl/completion.h>
#include <lx_emul/impl/wait.h>
static struct drm_device * lx_drm_device = nullptr;
struct Drm_guard
{
drm_device * _dev;
Drm_guard(drm_device *dev) : _dev(dev)
{
if (dev) {
mutex_lock(&dev->mode_config.mutex);
mutex_lock(&dev->mode_config.blob_lock);
drm_modeset_lock_all(dev);
}
}
~Drm_guard()
{
if (_dev) {
drm_modeset_unlock_all(_dev);
mutex_unlock(&_dev->mode_config.mutex);
mutex_unlock(&_dev->mode_config.blob_lock);
}
}
};
template <typename FUNCTOR>
static inline void lx_for_each_connector(drm_device * dev, FUNCTOR f)
{
struct drm_connector *connector;
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
f(connector);
}
drm_display_mode *
Framebuffer::Driver::_preferred_mode(drm_connector *connector)
{
using namespace Genode;
using Genode::size_t;
/* try to read configuration for connector */
try {
Xml_node config = _session.config();
Xml_node xn = config.sub_node();
for (unsigned i = 0; i < config.num_sub_nodes(); xn = xn.next()) {
if (!xn.has_type("connector"))
continue;
String<64> con_policy;
xn.attribute("name").value(&con_policy);
if (Genode::strcmp(con_policy.string(), connector->name) != 0)
continue;
bool enabled = xn.attribute_value("enabled", true);
if (!enabled)
return nullptr;
unsigned long width = 0;
unsigned long height = 0;
xn.attribute("width").value(&width);
xn.attribute("height").value(&height);
struct drm_display_mode *mode;
list_for_each_entry(mode, &connector->modes, head) {
if (mode->hdisplay == (int) width &&
mode->vdisplay == (int) height)
return mode;
};
}
} catch (...) {
/**
* If no config is given, we take the most wide mode of a
* connector as long as it is connected at all
*/
if (connector->status != connector_status_connected)
return nullptr;
struct drm_display_mode *mode = nullptr, *tmp;
list_for_each_entry(tmp, &connector->modes, head) {
if (!mode || tmp->hdisplay > mode->hdisplay) mode = tmp;
};
return mode;
}
return nullptr;
}
void Framebuffer::Driver::finish_initialization()
{
lx_c_set_driver(lx_drm_device, (void*)this);
generate_report();
_session.config_changed();
}
#include <lx_kit/irq.h>
void Framebuffer::Driver::_poll()
{
Lx::Pci_dev * pci_dev = (Lx::Pci_dev*) lx_drm_device->pdev->bus;
Lx::Irq::irq().inject_irq(pci_dev->client());
}
void Framebuffer::Driver::set_polling(unsigned long poll)
{
if (poll == _poll_ms) return;
_poll_ms = poll;
if (_poll_ms) {
_timer.sigh(_poll_handler);
_timer.trigger_periodic(_poll_ms * 1000);
} else {
_timer.sigh(Genode::Signal_context_capability());
}
}
void Framebuffer::Driver::update_mode()
{
using namespace Genode;
Configuration old = _config;
_config = Configuration();
lx_for_each_connector(lx_drm_device, [&] (drm_connector *c) {
drm_display_mode * mode = _preferred_mode(c);
if (!mode) return;
if (mode->hdisplay > _config._lx.width) _config._lx.width = mode->hdisplay;
if (mode->vdisplay > _config._lx.height) _config._lx.height = mode->vdisplay;
});
lx_c_allocate_framebuffer(lx_drm_device, &_config._lx);
{
Drm_guard guard(lx_drm_device);
lx_for_each_connector(lx_drm_device, [&] (drm_connector *c) {
lx_c_set_mode(lx_drm_device, c, _config._lx.lx_fb,
_preferred_mode(c)); });
}
/* force virtual framebuffer size if requested */
if (int w = _session.force_width_from_config())
_config._lx.width = min(_config._lx.width, w);
if (int h = _session.force_height_from_config())
_config._lx.height = min(_config._lx.height, h);
if (old._lx.addr) Lx::iounmap(old._lx.addr);
/* drm_crtc.h in drm_framebuffer_funcs definition: use drm_fb_remove */
if (old._lx.lx_fb) drm_framebuffer_remove(old._lx.lx_fb);
}
void Framebuffer::Driver::generate_report()
{
Drm_guard guard(lx_drm_device);
/* detect mode information per connector */
{
struct drm_connector *c;
list_for_each_entry(c, &lx_drm_device->mode_config.connector_list,
head)
{
/*
* All states unequal to disconnected are handled as connected,
* since some displays stay in unknown state if not fill_modes()
* is called at least one time.
*/
bool connected = c->status != connector_status_disconnected;
if ((connected && list_empty(&c->modes)) ||
(!connected && !list_empty(&c->modes)))
c->funcs->fill_modes(c, 0, 0);
}
}
/* check for report configuration option */
try {
_reporter.enabled(_session.config().sub_node("report")
.attribute_value(_reporter.name().string(), false));
} catch (...) {
_reporter.enabled(false);
}
if (!_reporter.is_enabled()) return;
/* write new report */
try {
Genode::Reporter::Xml_generator xml(_reporter, [&] ()
{
struct drm_connector *c;
list_for_each_entry(c, &lx_drm_device->mode_config.connector_list,
head) {
xml.node("connector", [&] ()
{
bool connected = c->status == connector_status_connected;
xml.attribute("name", c->name);
xml.attribute("connected", connected);
if (!connected) return;
struct drm_display_mode *mode;
list_for_each_entry(mode, &c->modes, head) {
xml.node("mode", [&] ()
{
xml.attribute("width", mode->hdisplay);
xml.attribute("height", mode->vdisplay);
xml.attribute("hz", mode->vrefresh);
});
}
});
}
});
} catch (...) {
Genode::warning("Failed to generate report");
}
}
extern "C" {
/**********************
** Global variables **
**********************/
struct task_struct *current;
struct boot_cpu_data boot_cpu_data;
int oops_in_progress;
/********************
** linux/string.h **
********************/
char *strncpy(char *dst, const char* src, size_t n)
{
return Genode::strncpy(dst, src, n);
}
int strncmp(const char *cs, const char *ct, size_t count)
{
return Genode::strcmp(cs, ct, count);
}
int memcmp(const void *cs, const void *ct, size_t count)
{
/* original implementation from lib/string.c */
const unsigned char *su1, *su2;
int res = 0;
for (su1 = (unsigned char*)cs, su2 = (unsigned char*)ct;
0 < count; ++su1, ++su2, count--)
if ((res = *su1 - *su2) != 0)
break;
return res;
}
size_t strlen(const char *s)
{
return Genode::strlen(s);
}
long simple_strtol(const char *cp, char **endp, unsigned int base)
{
unsigned long result = 0;
size_t ret = Genode::ascii_to_unsigned(cp, result, base);
if (endp) *endp = (char*)cp + ret;
return result;
}
size_t strlcpy(char *dest, const char *src, size_t size)
{
size_t ret = strlen(src);
if (size) {
size_t len = (ret >= size) ? size - 1 : ret;
memcpy(dest, src, len);
dest[len] = '\0';
}
return ret;
}
int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name)
{
TRACE;
return 0;
}
/*****************
** linux/dmi.h **
*****************/
int dmi_check_system(const struct dmi_system_id *list)
{
TRACE;
/*
* Is used to check for quirks of the platform.
*/
return 0;
}
/*******************
** linux/timer.h **
*******************/
int mod_timer_pinned(struct timer_list *timer, unsigned long expires)
{
return mod_timer(timer, expires);
}
/*******************
** Kernel memory **
*******************/
dma_addr_t page_to_phys(struct page *page)
{
return page->paddr;
}
void *kmem_cache_zalloc(struct kmem_cache *k, gfp_t flags)
{
return kmem_cache_alloc(k, flags | __GFP_ZERO);
}
void *krealloc(const void *p, size_t size, gfp_t flags)
{
/* use const-less version from <impl/slab.h> */
return krealloc(const_cast<void*>(p), size, flags);
}
/*****************
** linux/idr.h **
*****************/
void idr_init(struct idr *idp)
{
Genode::memset(idp, 0, sizeof(struct idr));
}
static Genode::Bit_allocator<1024> id_allocator;
int idr_alloc(struct idr *idp, void *ptr, int start, int end, gfp_t gfp_mask)
{
int max = end > 0 ? end - 1 : ((int)(~0U>>1)); /* inclusive upper limit */
int id;
/* sanity checks */
if (start < 0) return -EINVAL;
if (max < start) return -ENOSPC;
/* allocate id */
id = id_allocator.alloc();
if (id == 0) id = id_allocator.alloc(); /* do not use id zero */
if (id > max) return -ENOSPC;
ASSERT(id >= start);
return id;
}
int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end, gfp_t gfp_mask)
{
int max = end > 0 ? end - 1 : ((int)(~0U>>1));
int id = id_allocator.alloc();
if (id > max) return -ENOSPC;
ASSERT((unsigned int)id >= start);
return id;
}
void ida_remove(struct ida *ida, int id)
{
id_allocator.free(id);
}
void idr_remove(struct idr *idp, int id)
{
id_allocator.free(id);
}
void *idr_find(struct idr *idr, int id)
{
TRACE;
return NULL;
}
void *idr_replace(struct idr *idp, void *ptr, int id)
{
TRACE;
return NULL;
}
/**********************
** asm/cacheflush.h **
**********************/
int set_pages_uc(struct page *page, int numpages)
{
TRACE;
return 0;
}
/********************
** linux/ioport.h **
********************/
struct resource iomem_resource;
/*********
** PCI **
*********/
struct pci_dev *pci_get_bus_and_slot(unsigned int bus, unsigned int devfn)
{
if (bus != 0 || devfn != 0)
TRACE_AND_STOP;
pci_dev *pci_dev = nullptr;
auto lamda = [&] (Platform::Device_capability cap) {
Platform::Device_client client(cap);
/* request bus address of device from platform driver */
unsigned char dev_bus = 0, dev_slot = 0, dev_fn = 0;
client.bus_address(&dev_bus, &dev_slot, &dev_fn);
if (dev_bus == bus && PCI_SLOT(devfn) == dev_slot && PCI_FUNC(devfn) == dev_fn) {
Lx::Pci_dev *dev = new (Lx::Malloc::mem()) Lx::Pci_dev(cap);
Lx::pci_dev_registry()->insert(dev);
pci_dev = dev;
return true;
}
return false;
};
Lx::for_each_pci_device(lamda);
return pci_dev;
}
void pci_dev_put(struct pci_dev *pci_dev)
{
Genode::destroy(Lx::Malloc::mem(), pci_dev);
}
struct pci_dev *pci_get_class(unsigned int class_code, struct pci_dev *from)
{
/*
* The function is solely called by the i915 initialization code to
* probe for the ISA bridge device in order to detect the hardware
* generation.
*
* We look up the bridge but don't need to support the iteration over
* multiple devices of the given class.
*/
if (from)
return nullptr;
pci_dev *pci_dev = nullptr;
auto lamda = [&] (Platform::Device_capability cap) {
Platform::Device_client client(cap);
if (client.class_code() == class_code) {
pci_dev = new (Lx::Malloc::mem()) Lx::Pci_dev(cap);
return true;
}
return false;
};
Lx::for_each_pci_device(lamda);
return pci_dev;
}
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max)
{
return pci_ioremap_bar(dev, bar);
}
struct pci_dev *pci_dev_get(struct pci_dev *dev)
{
TRACE;
return dev;
};
int vga_get_uninterruptible(struct pci_dev *pdev, unsigned int rsrc)
{
/*
* This function locks the VGA device. It is normally provided by the
* VGA arbiter in the Linux kernel. We don't need this arbitration because
* the platform-driver enforces the exclusive access to the VGA resources
* by our driver.
*
* At the time when this function is called, the 'pci_dev' structure for
* the VGA card was already requested. Hence, subsequent I/O accesses
* should work.
*/
TRACE;
return 0;
}
void vga_put(struct pci_dev *pdev, unsigned int rsrc)
{
TRACE;
}
int pci_bus_alloc_resource(struct pci_bus *, struct resource *, resource_size_t,
resource_size_t, resource_size_t, unsigned int,
resource_size_t (*)(void *, const struct resource *,
resource_size_t, resource_size_t),
void *alignf_data)
{
TRACE;
return 0;
}
void pci_set_master(struct pci_dev *dev)
{
TRACE;
}
int pci_enable_msi(struct pci_dev *dev)
{
TRACE;
return 0;
}
dma_addr_t pci_map_page(struct pci_dev *hwdev, struct page *page,
unsigned long offset, size_t size, int direction) {
return page->paddr + offset;
}
int pci_dma_mapping_error(struct pci_dev *pdev, dma_addr_t dma_addr)
{
TRACE;
return 0;
}
struct io_mapping
{
private:
resource_size_t _base;
unsigned long _size;
public:
/**
* Constructor
*/
io_mapping(resource_size_t base, unsigned long size) :
_base(base), _size(size) { }
resource_size_t base() const { return _base; }
};
/**
* I/O mapping used by i915_dma.c to map the GTT aperture
*/
struct io_mapping *io_mapping_create_wc(resource_size_t base, unsigned long size)
{
static int called;
TRACE;
if (called++ != 0) {
Genode::error("io_mapping_create_wc unexpectedly called twice");
return 0;
}
io_mapping *mapping = new (Lx::Malloc::mem()) io_mapping(base, size);
return mapping;
}
void iounmap(volatile void *addr)
{
/* do not unmap here, but when client requests new dataspace */
TRACE;
}
/****************
** linux/io.h **
****************/
int arch_phys_wc_add(unsigned long base, unsigned long size)
{
/*
* Linux tries to manipulate physical memory attributes here (e.g.,
* using MTRRs). But when using PAT, this is not needed. When running
* on top of a microkernel, we cannot manipulate the attributes
* anyway.
*/
TRACE;
return 0;
}
/*******************************
** arch/x86/include/asm/io.h **
*******************************/
void memset_io(void *addr, int val, size_t count)
{
memset((void __force *)addr, val, count);
}
/********************
** linux/device.h **
********************/
struct subsys_private { int dummy; };
int bus_register(struct bus_type *bus)
{
/*
* called by i2c-core init
*
* The subsequent code checks for the 'p' member of the 'bus'. So
* we have to supply a valid pointer there.
*/
static subsys_private priv = { 0 };
bus->p = &priv;
TRACE;
return 0;
}
/**
* Assuming that driver_register is only called for i2c device driver
* registration, we can store its pointer here
*/
static struct device_driver *i2c_device_driver = nullptr;
int driver_register(struct device_driver *drv)
{
TRACE;
ASSERT(!i2c_device_driver);
i2c_device_driver = drv;
return 0;
}
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data,
int (*fn)(struct device *dev, void *data))
{
/*
* Called bu the i2c-core driver after registering the driver. This
* function is called to process drivers that are present at initialization
* time. Since we initialize the i2c driver prior the others, we don't
* need to perform anything.
*/
TRACE;
return 0;
}
int dev_set_name(struct device *dev, const char *name, ...)
{
TRACE;
return 0;
}
int device_register(struct device *dev)
{
TRACE;
return 0;
}
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
void *data, int (*fn)(struct device_driver *, void *))
{
TRACE;
return fn(i2c_device_driver, data);
}
/***********************
** linux/workqueue.h **
***********************/
struct workqueue_struct *system_wq;
struct workqueue_struct *create_singlethread_workqueue(char const *)
{
workqueue_struct *wq = (workqueue_struct *)kzalloc(sizeof(workqueue_struct), 0);
return wq;
}
struct workqueue_struct *alloc_ordered_workqueue(char const *name , unsigned int flags, ...)
{
return create_singlethread_workqueue(name);
}
bool flush_work(struct work_struct *work)
{
cancel_work_sync(work);
return 0;
}
/***************
** Execution **
***************/
bool in_atomic()
{
return false;
}
bool irqs_disabled()
{
return false;
}
void usleep_range(unsigned long min, unsigned long max)
{
udelay(min);
}
unsigned long round_jiffies_up_relative(unsigned long j)
{
j += jiffies;
return j - (j%HZ) + HZ;
}
/************************
** DRM implementation **
************************/
unsigned int drm_debug = 0x0;
extern "C" int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
{
return pci_register_driver(pdriver);
}
void drm_ut_debug_printk(const char *function_name, const char *format, ...)
{
using namespace Genode;
va_list list;
va_start(list, format);
lx_printf("[drm:%s] ", function_name);
lx_vprintf(format, list);
va_end(list);
}
void drm_err(const char *format, ...)
{
using namespace Genode;
va_list list;
va_start(list, format);
lx_printf("[drm:ERROR] ");
lx_vprintf(format, list);
va_end(list);
}
struct drm_device *drm_dev_alloc(struct drm_driver *driver, struct device *parent)
{
struct drm_device *dev = (drm_device *)kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return nullptr;
dev->dev = parent;
dev->driver = driver;
INIT_LIST_HEAD(&dev->filelist);
INIT_LIST_HEAD(&dev->ctxlist);
INIT_LIST_HEAD(&dev->vmalist);
INIT_LIST_HEAD(&dev->maplist);
INIT_LIST_HEAD(&dev->vblank_event_list);
spin_lock_init(&dev->buf_lock);
spin_lock_init(&dev->event_lock);
mutex_init(&dev->ctxlist_mutex);
mutex_init(&dev->master_mutex);
mutex_init(&dev->struct_mutex);
return dev;
}
static void drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type)
{
struct drm_minor *new_minor = (struct drm_minor*)
kzalloc(sizeof(struct drm_minor), GFP_KERNEL);
ASSERT(new_minor);
new_minor->type = type;
new_minor->dev = dev;
*minor = new_minor;
}
int drm_dev_register(struct drm_device *dev, unsigned long flags)
{
drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY);
ASSERT(!lx_drm_device);
lx_drm_device = dev;
return dev->driver->load(dev, flags);
}
/*
* Called indirectly when 'pci_register_driver' has found a matching
* device.
*/
int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
struct drm_driver *driver)
{
drm_device *dev = drm_dev_alloc(driver, &pdev->dev);
if (!dev)
return -ENOMEM;
dev->pdev = pdev;
pci_set_drvdata(pdev, dev);
/*
* Kick off the actual driver initialization code
*
* In the Linux DRM code, this happens indirectly via the call of
* 'drm_dev_register'.
*/
drm_dev_register(dev, ent->driver_data);
DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n",
driver->name, driver->major, driver->minor, driver->patchlevel,
driver->date, pci_name(pdev), dev->primary->index);
return 0;
}
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
struct drm_device * drm_dev = (struct drm_device*) dev;
Lx::Pci_dev * pci_dev = (Lx::Pci_dev*) drm_dev->pdev->bus;
Lx::Irq::irq().request_irq(pci_dev->client(), handler, dev);
return 0;
}
void drm_gem_private_object_init(struct drm_device *dev,
struct drm_gem_object *obj, size_t size)
{
obj->dev = dev;
kref_init(&obj->refcount);
obj->filp = NULL;
obj->size = size;
}
void drm_gem_object_free(struct kref *kref)
{
struct drm_gem_object *obj =
container_of(kref, struct drm_gem_object, refcount);
struct drm_device *dev = obj->dev;
if (dev->driver->gem_free_object != NULL)
dev->driver->gem_free_object(obj);
}
void drm_gem_free_mmap_offset(struct drm_gem_object *obj)
{
if (obj && obj->filp && obj->filp->f_inode && obj->filp->f_inode->i_mapping)
free_pages((unsigned long)obj->filp->f_inode->i_mapping->my_page->addr, 0);
}
void drm_gem_object_release(struct drm_gem_object *obj)
{
if (!obj || !obj->filp || !obj->filp->f_inode
|| !obj->filp->f_inode->i_mapping) return;
kfree(obj->filp->f_inode->i_mapping);
kfree(obj->filp->f_inode);
kfree(obj->filp);
}
/***************************
** arch/x86/kernel/tsc.c **
***************************/
unsigned int tsc_khz;
/**************************************
** arch/x86/include/asm/processor.h **
**************************************/
void cpu_relax(void)
{
Lx::timer_update_jiffies();
asm volatile("rep; nop" ::: "memory");
}
/***********************
** linux/workqueue.h **
***********************/
bool mod_delayed_work(struct workqueue_struct *, struct delayed_work *, unsigned long)
{
TRACE;
return false;
}
bool flush_delayed_work(struct delayed_work *dwork)
{
TRACE;
return false;
}
async_cookie_t async_schedule(async_func_t func, void *data)
{
TRACE;
return 0;
}
void async_synchronize_full(void)
{
TRACE;
}
/***********************
** drivers/pci/rom.c **
***********************/
void __iomem __must_check *pci_map_rom(struct pci_dev *pdev, size_t *size)
{
enum { VIDEO_ROM_BASE = 0xC0000, VIDEO_ROM_SIZE = 0x20000 };
static Genode::Attached_io_mem_dataspace vrom(Lx_kit::env().env(),
VIDEO_ROM_BASE, VIDEO_ROM_SIZE);
*size = VIDEO_ROM_SIZE;
return vrom.local_addr<void*>();
}
void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom) {}
/***********************
** lib/scatterlist.c **
***********************/
void sg_mark_end(struct scatterlist *sg)
{
sg->page_link |= 0x02;
sg->page_link &= ~0x01;
}
int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
{
enum { MAX_ENTS = 4096 / sizeof(struct scatterlist) };
Genode::memset(table, 0, sizeof(*table));
struct scatterlist *sg = (scatterlist*)
Lx::Malloc::mem().alloc_large(nents * sizeof(struct scatterlist));
if (!sg) return -ENOMEM;
Genode::memset(sg, 0, sizeof(*sg) * nents);
table->nents = nents;
table->sgl = sg;
sg_mark_end(&sg[nents - 1]);
return 0;
}
void sg_free_table(struct sg_table *table)
{
TRACE;
if (table && table->sgl) Lx::Malloc::mem().free_large(table->sgl);
}
static inline bool sg_is_last(scatterlist * sg) {
return (sg->page_link & 0x02); }
struct scatterlist *sg_next(struct scatterlist * sg)
{
if (sg_is_last(sg))
return NULL;
sg++;
if (unlikely(sg_is_chain(sg)))
sg = sg_chain_ptr(sg);
return sg;
}
void __sg_page_iter_start(struct sg_page_iter *piter, struct scatterlist *sglist,
unsigned int nents, unsigned long pgoffset)
{
piter->__pg_advance = 0;
piter->__nents = nents;
piter->sg = sglist;
piter->sg_pgoffset = pgoffset;
}
static int sg_page_count(struct scatterlist *sg) {
return Genode::align_addr(sg->offset + sg->length, 12) >> PAGE_SHIFT; }
bool __sg_page_iter_next(struct sg_page_iter *piter)
{
if (!piter->__nents || !piter->sg)
return false;
piter->sg_pgoffset += piter->__pg_advance;
piter->__pg_advance = 1;
while (piter->sg_pgoffset >= (unsigned long)sg_page_count(piter->sg)) {
piter->sg_pgoffset -= sg_page_count(piter->sg);
piter->sg = sg_next(piter->sg);
if (!--piter->__nents || !piter->sg)
return false;
}
return true;
}
dma_addr_t sg_page_iter_dma_address(struct sg_page_iter *piter)
{
return sg_dma_address(piter->sg) + (piter->sg_pgoffset << PAGE_SHIFT);
}
struct page *sg_page_iter_page(struct sg_page_iter *piter)
{
return (page*)(PAGE_SIZE * (page_to_pfn((sg_page(piter->sg))) + (piter->sg_pgoffset)));
}
void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
{
TRACE;
}
void mark_page_accessed(struct page *p)
{
//TRACE;
}
void put_page(struct page *page)
{
//TRACE;
}
unsigned long invalidate_mapping_pages(struct address_space *mapping, pgoff_t start, pgoff_t end)
{
TRACE;
return 0;
}
/******************
** linux/kref.h **
******************/
void kref_init(struct kref *kref) {
kref->refcount.counter = 1; }
void kref_get(struct kref *kref) {
kref->refcount.counter++; }
int kref_put(struct kref *kref, void (*release) (struct kref *kref))
{
kref->refcount.counter--;
if (kref->refcount.counter == 0) {
release(kref);
return 1;
}
return 0;
}
int kref_put_mutex(struct kref *kref, void (*release)(struct kref *kref), struct mutex *lock)
{
if (kref_put(kref, release)) {
mutex_lock(lock);
return 1;
}
return 0;
}
void *kmalloc_array(size_t n, size_t size, gfp_t flags)
{
if (size != 0 && n > SIZE_MAX / size) return NULL;
return kmalloc(n * size, flags);
}
/**************************************
** Stubs for non-ported driver code **
**************************************/
void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, s32 value)
{
TRACE;
}
void pm_qos_update_request(struct pm_qos_request *req, s32 new_value)
{
}
int vga_client_register(struct pci_dev *pdev, void *cookie,
void (*irq_set_state)(void *cookie, bool state),
unsigned int (*set_vga_decode)(void *cookie, bool state))
{
TRACE;
return -ENODEV;
}
int vga_switcheroo_register_client(struct pci_dev *dev,
const struct vga_switcheroo_client_ops *ops,
bool driver_power_control)
{
TRACE;
return 0;
}
struct resource * devm_request_mem_region(struct device *dev,
resource_size_t start,
resource_size_t n,
const char *name)
{
/*
* This function solely called for keeping the stolen memory preserved
* for the driver only ('i915_stolen_to_physical'). The returned pointer is
* just checked for NULL but not used otherwise.
*/
TRACE;
static struct resource dummy;
return &dummy;
}
int acpi_lid_notifier_register(struct notifier_block *nb)
{
TRACE;
return 0;
}
int drm_sysfs_connector_add(struct drm_connector *connector)
{
TRACE;
connector->kdev = (struct device*)
kmalloc(sizeof(struct device), GFP_KERNEL);
DRM_DEBUG("adding \"%s\" to sysfs\n", connector->name);
drm_sysfs_hotplug_event(NULL);
return 0;
}
void drm_sysfs_connector_remove(struct drm_connector *connector)
{
TRACE;
kfree(connector->kdev);
}
void assert_spin_locked(spinlock_t *lock)
{
}
void spin_lock_irq(spinlock_t *lock)
{
}
void spin_unlock_irq(spinlock_t *lock)
{
}
int fb_get_options(const char *name, char **option)
{
return 0;
}
void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info)
{
TRACE;
}
int register_sysrq_key(int key, struct sysrq_key_op *op)
{
TRACE;
return 0;
}
void trace_intel_gpu_freq_change(int)
{
TRACE;
}
struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
{
TRACE;
return NULL;
}
int unregister_sysrq_key(int key, struct sysrq_key_op *op)
{
TRACE;
return 0;
}
int of_alias_get_highest_id(const char *stem)
{
TRACE;
return 0;
}
void down_write(struct rw_semaphore *sem)
{
TRACE;
}
void up_write(struct rw_semaphore *sem)
{
TRACE;
}
void intel_csr_ucode_init(struct drm_device *dev)
{
TRACE;
}
void intel_guc_ucode_init(struct drm_device *dev)
{
TRACE;
}
void i915_gem_shrinker_init(struct drm_i915_private *dev_priv)
{
TRACE;
}
void intel_init_audio(struct drm_device *dev)
{
TRACE;
}
bool static_key_false(struct static_key *key)
{
TRACE;
return false;
}
int i915_gem_init_userptr(struct drm_device *dev)
{
TRACE;
return 0;
}
void i915_gem_batch_pool_init(struct drm_device *dev, struct i915_gem_batch_pool *pool)
{
TRACE;
}
void spin_lock(spinlock_t *lock)
{
}
#define __GFP_BITS_SHIFT 26
#define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
int drm_gem_object_init(struct drm_device *dev, struct drm_gem_object *obj, size_t size)
{
drm_gem_private_object_init(dev, obj, size);
struct file * filp = (struct file*) kmalloc(sizeof(struct file), GFP_KERNEL);
struct inode * inode = (struct inode*) kmalloc(sizeof(struct inode*), GFP_KERNEL);
struct address_space * mapping = (struct address_space*)
kmalloc(sizeof(struct address_space*), GFP_KERNEL);
inode->i_mapping = mapping;
filp->f_inode = inode;
obj->filp = filp;
unsigned long npages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
size_t sz_log2 = Genode::log2(npages);
sz_log2 += (npages > (1UL << sz_log2)) ? 1 : 0;
struct page *pages = alloc_pages(0, sz_log2);
mapping->my_page = pages;
size = PAGE_SIZE * npages;
void * data = page_address(pages);
memset(data, 0, size);
return 0;
}
struct inode *file_inode(struct file *f)
{
return f->f_inode;
}
void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)
{
TRACE;
}
gfp_t mapping_gfp_constraint(struct address_space *mapping, gfp_t gfp_mask)
{
TRACE;
return 0;
}
struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
pgoff_t index, gfp_t gfp_mask)
{
return mapping->my_page;
}
void sg_set_page(struct scatterlist *sg, struct page *page,
unsigned int len, unsigned int offset)
{
unsigned long page_link = sg->page_link & 0x3;
sg->page_link = page_link | (unsigned long) page;
sg->offset = offset;
sg->length = len;
}
dma_addr_t page_to_pfn(struct page *page)
{
return page->paddr / PAGE_SIZE;
}
struct page *sg_page(struct scatterlist *sg)
{
return (struct page *)((sg)->page_link & ~0x3);
}
void *sg_virt(struct scatterlist *sg)
{
return (void*)((size_t)page_address(sg_page(sg)) + sg->offset);
}
int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir, struct dma_attrs *attrs)
{
int i;
struct scatterlist *s;
Genode::addr_t base = page_to_phys(sg_page(sg));
Genode::size_t offs = 0;
for_each_sg(sg, s, nents, i) {
s->dma_address = base + offs;
offs += s->length;
}
return nents;
}
dma_addr_t dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size,
enum dma_data_direction direction)
{
return page_to_phys(page) + offset;
}
int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
return 0;
}
int on_each_cpu(void (*func) (void *info), void *info, int wait)
{
func(info);
return 0;
}
void wbinvd()
{
TRACE;
}
int i915_cmd_parser_init_ring(struct intel_engine_cs *ring)
{
TRACE;
return 0;
}
u64 ktime_get_raw_ns(void)
{
return ktime_get().tv64;
}
ktime_t ktime_add_ns(const ktime_t kt, u64 nsec)
{
ktime_t ktime;
ktime.tv64 = kt.tv64 + nsec;
return ktime;
}
s64 ktime_us_delta(const ktime_t later, const ktime_t earlier)
{
return ktime_to_us(ktime_sub(later, earlier));
}
void i915_setup_sysfs(struct drm_device *dev_priv)
{
TRACE;
}
int acpi_video_register(void)
{
TRACE;
return 0;
}
void i915_audio_component_init(struct drm_i915_private *dev_priv)
{
TRACE;
}
void ww_mutex_init(struct ww_mutex *lock, struct ww_class *ww_class)
{
lock->ctx = NULL;
lock->locked = false;
}
void ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_class *ww_class)
{
TRACE;
}
int ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
{
if (ctx && (lock->ctx == ctx))
return -EALREADY;
lock->ctx = ctx;
lock->locked = true;
return 0;
}
void ww_mutex_unlock(struct ww_mutex *lock)
{
lock->ctx = NULL;
lock->locked = false;
}
bool ww_mutex_is_locked(struct ww_mutex *lock)
{
return lock->locked;
}
void ww_acquire_fini(struct ww_acquire_ctx *ctx)
{
TRACE;
}
void local_irq_disable()
{
TRACE;
}
void local_irq_enable()
{
TRACE;
}
void drm_sysfs_hotplug_event(struct drm_device *dev)
{
Framebuffer::Driver * driver = (Framebuffer::Driver*)
lx_c_get_driver(lx_drm_device);
if (driver) {
DRM_DEBUG("generating hotplug event\n");
driver->generate_report();
}
}
void intel_audio_codec_enable(struct intel_encoder *encoder)
{
TRACE;
}
static void clflush(uint8_t * page)
{
unsigned int i;
const int size = 64;
ASSERT(sizeof(unsigned long) == 8);
// FIXME clflush with other opcode see X86_FEATURE_CLFLUSHOPT
for (i = 0; i < PAGE_SIZE; i += size)
asm volatile(".byte 0x3e; clflush %P0"
: "+m" (*(volatile char __force *)(page+i)));
}
void drm_clflush_sg(struct sg_table *st)
{
unsigned int i;
struct scatterlist *s;
Genode::addr_t base = (Genode::addr_t) sg_virt(st->sgl);
Genode::size_t offs = 0;
asm volatile("mfence":::"memory");
for_each_sg(st->sgl, s, st->nents, i) {
clflush((uint8_t*)(base + offs));
offs += s->length;
}
asm volatile("mfence":::"memory");
}
void intel_audio_codec_disable(struct intel_encoder *encoder)
{
TRACE;
}
struct backlight_device *backlight_device_register(const char *name,
struct device *dev, void *devdata, const struct backlight_ops *ops,
const struct backlight_properties *props)
{
struct backlight_device *new_bd;
new_bd = (backlight_device*) kzalloc(sizeof(struct backlight_device), GFP_KERNEL);
return new_bd;
}
void synchronize_irq(unsigned int irq)
{
TRACE;
}
void bitmap_zero(unsigned long *dst, unsigned int nbits)
{
unsigned int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
Genode::memset(dst, 0, len);
}
int bitmap_empty(const unsigned long *src, unsigned nbits)
{
return find_first_bit(src, nbits) == nbits;
}
#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1)))
#define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1)))
unsigned long find_next_bit(const unsigned long *addr, unsigned long nbits,
unsigned long start)
{
unsigned long tmp;
if (!nbits || start >= nbits)
return nbits;
tmp = addr[start / BITS_PER_LONG] ^ 0UL;
/* Handle 1st word. */
tmp &= BITMAP_FIRST_WORD_MASK(start);
start = round_down(start, BITS_PER_LONG);
while (!tmp) {
start += BITS_PER_LONG;
if (start >= nbits)
return nbits;
tmp = addr[start / BITS_PER_LONG] ^ 0UL;
}
return min(start + __ffs(tmp), nbits);
}
void bitmap_set(unsigned long *map, unsigned int start, int len)
{
unsigned long *p = map + BIT_WORD(start);
const unsigned int size = start + len;
int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
while (len - bits_to_set >= 0) {
*p |= mask_to_set;
len -= bits_to_set;
bits_to_set = BITS_PER_LONG;
mask_to_set = ~0UL;
p++;
}
if (len) {
mask_to_set &= BITMAP_LAST_WORD_MASK(size);
*p |= mask_to_set;
}
}
void bitmap_or(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, unsigned int nbits)
{
unsigned int k;
unsigned int nr = BITS_TO_LONGS(nbits);
for (k = 0; k < nr; k++)
dst[k] = src1[k] | src2[k];
}
int i915_gem_render_state_init(struct drm_i915_gem_request *req)
{
TRACE;
return 0;
}
int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id)
{
TRACE;
return 0;
}
signed long schedule_timeout_uninterruptible(signed long timeout)
{
return schedule_timeout(timeout);
}
int intel_logical_rings_init(struct drm_device *dev)
{
TRACE;
return 0;
}
int intel_guc_ucode_load(struct drm_device *dev)
{
TRACE;
return 0;
}
void intel_lrc_irq_handler(struct intel_engine_cs *ring)
{
TRACE;
}
} /* extern "C" */