NetBSD/sys/arch/macppc/dev/ofb.c
chs e07f0b9362 eliminate uvm_useracc() in favor of checking the return value of
copyin() or copyout().

uvm_useracc() tells us whether the mapping permissions allow access to
the desired part of an address space, and many callers assume that
this is the same as knowing whether an attempt to access that part of
the address space will succeed.  however, access to user space can
fail for reasons other than insufficient permission, most notably that
paging in any non-resident data can fail due to i/o errors.  most of
the callers of uvm_useracc() make the above incorrect assumption.  the
rest are all misguided optimizations, which optimize for the case
where an operation will fail.  we'd rather optimize for operations
succeeding, in which case we should just attempt the access and handle
failures due to insufficient permissions the same way we handle i/o
errors.  since there appear to be no good uses of uvm_useracc(), we'll
just remove it.
2003-11-13 03:09:28 +00:00

596 lines
14 KiB
C

/* $NetBSD: ofb.c,v 1.38 2003/11/13 03:09:28 chs Exp $ */
/*
* Copyright (c) 1995, 1996 Carnegie-Mellon University.
* All rights reserved.
*
* Author: Chris G. Demetriou
*
* Permission to use, copy, modify and distribute this software and
* its documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ofb.c,v 1.38 2003/11/13 03:09:28 chs Exp $");
#include <sys/param.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <uvm/uvm_extern.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pciio.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/rasops/rasops.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_pci.h>
#include <machine/bus.h>
#include <machine/autoconf.h>
#include <machine/grfioctl.h>
#include <powerpc/oea/bat.h>
#include <macppc/dev/ofbvar.h>
#if OFB_ENABLE_CACHE
int ofb_enable_cache = 1;
#else
int ofb_enable_cache = 0;
#endif
int ofbmatch __P((struct device *, struct cfdata *, void *));
void ofbattach __P((struct device *, struct device *, void *));
int ofbprint __P((void *, const char *));
CFATTACH_DECL(ofb, sizeof(struct ofb_softc),
ofbmatch, ofbattach, NULL, NULL);
struct ofb_devconfig ofb_console_dc;
struct wsscreen_descr ofb_stdscreen = {
"std",
0, 0, /* will be filled in -- XXX shouldn't, it's global */
0,
0, 0,
WSSCREEN_REVERSE
};
const struct wsscreen_descr *_ofb_scrlist[] = {
&ofb_stdscreen,
/* XXX other formats, graphics screen? */
};
struct wsscreen_list ofb_screenlist = {
sizeof(_ofb_scrlist) / sizeof(struct wsscreen_descr *), _ofb_scrlist
};
static int ofb_ioctl __P((void *, u_long, caddr_t, int, struct proc *));
static paddr_t ofb_mmap __P((void *, off_t, int));
static int ofb_alloc_screen __P((void *, const struct wsscreen_descr *,
void **, int *, int *, long *));
static void ofb_free_screen __P((void *, void *));
static int ofb_show_screen __P((void *, void *, int,
void (*) (void *, int, int), void *));
static int copy_rom_font __P((void));
struct wsdisplay_accessops ofb_accessops = {
ofb_ioctl,
ofb_mmap,
ofb_alloc_screen,
ofb_free_screen,
ofb_show_screen,
0 /* load_font */
};
static struct wsdisplay_font openfirm6x11;
static void ofb_common_init __P((int, struct ofb_devconfig *));
static int ofb_getcmap __P((struct ofb_softc *, struct wsdisplay_cmap *));
static int ofb_putcmap __P((struct ofb_softc *, struct wsdisplay_cmap *));
int
ofbmatch(parent, match, aux)
struct device *parent;
struct cfdata *match;
void *aux;
{
struct pci_attach_args *pa = aux;
if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_APPLE &&
PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_APPLE_CONTROL)
return 1;
if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY)
return 1;
return 0;
}
void
ofbattach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct ofb_softc *sc = (struct ofb_softc *)self;
struct pci_attach_args *pa = aux;
struct wsemuldisplaydev_attach_args a;
int console, node;
struct ofb_devconfig *dc;
char devinfo[256];
console = ofb_is_console();
if (console) {
dc = &ofb_console_dc;
node = dc->dc_node;
sc->nscreens = 1;
} else {
int i, len, screenbytes;
dc = malloc(sizeof(struct ofb_devconfig), M_DEVBUF, M_WAITOK);
memset(dc, 0, sizeof(struct ofb_devconfig));
node = pcidev_to_ofdev(pa->pa_pc, pa->pa_tag);
if (node == 0) {
printf(": ofdev not found\n");
return;
}
/* XXX There are two child screens on PowerBook. */
memset(devinfo, 0, sizeof(devinfo));
OF_getprop(node, "device_type", devinfo, sizeof(devinfo));
len = strlen(devinfo);
if (strcmp(devinfo + len - 7, "-parent") == 0)
node = OF_child(node);
ofb_common_init(node, dc);
screenbytes = dc->dc_ri.ri_stride * dc->dc_ri.ri_height;
for (i = 0; i < screenbytes; i += sizeof(u_int32_t))
*(u_int32_t *)(dc->dc_paddr + i) = 0xffffffff;
}
sc->sc_dc = dc;
sc->sc_pc = pa->pa_pc;
sc->sc_pcitag = pa->pa_tag;
/* XXX */
if (OF_getprop(node, "assigned-addresses", sc->sc_addrs,
sizeof(sc->sc_addrs)) == -1) {
node = OF_parent(node);
OF_getprop(node, "assigned-addresses", sc->sc_addrs,
sizeof(sc->sc_addrs));
}
if (dc->dc_paddr == 0) {
printf(": cannot map framebuffer\n");
return;
}
pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo);
printf(": %s\n", devinfo);
printf("%s: %d x %d, %dbpp\n", self->dv_xname,
dc->dc_ri.ri_width, dc->dc_ri.ri_height, dc->dc_ri.ri_depth);
sc->sc_cmap_red[0] = sc->sc_cmap_green[0] = sc->sc_cmap_blue[0] = 0;
sc->sc_cmap_red[15] = sc->sc_cmap_red[255] = 0xff;
sc->sc_cmap_green[15] = sc->sc_cmap_green[255] = 0xff;
sc->sc_cmap_blue[15] = sc->sc_cmap_blue[255] = 0xff;
a.console = console;
a.scrdata = &ofb_screenlist;
a.accessops = &ofb_accessops;
a.accesscookie = sc;
config_found(self, &a, wsemuldisplaydevprint);
}
void
ofb_common_init(node, dc)
int node;
struct ofb_devconfig *dc;
{
struct rasops_info *ri = &dc->dc_ri;
int32_t addr, width, height, linebytes, depth;
dc->dc_node = node;
if (dc->dc_ih == 0) {
char name[64];
memset(name, 0, 64);
OF_package_to_path(node, name, sizeof(name));
dc->dc_ih = OF_open(name);
}
/* XXX /chaos/control doesn't have "width", "height", ... */
width = height = -1;
if (OF_getprop(node, "width", &width, 4) != 4)
OF_interpret("screen-width", 1, &width);
if (OF_getprop(node, "height", &height, 4) != 4)
OF_interpret("screen-height", 1, &height);
if (OF_getprop(node, "linebytes", &linebytes, 4) != 4)
linebytes = width; /* XXX */
if (OF_getprop(node, "depth", &depth, 4) != 4)
depth = 8; /* XXX */
if (OF_getprop(node, "address", &addr, 4) != 4)
OF_interpret("frame-buffer-adr", 1, &addr);
if (width == -1 || height == -1 || addr == 0 || addr == -1)
return;
dc->dc_paddr = addr; /* PA of the frame buffer */
/* Make sure 0/0/0 is black and 255/255/255 is white. */
OF_call_method_1("color!", dc->dc_ih, 4, 0, 0, 0, 0);
OF_call_method_1("color!", dc->dc_ih, 4, 255, 255, 255, 255);
/* Enable write-through cache. */
if (ofb_enable_cache) {
vaddr_t va;
/*
* Let's try to find an empty BAT to use
*/
for (va = SEGMENT_LENGTH; va < (USER_SR << ADDR_SR_SHFT);
va += SEGMENT_LENGTH) {
if (battable[va >> ADDR_SR_SHFT].batu == 0) {
battable[va >> ADDR_SR_SHFT].batl =
BATL(addr & 0xf0000000,
BAT_G | BAT_W | BAT_M, BAT_PP_RW);
battable[va >> ADDR_SR_SHFT].batu =
BATL(va, BAT_BL_256M, BAT_Vs);
addr &= 0x0fffffff;
addr |= va;
break;
}
}
}
/* initialize rasops */
ri->ri_width = width;
ri->ri_height = height;
ri->ri_depth = depth;
ri->ri_stride = linebytes;
ri->ri_bits = (char *)addr;
ri->ri_flg = RI_FORCEMONO | RI_FULLCLEAR | RI_CENTER;
/* If screen is smaller than 1024x768, use small font. */
if ((width < 1024 || height < 768) && copy_rom_font() == 0) {
int cols, rows;
OF_interpret("#lines", 1, &rows);
OF_interpret("#columns", 1, &cols);
ri->ri_font = &openfirm6x11;
ri->ri_wsfcookie = -1; /* not using wsfont */
rasops_init(ri, rows, cols);
ri->ri_xorigin = 2;
ri->ri_yorigin = 3;
ri->ri_bits = (char *)addr + ri->ri_xorigin +
ri->ri_stride * ri->ri_yorigin;
} else {
rasops_init(ri, 24, 80);
rasops_reconfig(ri, (height - 2) / ri->ri_font->fontheight,
((width - 2) / ri->ri_font->fontwidth) & ~7);
}
/* black on white */
ri->ri_devcmap[0] = 0xffffffff; /* bg */
ri->ri_devcmap[1] = 0; /* fg */
ofb_stdscreen.nrows = ri->ri_rows;
ofb_stdscreen.ncols = ri->ri_cols;
ofb_stdscreen.textops = &ri->ri_ops;
ofb_stdscreen.capabilities = ri->ri_caps;
}
int
ofb_is_console()
{
int chosen, stdout, node;
char type[16];
chosen = OF_finddevice("/chosen");
OF_getprop(chosen, "stdout", &stdout, 4);
node = OF_instance_to_package(stdout);
OF_getprop(node, "device_type", type, sizeof(type));
if (strcmp(type, "display") == 0)
return 1;
else
return 0;
}
int
ofb_ioctl(v, cmd, data, flag, p)
void *v;
u_long cmd;
caddr_t data;
int flag;
struct proc *p;
{
struct ofb_softc *sc = v;
struct ofb_devconfig *dc = sc->sc_dc;
struct wsdisplay_fbinfo *wdf;
struct grfinfo *gm;
switch (cmd) {
case WSDISPLAYIO_GTYPE:
*(u_int *)data = WSDISPLAY_TYPE_PCIMISC; /* XXX ? */
return 0;
case WSDISPLAYIO_GINFO:
wdf = (void *)data;
wdf->height = dc->dc_ri.ri_height;
wdf->width = dc->dc_ri.ri_width;
wdf->depth = dc->dc_ri.ri_depth;
wdf->cmsize = 256;
return 0;
case WSDISPLAYIO_GETCMAP:
return ofb_getcmap(sc, (struct wsdisplay_cmap *)data);
case WSDISPLAYIO_PUTCMAP:
return ofb_putcmap(sc, (struct wsdisplay_cmap *)data);
/* XXX There are no way to know framebuffer pa from a user program. */
case GRFIOCGINFO:
gm = (void *)data;
memset(gm, 0, sizeof(struct grfinfo));
gm->gd_fbaddr = (caddr_t)dc->dc_paddr;
gm->gd_fbrowbytes = dc->dc_ri.ri_stride;
return 0;
/* PCI config read/write passthrough. */
case PCI_IOC_CFGREAD:
case PCI_IOC_CFGWRITE:
return (pci_devioctl(sc->sc_pc, sc->sc_pcitag,
cmd, data, flag, p));
}
return EPASSTHROUGH;
}
paddr_t
ofb_mmap(v, offset, prot)
void *v;
off_t offset;
int prot;
{
struct ofb_softc *sc = v;
struct ofb_devconfig *dc = sc->sc_dc;
struct rasops_info *ri = &dc->dc_ri;
u_int32_t *ap = sc->sc_addrs;
paddr_t pa;
int i;
if (offset >=0 && offset < (ri->ri_stride * ri->ri_height))
return dc->dc_paddr + offset;
pa = offset;
for (i = 0; i < 6; i++) {
switch (ap[0] & OFW_PCI_PHYS_HI_SPACEMASK) {
case OFW_PCI_PHYS_HI_SPACE_MEM32:
if (pa >= ap[2] && pa < ap[2] + ap[4])
return pa;
/* XXX I/O space? */
}
ap += 5;
}
return -1;
}
int
ofb_alloc_screen(v, type, cookiep, curxp, curyp, attrp)
void *v;
const struct wsscreen_descr *type;
void **cookiep;
int *curxp, *curyp;
long *attrp;
{
struct ofb_softc *sc = v;
struct rasops_info *ri = &sc->sc_dc->dc_ri;
long defattr;
if (sc->nscreens > 0)
return (ENOMEM);
*cookiep = ri; /* one and only for now */
*curxp = 0;
*curyp = 0;
(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
*attrp = defattr;
sc->nscreens++;
return 0;
}
void
ofb_free_screen(v, cookie)
void *v;
void *cookie;
{
struct ofb_softc *sc = v;
if (sc->sc_dc == &ofb_console_dc)
panic("ofb_free_screen: console");
sc->nscreens--;
}
int
ofb_show_screen(v, cookie, waitok, cb, cbarg)
void *v;
void *cookie;
int waitok;
void (*cb) __P((void *, int, int));
void *cbarg;
{
return (0);
}
int
ofb_cnattach()
{
struct ofb_devconfig *dc = &ofb_console_dc;
struct rasops_info *ri = &dc->dc_ri;
long defattr;
int crow = 0;
int chosen, stdout, node;
chosen = OF_finddevice("/chosen");
OF_getprop(chosen, "stdout", &stdout, sizeof(stdout));
node = OF_instance_to_package(stdout);
dc->dc_ih = stdout;
/* get current cursor position */
OF_interpret("line#", 1, &crow);
/* move (rom monitor) cursor to the lowest line - 1 */
OF_interpret("#lines 2 - to line#", 0);
ofb_common_init(node, dc);
if (ri->ri_width >= 1024 && ri->ri_height >= 768) {
int i, screenbytes = ri->ri_stride * ri->ri_height;
for (i = 0; i < screenbytes; i += sizeof(u_int32_t))
*(u_int32_t *)(dc->dc_paddr + i) = 0xffffffff;
crow = 0;
}
(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
wsdisplay_cnattach(&ofb_stdscreen, ri, 0, crow, defattr);
return 0;
}
int
copy_rom_font()
{
u_char *romfont;
int char_width, char_height;
int chosen, mmu, m, e;
/* Get ROM FONT address. */
OF_interpret("font-adr", 1, &romfont);
if (romfont == NULL)
return -1;
chosen = OF_finddevice("/chosen");
OF_getprop(chosen, "mmu", &mmu, 4);
/*
* Convert to physcal address. We cannot access to Open Firmware's
* virtual address space.
*/
OF_call_method("translate", mmu, 1, 3, romfont, &romfont, &m, &e);
/* Get character size */
OF_interpret("char-width", 1, &char_width);
OF_interpret("char-height", 1, &char_height);
openfirm6x11.name = "Open Firmware";
openfirm6x11.firstchar = 32;
openfirm6x11.numchars = 96;
openfirm6x11.encoding = WSDISPLAY_FONTENC_ISO;
openfirm6x11.fontwidth = char_width;
openfirm6x11.fontheight = char_height;
openfirm6x11.stride = 1;
openfirm6x11.bitorder = WSDISPLAY_FONTORDER_L2R;
openfirm6x11.byteorder = WSDISPLAY_FONTORDER_L2R;
openfirm6x11.data = romfont;
return 0;
}
int
ofb_getcmap(sc, cm)
struct ofb_softc *sc;
struct wsdisplay_cmap *cm;
{
u_int index = cm->index;
u_int count = cm->count;
int error;
if (index >= 256 || count > 256 || index + count > 256)
return EINVAL;
error = copyout(&sc->sc_cmap_red[index], cm->red, count);
if (error)
return error;
error = copyout(&sc->sc_cmap_green[index], cm->green, count);
if (error)
return error;
error = copyout(&sc->sc_cmap_blue[index], cm->blue, count);
if (error)
return error;
return 0;
}
int
ofb_putcmap(sc, cm)
struct ofb_softc *sc;
struct wsdisplay_cmap *cm;
{
struct ofb_devconfig *dc = sc->sc_dc;
u_int index = cm->index;
u_int count = cm->count;
int i, error;
u_char rbuf[256], gbuf[256], bbuf[256];
u_char *r, *g, *b;
if (cm->index >= 256 || cm->count > 256 ||
(cm->index + cm->count) > 256)
return EINVAL;
error = copyin(cm->red, &rbuf[index], count);
if (error)
return error;
error = copyin(cm->green, &gbuf[index], count);
if (error)
return error;
error = copyin(cm->blue, &bbuf[index], count);
if (error)
return error;
memcpy(&sc->sc_cmap_red[index], &rbuf[index], count);
memcpy(&sc->sc_cmap_green[index], &gbuf[index], count);
memcpy(&sc->sc_cmap_blue[index], &bbuf[index], count);
r = &sc->sc_cmap_red[index];
g = &sc->sc_cmap_green[index];
b = &sc->sc_cmap_blue[index];
for (i = 0; i < count; i++) {
OF_call_method_1("color!", dc->dc_ih, 4, *r, *g, *b, index);
r++, g++, b++, index++;
}
return 0;
}