diff --git a/sys/amd64/conf/NOTES b/sys/amd64/conf/NOTES --- a/sys/amd64/conf/NOTES +++ b/sys/amd64/conf/NOTES @@ -502,6 +502,7 @@ # Microsoft Hyper-V enhancement support device hyperv # HyperV drivers device hvhid # HyperV HID device +device hvfb # HyperV framebuffer # Xen HVM Guest Optimizations options XENHVM # Xen HVM kernel infrastructure diff --git a/sys/conf/files.x86 b/sys/conf/files.x86 --- a/sys/conf/files.x86 +++ b/sys/conf/files.x86 @@ -115,6 +115,7 @@ dev/hwpmc/hwpmc_tsc.c optional hwpmc dev/hwpmc/hwpmc_x86.c optional hwpmc dev/hyperv/hvsock/hv_sock.c optional hyperv +dev/hyperv/hvfb/hv_fb.c optional hyperv hvfb dev/hyperv/input/hv_hid.c optional hyperv hvhid dev/hyperv/input/hv_kbd.c optional hyperv dev/hyperv/input/hv_kbdc.c optional hyperv diff --git a/sys/dev/hyperv/hvfb/hv_fb.c b/sys/dev/hyperv/hvfb/hv_fb.c new file mode 100644 --- /dev/null +++ b/sys/dev/hyperv/hvfb/hv_fb.c @@ -0,0 +1,727 @@ +/*- + * Copyright (c) 2017 Microsoft Corp. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "vmbus_if.h" + +/* Protocol details taken from linux hyperv_fb driver */ + +#define SV_VER(major, minor) ((minor) << 16 | (major)) +#define SV_VER_MAJOR(ver) (ver & 0x0000ffff) +#define SV_VER_MINOR(ver) ((ver & 0xffff0000) >> 16) + +#define SV_VER_WIN7 SV_VER(3, 0) +#define SV_VER_WIN8 SV_VER(3, 2) +#define SV_VER_WIN10 SV_VER(3, 5) + +#define HV_BUFSIZ (4 * PAGE_SIZE) +#define HV_RINGBUFF_SZ (10 * PAGE_SIZE) + +typedef struct { + device_t dev; + struct mtx mtx; + struct callout co; + bool ready; + /* vmbus */ + struct vmbus_channel *hs_chan; + struct vmbus_xact_ctx *hs_xact_ctx; + uint8_t *buf; + int buflen; + /* fb */ + struct fb_info fb; +} hv_fb_sc; + +typedef enum { + SV_VER_REQ = 1, + SV_VER_RESP = 2, + SV_VRAM = 3, + SV_VRAM_ACK = 4, + SV_SITU = 5, + SV_SITU_ACK = 6, + SV_PTR_POS = 7, + SV_PTR_SHAPE = 8, + SV_FEAT_CHG = 9, + SV_DIRT = 10, + SV_RES_REQ = 13, + SV_RES_RESP = 14, +} sv_msg_type; + +typedef struct { + sv_msg_type type; + uint32_t size; /* includes header size */ +} __packed sv_msg_hdr; + +typedef struct { + uint32_t ver; +} __packed sv_ver_req; + +typedef struct { + uint32_t ver; + uint8_t is_acc; + uint8_t max_vo; +} __packed sv_ver_resp; + +typedef struct { + uint8_t max_res_cnt; +} __packed sv_res_req; + +typedef struct { + uint16_t width; + uint16_t height; +} __packed sv_scr_info; + +#define SV_EDID_SZ 128 +#define SV_MAX_RES_CNT 64 + +typedef struct { + uint8_t edid[SV_EDID_SZ]; + uint8_t res_cnt; + uint8_t def_res_idx; + uint8_t is_std; + sv_scr_info res[SV_MAX_RES_CNT]; +} __packed sv_res_resp; + +typedef struct { + uint64_t ctx; + uint8_t is_gpa; + uint64_t gpa; +} __packed sv_vram; + +typedef struct { + uint64_t ctx; +} __packed sv_vram_ack; + +typedef struct { + uint8_t active; + uint32_t vram_off; + uint8_t depth; + uint32_t width; + uint32_t height; + uint32_t pitch; +} __packed vo_situ; + +typedef struct { + uint64_t ctx; + uint8_t vo_cnt; + vo_situ vo[1]; +} __packed sv_situ; + +typedef struct { + uint64_t ctx; +} __packed sv_situ_ack; + +typedef struct { + uint8_t is_visible; + uint8_t vo; + int32_t img_x; + int32_t img_y; +} __packed sv_ptr_pos; + +typedef struct { + uint8_t part_idx; + uint8_t is_argb; + uint32_t width; + uint32_t height; + uint32_t hs_x; + uint32_t hs_y; + uint8_t data[4]; +} __packed sv_ptr_shape; + +typedef struct { + uint8_t dirt; + uint8_t ptr_pos; + uint8_t ptr_shape; + uint8_t situ; +} __packed sv_feat_chg; + +typedef struct { + int32_t x1; + int32_t y1; + int32_t x2; + int32_t y2; +} __packed sv_rect; + +typedef struct { + uint8_t vo; + uint8_t cnt; + sv_rect rect[1]; +} __packed sv_dirt; + +typedef enum { + HV_MSG_INVALID, + HV_MSG_DATA, +} hv_msg_type; + +typedef struct { + hv_msg_type type; + uint32_t size; /* does not include header size */ +} hv_fb_hdr; + +typedef struct { + hv_fb_hdr hdr; + sv_msg_hdr sv_hdr; + union { + sv_ver_req ver_req; + sv_ver_resp ver_resp; + sv_vram vram; + sv_vram_ack vram_ack; + sv_situ situ; + sv_situ_ack situ_ack; + sv_ptr_pos ptr_pos; + sv_ptr_shape ptr_shape; + sv_feat_chg feat_chg; + sv_dirt dirt; + sv_res_req res_req; + sv_res_resp res_resp; + }; +} __packed hv_fb_msg; + +#define FPS_MIN 1 +#define FPS_MAX 60 +#define FPS_DEF 30 + +SYSCTL_NODE(_hw, OID_AUTO, hvfb, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, + "Hyper-V framebuffer interface"); + +static int +hv_fb_handle_fps(SYSCTL_HANDLER_ARGS) +{ + int error, value; + + value = *(int *)arg1; + error = sysctl_handle_int(oidp, &value, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + if (value < FPS_MIN || value > FPS_MAX) + return (EINVAL); + *(int *)arg1 = value; + return (0); +} +static int hv_fb_fps = FPS_DEF; +SYSCTL_PROC(_hw_hvfb, OID_AUTO, fps, CTLFLAG_RWTUN | CTLTYPE_INT, + &hv_fb_fps, 0, hv_fb_handle_fps, "I", "frames per second (1-240)"); + +static inline int +hv_fb_send(hv_fb_sc *sc, hv_fb_msg *req, const hv_fb_msg **resp) +{ + struct vmbus_xact *xact; + hv_fb_msg *xreq; + const hv_fb_msg *xresp; + size_t xreqlen; + size_t xresplen; + int ret; + + xreqlen = sizeof(hv_fb_hdr) + req->sv_hdr.size; + xact = vmbus_xact_get(sc->hs_xact_ctx, xreqlen); + if (xact == NULL) { + device_printf(sc->dev, "failed to get xact"); + return (ENOMEM); + } + xreq = vmbus_xact_req_data(xact); + memcpy(xreq, req, xreqlen); + xreq->hdr.type = HV_MSG_DATA; + xreq->hdr.size = req->sv_hdr.size; + + vmbus_xact_activate(xact); + ret = vmbus_chan_send(sc->hs_chan, VMBUS_CHANPKT_TYPE_INBAND, + VMBUS_CHANPKT_FLAG_RC, xreq, xreqlen, (uint64_t)(uintptr_t)xact); + if (ret != 0 || resp == NULL) { + vmbus_xact_deactivate(xact); + vmbus_xact_put(xact); + return (ret); + } + xresp = vmbus_chan_xact_wait(sc->hs_chan, xact, &xresplen, true); + *resp = xresp; + vmbus_xact_put(xact); + return (0); +} + +static int +hv_fb_neg_ver(hv_fb_sc *sc, uint32_t ver) +{ + hv_fb_msg req = { 0 }; + const hv_fb_msg *resp; + + req.sv_hdr.type = SV_VER_REQ; + req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_ver_req); + req.ver_req.ver = ver; + + if (hv_fb_send(sc, &req, &resp) != 0 || + resp->ver_resp.is_acc == 0) + return (ENODEV); + + device_printf(sc->dev, "version %d.%d\n", + SV_VER_MAJOR(ver), SV_VER_MINOR(ver)); + return (0); +} + +static int +hv_fb_get_res(hv_fb_sc *sc) +{ + hv_fb_msg req = { 0 }; + const hv_fb_msg *resp; + uint8_t idx; + uint8_t i; + + req.sv_hdr.type = SV_RES_REQ; + req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_res_req); + req.res_req.max_res_cnt = SV_MAX_RES_CNT; + + if (hv_fb_send(sc, &req, &resp) != 0) + return (ENODEV); + if (resp->res_resp.res_cnt == 0) { + device_printf(sc->dev, "no supported resolutions"); + return (ENODEV); + } + idx = resp->res_resp.def_res_idx; + if (idx >= resp->res_resp.res_cnt) { + device_printf(sc->dev, "invalid resolution index: %d\n", idx); + return (ENODEV); + } + device_printf(sc->dev, "supported resolutions:\n"); + for (i = 0; i < resp->res_resp.res_cnt; i++) { + device_printf(sc->dev, " (%s%2d) %dx%d\n", + i == idx ? "*" : " ", i, + resp->res_resp.res[i].width, + resp->res_resp.res[i].height); + } + + sc->fb.fb_width = resp->res_resp.res[idx].width; + sc->fb.fb_height = resp->res_resp.res[idx].height; + + return (0); +} + +static int +hv_fb_connect_vsp(hv_fb_sc *sc) +{ + int ret; + + /* + * Negotiate version. + * Everything prior to WIN10 is already unsupported by Microsoft, more + * so I have no means of testing on previous versions. + */ + switch (vmbus_current_version) { + default: + case VMBUS_VERSION_WIN10: + ret = hv_fb_neg_ver(sc, SV_VER_WIN10); + break; + } + if (ret != 0) { + device_printf(sc->dev, "failed to negotiate version\n"); + return (ret); + } + + /* Get supported resolution list */ + ret = hv_fb_get_res(sc); + + return (ret); +} + +/* Essentially this hides the builtin pointer */ +static void +hv_fb_send_ptr(hv_fb_sc *sc) +{ + hv_fb_msg req; + + memset(&req, 0, sizeof(req)); + req.sv_hdr.type = SV_PTR_POS; + req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_ptr_pos); + req.ptr_pos.is_visible = 1; + req.ptr_pos.vo = 0; + req.ptr_pos.img_x = 0; + req.ptr_pos.img_y = 0; + if (hv_fb_send(sc, &req, NULL) != 0) + device_printf(sc->dev, "failed to send ptr pos\n"); + + memset(&req, 0, sizeof(req)); + req.sv_hdr.type = SV_PTR_SHAPE; + req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_ptr_shape); + req.ptr_shape.part_idx = -1; + req.ptr_shape.is_argb = 1; + req.ptr_shape.width = 1; + req.ptr_shape.height = 1; + req.ptr_shape.hs_x = 0; + req.ptr_shape.hs_y = 0; + req.ptr_shape.data[0] = 0; + req.ptr_shape.data[1] = 1; + req.ptr_shape.data[2] = 1; + req.ptr_shape.data[3] = 1; + if (hv_fb_send(sc, &req, NULL) != 0) + device_printf(sc->dev, "failed to send ptr shape\n"); +} + +static void +hv_fb_send_situ(hv_fb_sc *sc) +{ + hv_fb_msg req; + + memset(&req, 0, sizeof(req)); + req.sv_hdr.type = SV_SITU; + req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_situ); + req.situ.ctx = 0; + req.situ.vo_cnt = 1; + req.situ.vo[0].active = 1; + req.situ.vo[0].vram_off = 0; + req.situ.vo[0].depth = sc->fb.fb_depth; + req.situ.vo[0].width = sc->fb.fb_width; + req.situ.vo[0].height = sc->fb.fb_height; + req.situ.vo[0].pitch = sc->fb.fb_stride; + if (hv_fb_send(sc, &req, NULL) != 0) + device_printf(sc->dev, "failed to send situ\n"); +} + +static int +hv_fb_send_config(hv_fb_sc *sc) +{ + hv_fb_msg req; + const hv_fb_msg *resp; + + memset(&req, 0, sizeof(req)); + req.sv_hdr.type = SV_VRAM; + req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_vram); + req.vram.ctx = req.vram.gpa = sc->fb.fb_pbase; + req.vram.is_gpa = 1; + + if (hv_fb_send(sc, &req, &resp) != 0 || + resp->vram_ack.ctx != sc->fb.fb_pbase) { + device_printf(sc->dev, "failed to set vram\n"); + return (ENODEV); + } + + hv_fb_send_ptr(sc); + hv_fb_send_situ(sc); + + return (0); +} + +static void +hv_fb_update(hv_fb_sc *sc, int x1, int y1, int x2, int y2) +{ + hv_fb_msg req; + + if (x2 == -1) + x2 = sc->fb.fb_width; + if (y2 == -1) + y2 = sc->fb.fb_height; + + memset(&req, 0, sizeof(req)); + req.sv_hdr.type = SV_DIRT; + req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_dirt); + req.dirt.vo = 0; + req.dirt.cnt = 1; + req.dirt.rect[0].x1 = (x1 > x2) ? 0 : x1; + req.dirt.rect[0].y1 = (y1 > y2) ? 0 : y1; + req.dirt.rect[0].x2 = + (x2 < x1 || x2 > sc->fb.fb_width) ? sc->fb.fb_width : x2; + req.dirt.rect[0].y2 = + (y2 < y1 || y2 > sc->fb.fb_height) ? sc->fb.fb_height : y2; + hv_fb_send(sc, &req, NULL); +} + +static void +hv_fb_sync(void *arg) +{ + hv_fb_sc *sc; + + sc = arg; + hv_fb_update(sc, 0, 0, -1, -1); + callout_schedule(&sc->co, hz / hv_fb_fps); +} + +static void +hv_fb_receive(hv_fb_sc *sc, struct vmbus_chanpkt_hdr *pkt) +{ + const hv_fb_msg *msg; + uint32_t msg_len; + + msg = VMBUS_CHANPKT_CONST_DATA(pkt); + msg_len = VMBUS_CHANPKT_DATALEN(pkt); + + if (msg->hdr.type != HV_MSG_DATA) + return; + + switch (msg->sv_hdr.type) { + case SV_VER_RESP: + case SV_RES_RESP: + case SV_VRAM_ACK: + if (sc->hs_xact_ctx != NULL) + vmbus_xact_ctx_wakeup(sc->hs_xact_ctx, msg, msg_len); + break; + case SV_SITU_ACK: + /* Nothing interesting here */ + break; + case SV_FEAT_CHG: + mtx_lock(&sc->mtx); + if (sc->ready) { + hv_fb_send_ptr(sc); + hv_fb_send_situ(sc); + } + mtx_unlock(&sc->mtx); + break; + default: + device_printf(sc->dev, "unhandled type: %d\n", + msg->sv_hdr.type); + break; + } +} + +static void +hv_fb_read_channel(struct vmbus_channel *channel, void *ctx) +{ + hv_fb_sc *sc; + uint8_t *buf; + int buflen; + int ret; + + sc = ctx; + buf = sc->buf; + buflen = sc->buflen; + for (;;) { + struct vmbus_chanpkt_hdr *pkt; + int rcvd; + + pkt = (struct vmbus_chanpkt_hdr *)buf; + rcvd = buflen; + ret = vmbus_chan_recv_pkt(channel, pkt, &rcvd); + if (__predict_false(ret == ENOBUFS)) { + buflen = sc->buflen * 2; + while (buflen < rcvd) + buflen *= 2; + buf = malloc(buflen, M_DEVBUF, M_WAITOK | M_ZERO); + device_printf(sc->dev, "expand recvbuf %d -> %d\n", + sc->buflen, buflen); + free(sc->buf, M_DEVBUF); + sc->buf = buf; + sc->buflen = buflen; + continue; + } else if (__predict_false(ret == EAGAIN)) { + /* No more channel packets; done! */ + break; + } + KASSERT(ret == 0, ("vmbus_chan_recv_pkt failed: %d", ret)); + + switch (pkt->cph_type) { + case VMBUS_CHANPKT_TYPE_COMP: + case VMBUS_CHANPKT_TYPE_RXBUF: + device_printf(sc->dev, "unhandled event: %d\n", + pkt->cph_type); + break; + case VMBUS_CHANPKT_TYPE_INBAND: + hv_fb_receive(sc, pkt); + break; + default: + device_printf(sc->dev, "unknown event: %d\n", + pkt->cph_type); + break; + } + } +} + +static const struct vmbus_ic_desc vmbus_fb_descs[] = { + { + .ic_guid = { .hv_guid = { + 0x02, 0x78, 0x0a, 0xda, 0x77, 0xe3, 0xac, 0x4a, + 0x8e, 0x77, 0x05, 0x58, 0xeb, 0x10, 0x73, 0xf8} }, + .ic_desc = "Hyper-V framebuffer device" + }, + VMBUS_IC_DESC_END +}; + +/* TODO: add GUID support to devmatch(8) to export vmbus_fb_descs directly */ +const struct { + char *guid; +} vmbus_fb_descs_pnp[] = { { "da0a7802-e377-4aac-8e77-0558eb1073f8" } }; + +static int hv_fb_attach(device_t dev); +static int hv_fb_detach(device_t dev); + +static int +hv_fb_probe(device_t dev) +{ + device_t bus; + const struct vmbus_ic_desc *d; + + if (resource_disabled(device_get_name(dev), 0)) + return (ENXIO); + + bus = device_get_parent(dev); + for (d = vmbus_fb_descs; d->ic_desc != NULL; ++d) { + if (VMBUS_PROBE_GUID(bus, dev, &d->ic_guid) == 0) { + device_set_desc(dev, d->ic_desc); + return (BUS_PROBE_DEFAULT); + } + } + + return (ENXIO); +} + +static int +hv_fb_attach(device_t dev) +{ + hv_fb_sc *sc; + struct efi_fb *efifb; + caddr_t kmdp; + int ret; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->ready = false; + sc->buflen = HV_BUFSIZ; + sc->buf = malloc(sc->buflen, M_DEVBUF, M_WAITOK | M_ZERO); + mtx_init(&sc->mtx, "hvfb lock", NULL, MTX_DEF); + + ret = ENODEV; + sc->hs_chan = vmbus_get_channel(dev); + sc->hs_xact_ctx = vmbus_xact_ctx_create(bus_get_dma_tag(dev), + HV_RINGBUFF_SZ, HV_RINGBUFF_SZ, 0); + if (sc->hs_xact_ctx == NULL) { + device_printf(sc->dev, "failed to create xact ctx\n"); + ret = ENOMEM; + goto out; + } + vmbus_chan_set_readbatch(sc->hs_chan, false); + if (vmbus_chan_open(sc->hs_chan, HV_RINGBUFF_SZ, HV_RINGBUFF_SZ, NULL, + 0, hv_fb_read_channel, sc) != 0) { + device_printf(sc->dev, "failed to open vmbus chan\n"); + goto out; + } + + if (hv_fb_connect_vsp(sc) != 0) + goto out; + + /* Get the framebuffer information from EFI loader */ + kmdp = preload_search_by_type("elf kernel"); + if (kmdp == NULL) + kmdp = preload_search_by_type("elf64 kernel"); + efifb = (struct efi_fb *)preload_search_info(kmdp, + MODINFO_METADATA | MODINFOMD_EFI_FB); + if (efifb == NULL) { + device_printf(sc->dev, + "failed to find framebuffer information\n"); + goto out; + } + + sc->fb.fb_type = FBTYPE_PCIMISC; + sc->fb.fb_pbase = efifb->fb_addr; + sc->fb.fb_size = efifb->fb_size; + sc->fb.fb_vbase = (intptr_t)pmap_mapdev_attr(sc->fb.fb_pbase, + sc->fb.fb_size, VM_MEMATTR_WRITE_COMBINING); + sc->fb.fb_depth = 32; /* XXX */ + sc->fb.fb_bpp = roundup2(sc->fb.fb_depth, NBBY); + sc->fb.fb_stride = sc->fb.fb_width * (sc->fb.fb_bpp / NBBY); + sc->fb.fb_cmsize = 0; + + if (hv_fb_send_config(sc) != 0) + goto out; + + sc->fb.fb_fbd_dev = device_add_child(dev, "fbd", -1); + if (sc->fb.fb_fbd_dev == NULL || vt_fb_attach(&sc->fb) != 0) { + device_printf(sc->dev, "failed to attach fbd"); + goto out; + } + + /* Initialization complete, schedule synchronous updates */ + callout_init(&sc->co, 1); + callout_reset(&sc->co, hz / hv_fb_fps, hv_fb_sync, sc); + + mtx_lock(&sc->mtx); + sc->ready = true; + mtx_unlock(&sc->mtx); + ret = 0; +out: + if (ret != 0) + hv_fb_detach(dev); + return (ret); +} + +static int +hv_fb_detach(device_t dev) +{ + hv_fb_sc *sc; + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); + sc->ready = false; + mtx_unlock(&sc->mtx); + callout_drain(&sc->co); + if (sc->fb.fb_fbd_dev != NULL) { + mtx_lock(&Giant); + device_delete_child(dev, sc->fb.fb_fbd_dev); + mtx_unlock(&Giant); + sc->fb.fb_fbd_dev = NULL; + } + vt_fb_detach(&sc->fb); + + if (sc->hs_xact_ctx != NULL) + vmbus_xact_ctx_destroy(sc->hs_xact_ctx); + vmbus_chan_close(vmbus_get_channel(dev)); + free(sc->buf, M_DEVBUF); + mtx_destroy(&sc->mtx); + + return (0); +} + +static device_method_t hv_fb_methods[] = { + DEVMETHOD(device_probe, hv_fb_probe), + DEVMETHOD(device_attach, hv_fb_attach), + DEVMETHOD(device_detach, hv_fb_detach), + DEVMETHOD_END, +}; + +static driver_t hv_fb_driver = { + .name = "hvfb", + .methods = hv_fb_methods, + .size = sizeof(hv_fb_sc), +}; + +DRIVER_MODULE(hv_fb, vmbus, hv_fb_driver, NULL, NULL); +MODULE_VERSION(hv_fb, 1); +MODULE_DEPEND(hv_fb, vmbus, 1, 1, 1); +MODULE_DEPEND(hv_fb, fbd, 1, 1, 1); +MODULE_PNP_INFO("Z:classid", vmbus, hv_fb, vmbus_fb_descs_pnp, + nitems(vmbus_fb_descs_pnp)); diff --git a/sys/modules/hyperv/Makefile b/sys/modules/hyperv/Makefile --- a/sys/modules/hyperv/Makefile +++ b/sys/modules/hyperv/Makefile @@ -1,5 +1,5 @@ # $FreeBSD$ -SUBDIR = vmbus netvsc storvsc utilities hvsock hid +SUBDIR = vmbus netvsc storvsc utilities hvsock hid fb .include diff --git a/sys/modules/hyperv/fb/Makefile b/sys/modules/hyperv/fb/Makefile new file mode 100644 --- /dev/null +++ b/sys/modules/hyperv/fb/Makefile @@ -0,0 +1,11 @@ +.PATH: ${SRCTOP}/sys/dev/hyperv/hvfb + +KMOD= hv_fb +SRCS= hv_fb.c +SRCS+= bus_if.h device_if.h vmbus_if.h +SRCS+= opt_splash.h opt_syscons.h opt_teken.h + +CFLAGS+= -I${SRCTOP}/sys/dev/hyperv/include \ + -I${SRCTOP}/sys/dev/hyperv/vmbus + +.include