vncinput.cc revision 8635
11060SN/A/* 29814Sandreas.hansson@arm.com * Copyright (c) 2010 ARM Limited 39920Syasuko.eckert@amd.com * All rights reserved 47944SGiacomo.Gabrielli@arm.com * 57944SGiacomo.Gabrielli@arm.com * The license below extends only to copyright in the software and shall 67944SGiacomo.Gabrielli@arm.com * not be construed as granting a license to any other intellectual 77944SGiacomo.Gabrielli@arm.com * property including but not limited to intellectual property relating 87944SGiacomo.Gabrielli@arm.com * to a hardware implementation of the functionality of the software 97944SGiacomo.Gabrielli@arm.com * licensed hereunder. You may use the software subject to the license 107944SGiacomo.Gabrielli@arm.com * terms below provided that you ensure that this notice is replicated 117944SGiacomo.Gabrielli@arm.com * unmodified and in its entirety in all distributions of the software, 127944SGiacomo.Gabrielli@arm.com * modified or unmodified, in source code or in binary form. 137944SGiacomo.Gabrielli@arm.com * 147944SGiacomo.Gabrielli@arm.com * Redistribution and use in source and binary forms, with or without 152702Sktlim@umich.edu * modification, are permitted provided that the following conditions are 166973Stjones1@inf.ed.ac.uk * met: redistributions of source code must retain the above copyright 171060SN/A * notice, this list of conditions and the following disclaimer; 181060SN/A * redistributions in binary form must reproduce the above copyright 191060SN/A * notice, this list of conditions and the following disclaimer in the 201060SN/A * documentation and/or other materials provided with the distribution; 211060SN/A * neither the name of the copyright holders nor the names of its 221060SN/A * contributors may be used to endorse or promote products derived from 231060SN/A * this software without specific prior written permission. 241060SN/A * 251060SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 261060SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 271060SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 281060SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 291060SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 301060SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 311060SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 321060SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 331060SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 341060SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 351060SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 361060SN/A * 371060SN/A * Authors: Ali Saidi 381060SN/A * William Wang 391060SN/A */ 401060SN/A 412665Ssaidi@eecs.umich.edu/** @file 422665Ssaidi@eecs.umich.edu * Implementiation of a VNC server 436973Stjones1@inf.ed.ac.uk */ 441060SN/A 451060SN/A#include <sys/ioctl.h> 461464SN/A#include <sys/stat.h> 471464SN/A#include <sys/termios.h> 481060SN/A#include <sys/types.h> 492731Sktlim@umich.edu#include <fcntl.h> 502292SN/A#include <poll.h> 511464SN/A#include <unistd.h> 528733Sgeoffrey.blake@arm.com 531060SN/A#include <cerrno> 547720Sgblack@eecs.umich.edu#include <cstdio> 551060SN/A 566658Snate@binkert.org#include "base/vnc/vncserver.hh" 578887Sgeoffrey.blake@arm.com#include "base/atomicio.hh" 583770Sgblack@eecs.umich.edu#include "base/bitmap.hh" 591464SN/A#include "base/misc.hh" 601464SN/A#include "base/output.hh" 612669Sktlim@umich.edu#include "base/socket.hh" 621060SN/A#include "base/trace.hh" 636973Stjones1@inf.ed.ac.uk#include "debug/VNC.hh" 642669Sktlim@umich.edu#include "sim/byteswap.hh" 657678Sgblack@eecs.umich.edu#include "sim/core.hh" 668817Sgblack@eecs.umich.edu 672292SN/Ausing namespace std; 686023Snate@binkert.org 691060SN/A/** 701060SN/A * Poll event for the listen socket 711060SN/A */ 721060SN/AVncServer::ListenEvent::ListenEvent(VncServer *vs, int fd, int e) 731060SN/A : PollEvent(fd, e), vncserver(vs) 741060SN/A{ 751060SN/A} 769044SAli.Saidi@ARM.com 771060SN/Avoid 781060SN/AVncServer::ListenEvent::process(int revent) 791060SN/A{ 802733Sktlim@umich.edu vncserver->accept(); 812733Sktlim@umich.edu} 821060SN/A 832292SN/A/** 842107SN/A * Poll event for the data socket 852690Sktlim@umich.edu */ 862107SN/AVncServer::DataEvent::DataEvent(VncServer *vs, int fd, int e) 872690Sktlim@umich.edu : PollEvent(fd, e), vncserver(vs) 882690Sktlim@umich.edu{ 891060SN/A} 902292SN/A 912292SN/Avoid 928486Sgblack@eecs.umich.eduVncServer::DataEvent::process(int revent) 932292SN/A{ 942292SN/A if (revent & POLLIN) 952292SN/A vncserver->data(); 962292SN/A else if (revent & POLLNVAL) 971060SN/A vncserver->detach(); 985543Ssaidi@eecs.umich.edu} 998902Sandreas.hansson@arm.com 1001060SN/A/** 1011060SN/A * VncServer 1029046SAli.Saidi@ARM.com */ 1039046SAli.Saidi@ARM.comVncServer::VncServer(const Params *p) 1049046SAli.Saidi@ARM.com : SimObject(p), listenEvent(NULL), dataEvent(NULL), number(p->number), 1059046SAli.Saidi@ARM.com dataFd(-1), _videoWidth(1), _videoHeight(1), clientRfb(0), keyboard(NULL), 1069046SAli.Saidi@ARM.com mouse(NULL), sendUpdate(false), videoMode(VideoConvert::UnknownMode), 1079046SAli.Saidi@ARM.com vc(NULL), captureEnabled(p->frame_capture), captureCurrentFrame(0), 1089046SAli.Saidi@ARM.com captureLastHash(0), captureBitmap(0) 1099046SAli.Saidi@ARM.com{ 1109046SAli.Saidi@ARM.com if (p->port) 1119046SAli.Saidi@ARM.com listen(p->port); 1129046SAli.Saidi@ARM.com 1139046SAli.Saidi@ARM.com curState = WaitForProtocolVersion; 1149046SAli.Saidi@ARM.com 1159046SAli.Saidi@ARM.com // currently we only support this one pixel format 1169046SAli.Saidi@ARM.com // unpacked 32bit rgb (rgb888 + 8 bits of nothing/alpha) 1179046SAli.Saidi@ARM.com // keep it around for telling the client and making 1189046SAli.Saidi@ARM.com // sure the client cooperates 1199046SAli.Saidi@ARM.com pixelFormat.bpp = 32; 1209046SAli.Saidi@ARM.com pixelFormat.depth = 24; 1219046SAli.Saidi@ARM.com pixelFormat.bigendian = 0; 1229046SAli.Saidi@ARM.com pixelFormat.truecolor = 1; 1239046SAli.Saidi@ARM.com pixelFormat.redmax = 0xff; 1249046SAli.Saidi@ARM.com pixelFormat.greenmax = 0xff; 1259046SAli.Saidi@ARM.com pixelFormat.bluemax = 0xff; 1269046SAli.Saidi@ARM.com pixelFormat.redshift = 16; 1279046SAli.Saidi@ARM.com pixelFormat.greenshift = 8; 1289046SAli.Saidi@ARM.com pixelFormat.blueshift = 0; 1299046SAli.Saidi@ARM.com 1309046SAli.Saidi@ARM.com if (captureEnabled) { 1319046SAli.Saidi@ARM.com // remove existing frame output directory if it exists, then create a 1329046SAli.Saidi@ARM.com // clean empty directory 1339046SAli.Saidi@ARM.com const string FRAME_OUTPUT_SUBDIR = "frames_" + name(); 1349046SAli.Saidi@ARM.com simout.remove(FRAME_OUTPUT_SUBDIR, true); 1359046SAli.Saidi@ARM.com captureOutputDirectory = simout.createSubdirectory( 1369046SAli.Saidi@ARM.com FRAME_OUTPUT_SUBDIR); 1379046SAli.Saidi@ARM.com } 1389046SAli.Saidi@ARM.com 1399046SAli.Saidi@ARM.com DPRINTF(VNC, "Vnc server created at port %d\n", p->port); 1409046SAli.Saidi@ARM.com} 1419046SAli.Saidi@ARM.com 1429046SAli.Saidi@ARM.comVncServer::~VncServer() 1439046SAli.Saidi@ARM.com{ 1449046SAli.Saidi@ARM.com if (dataFd != -1) 1459046SAli.Saidi@ARM.com ::close(dataFd); 1469046SAli.Saidi@ARM.com 1479046SAli.Saidi@ARM.com if (listenEvent) 1489046SAli.Saidi@ARM.com delete listenEvent; 1499046SAli.Saidi@ARM.com 1509046SAli.Saidi@ARM.com if (dataEvent) 1519046SAli.Saidi@ARM.com delete dataEvent; 1529046SAli.Saidi@ARM.com} 1539046SAli.Saidi@ARM.com 1549046SAli.Saidi@ARM.com 1559046SAli.Saidi@ARM.com//socket creation and vnc client attach 1569046SAli.Saidi@ARM.comvoid 1579046SAli.Saidi@ARM.comVncServer::listen(int port) 1589046SAli.Saidi@ARM.com{ 1599046SAli.Saidi@ARM.com if (ListenSocket::allDisabled()) { 1609046SAli.Saidi@ARM.com warn_once("Sockets disabled, not accepting vnc client connections"); 1612292SN/A return; 1622107SN/A } 1639046SAli.Saidi@ARM.com 1649046SAli.Saidi@ARM.com while (!listener.listen(port, true)) { 1659046SAli.Saidi@ARM.com DPRINTF(VNC, 1669046SAli.Saidi@ARM.com "can't bind address vnc server port %d in use PID %d\n", 16710030SAli.Saidi@ARM.com port, getpid()); 16810030SAli.Saidi@ARM.com port++; 1699046SAli.Saidi@ARM.com } 1709046SAli.Saidi@ARM.com 1719046SAli.Saidi@ARM.com int p1, p2; 1729046SAli.Saidi@ARM.com p2 = name().rfind('.') - 1; 1739046SAli.Saidi@ARM.com p1 = name().rfind('.', p2); 1749046SAli.Saidi@ARM.com ccprintf(cerr, "Listening for %s connection on port %d\n", 1759046SAli.Saidi@ARM.com name().substr(p1 + 1, p2 - p1), port); 1769046SAli.Saidi@ARM.com 1779046SAli.Saidi@ARM.com listenEvent = new ListenEvent(this, listener.getfd(), POLLIN); 1789046SAli.Saidi@ARM.com pollQueue.schedule(listenEvent); 1799046SAli.Saidi@ARM.com} 1809046SAli.Saidi@ARM.com 1819046SAli.Saidi@ARM.com// attach a vnc client 1829046SAli.Saidi@ARM.comvoid 1839046SAli.Saidi@ARM.comVncServer::accept() 1849046SAli.Saidi@ARM.com{ 1859046SAli.Saidi@ARM.com if (!listener.islistening()) 1869046SAli.Saidi@ARM.com panic("%s: cannot accept a connection if not listening!", name()); 1879046SAli.Saidi@ARM.com 1889046SAli.Saidi@ARM.com int fd = listener.accept(true); 1899046SAli.Saidi@ARM.com if (dataFd != -1) { 1909046SAli.Saidi@ARM.com char message[] = "vnc server already attached!\n"; 1919046SAli.Saidi@ARM.com atomic_write(fd, message, sizeof(message)); 1929046SAli.Saidi@ARM.com ::close(fd); 1939046SAli.Saidi@ARM.com return; 1949046SAli.Saidi@ARM.com } 1959046SAli.Saidi@ARM.com 1969046SAli.Saidi@ARM.com dataFd = fd; 1979046SAli.Saidi@ARM.com 1989046SAli.Saidi@ARM.com // Send our version number to the client 1999046SAli.Saidi@ARM.com write((uint8_t*)vncVersion(), strlen(vncVersion())); 2009046SAli.Saidi@ARM.com 2019046SAli.Saidi@ARM.com // read the client response 2029046SAli.Saidi@ARM.com dataEvent = new DataEvent(this, dataFd, POLLIN); 2039046SAli.Saidi@ARM.com pollQueue.schedule(dataEvent); 2049046SAli.Saidi@ARM.com 2059046SAli.Saidi@ARM.com inform("VNC client attached\n"); 2069046SAli.Saidi@ARM.com} 2079046SAli.Saidi@ARM.com 2089046SAli.Saidi@ARM.com// data called by data event 2099046SAli.Saidi@ARM.comvoid 2108502Sgblack@eecs.umich.eduVncServer::data() 2111060SN/A{ 2129046SAli.Saidi@ARM.com // We have new data, see if we can handle it 2139046SAli.Saidi@ARM.com size_t len; 2149046SAli.Saidi@ARM.com DPRINTF(VNC, "Vnc client message recieved\n"); 2159046SAli.Saidi@ARM.com 2169046SAli.Saidi@ARM.com switch (curState) { 2179046SAli.Saidi@ARM.com case WaitForProtocolVersion: 2189046SAli.Saidi@ARM.com checkProtocolVersion(); 2199046SAli.Saidi@ARM.com break; 2209046SAli.Saidi@ARM.com case WaitForSecurityResponse: 2219046SAli.Saidi@ARM.com checkSecurity(); 2229046SAli.Saidi@ARM.com break; 2239046SAli.Saidi@ARM.com case WaitForClientInit: 2249046SAli.Saidi@ARM.com // Don't care about shared, just need to read it out of the socket 2259046SAli.Saidi@ARM.com uint8_t shared; 2269046SAli.Saidi@ARM.com len = read(&shared); 2279046SAli.Saidi@ARM.com assert(len == 1); 2289046SAli.Saidi@ARM.com 2299046SAli.Saidi@ARM.com // Send our idea of the frame buffer 2309046SAli.Saidi@ARM.com sendServerInit(); 2319046SAli.Saidi@ARM.com 2329046SAli.Saidi@ARM.com break; 2339046SAli.Saidi@ARM.com case NormalPhase: 2349046SAli.Saidi@ARM.com uint8_t message_type; 2359046SAli.Saidi@ARM.com len = read(&message_type); 2369046SAli.Saidi@ARM.com if (!len) { 2379046SAli.Saidi@ARM.com detach(); 2389046SAli.Saidi@ARM.com return; 2399046SAli.Saidi@ARM.com } 2409046SAli.Saidi@ARM.com assert(len == 1); 2419046SAli.Saidi@ARM.com 2429046SAli.Saidi@ARM.com switch (message_type) { 2439046SAli.Saidi@ARM.com case ClientSetPixelFormat: 2449046SAli.Saidi@ARM.com setPixelFormat(); 2459046SAli.Saidi@ARM.com break; 2469046SAli.Saidi@ARM.com case ClientSetEncodings: 2479046SAli.Saidi@ARM.com setEncodings(); 2489046SAli.Saidi@ARM.com break; 2499046SAli.Saidi@ARM.com case ClientFrameBufferUpdate: 2509046SAli.Saidi@ARM.com requestFbUpdate(); 2519046SAli.Saidi@ARM.com break; 2529046SAli.Saidi@ARM.com case ClientKeyEvent: 2539046SAli.Saidi@ARM.com recvKeyboardInput(); 2549046SAli.Saidi@ARM.com break; 2559046SAli.Saidi@ARM.com case ClientPointerEvent: 2569046SAli.Saidi@ARM.com recvPointerInput(); 2579046SAli.Saidi@ARM.com break; 2589046SAli.Saidi@ARM.com case ClientCutText: 2599046SAli.Saidi@ARM.com recvCutText(); 2609046SAli.Saidi@ARM.com break; 2619046SAli.Saidi@ARM.com default: 2629046SAli.Saidi@ARM.com panic("Unimplemented message type recv from client: %d\n", 2639046SAli.Saidi@ARM.com message_type); 2649046SAli.Saidi@ARM.com break; 2659046SAli.Saidi@ARM.com } 2669046SAli.Saidi@ARM.com break; 2679046SAli.Saidi@ARM.com default: 2689046SAli.Saidi@ARM.com panic("Unknown vnc server state\n"); 2699046SAli.Saidi@ARM.com } 2709046SAli.Saidi@ARM.com} 2719046SAli.Saidi@ARM.com 2729046SAli.Saidi@ARM.com 2739046SAli.Saidi@ARM.com// read from socket 2749046SAli.Saidi@ARM.comsize_t 2759046SAli.Saidi@ARM.comVncServer::read(uint8_t *buf, size_t len) 2769046SAli.Saidi@ARM.com{ 2779046SAli.Saidi@ARM.com if (dataFd < 0) 2789046SAli.Saidi@ARM.com panic("vnc not properly attached.\n"); 2799046SAli.Saidi@ARM.com 2809046SAli.Saidi@ARM.com size_t ret; 2819046SAli.Saidi@ARM.com do { 2829046SAli.Saidi@ARM.com ret = ::read(dataFd, buf, len); 2839046SAli.Saidi@ARM.com } while (ret == -1 && errno == EINTR); 2849046SAli.Saidi@ARM.com 2859046SAli.Saidi@ARM.com 2869046SAli.Saidi@ARM.com if (ret <= 0){ 2879046SAli.Saidi@ARM.com DPRINTF(VNC, "Read failed.\n"); 2889046SAli.Saidi@ARM.com detach(); 2899046SAli.Saidi@ARM.com return 0; 2909046SAli.Saidi@ARM.com } 2919046SAli.Saidi@ARM.com 2929046SAli.Saidi@ARM.com return ret; 2939046SAli.Saidi@ARM.com} 2949046SAli.Saidi@ARM.com 2951060SN/Asize_t 2961060SN/AVncServer::read1(uint8_t *buf, size_t len) 2971060SN/A{ 2981060SN/A size_t read_len M5_VAR_USED; 2991060SN/A read_len = read(buf + 1, len - 1); 3001060SN/A assert(read_len == len - 1); 3015358Sgblack@eecs.umich.edu return read_len; 3025358Sgblack@eecs.umich.edu} 3035358Sgblack@eecs.umich.edu 3045358Sgblack@eecs.umich.edu 3055358Sgblack@eecs.umich.edutemplate<typename T> 3065358Sgblack@eecs.umich.edusize_t 3075358Sgblack@eecs.umich.eduVncServer::read(T* val) 3085358Sgblack@eecs.umich.edu{ 3095358Sgblack@eecs.umich.edu return read((uint8_t*)val, sizeof(T)); 3105358Sgblack@eecs.umich.edu} 3115358Sgblack@eecs.umich.edu 3125358Sgblack@eecs.umich.edu// write to socket 3135358Sgblack@eecs.umich.edusize_t 3148444Sgblack@eecs.umich.eduVncServer::write(const uint8_t *buf, size_t len) 3157520Sgblack@eecs.umich.edu{ 3168444Sgblack@eecs.umich.edu if (dataFd < 0) 3178444Sgblack@eecs.umich.edu panic("Vnc client not properly attached.\n"); 3187520Sgblack@eecs.umich.edu 3196974Stjones1@inf.ed.ac.uk ssize_t ret; 3206974Stjones1@inf.ed.ac.uk ret = atomic_write(dataFd, buf, len); 3216974Stjones1@inf.ed.ac.uk 3226974Stjones1@inf.ed.ac.uk if (ret < len) 3236973Stjones1@inf.ed.ac.uk detach(); 3246974Stjones1@inf.ed.ac.uk 3256974Stjones1@inf.ed.ac.uk return ret; 3266973Stjones1@inf.ed.ac.uk} 3276973Stjones1@inf.ed.ac.uk 3286973Stjones1@inf.ed.ac.uktemplate<typename T> 3296973Stjones1@inf.ed.ac.uksize_t 3301060SN/AVncServer::write(T* val) 3317944SGiacomo.Gabrielli@arm.com{ 3329046SAli.Saidi@ARM.com return write((uint8_t*)val, sizeof(T)); 3339046SAli.Saidi@ARM.com} 3347944SGiacomo.Gabrielli@arm.com 3357944SGiacomo.Gabrielli@arm.comsize_t 3369046SAli.Saidi@ARM.comVncServer::write(const char* str) 3379046SAli.Saidi@ARM.com{ 3387944SGiacomo.Gabrielli@arm.com return write((uint8_t*)str, strlen(str)); 3398545Ssaidi@eecs.umich.edu} 3408545Ssaidi@eecs.umich.edu 3418545Ssaidi@eecs.umich.edu// detach a vnc client 3428545Ssaidi@eecs.umich.eduvoid 3438545Ssaidi@eecs.umich.eduVncServer::detach() 3449046SAli.Saidi@ARM.com{ 3459046SAli.Saidi@ARM.com if (dataFd != -1) { 3468545Ssaidi@eecs.umich.edu ::close(dataFd); 3478545Ssaidi@eecs.umich.edu dataFd = -1; 3488545Ssaidi@eecs.umich.edu } 3498545Ssaidi@eecs.umich.edu 3508545Ssaidi@eecs.umich.edu if (!dataEvent || !dataEvent->queued()) 3519046SAli.Saidi@ARM.com return; 3529046SAli.Saidi@ARM.com 3538545Ssaidi@eecs.umich.edu pollQueue.remove(dataEvent); 3547944SGiacomo.Gabrielli@arm.com delete dataEvent; 3557944SGiacomo.Gabrielli@arm.com dataEvent = NULL; 3567944SGiacomo.Gabrielli@arm.com curState = WaitForProtocolVersion; 3577944SGiacomo.Gabrielli@arm.com 3587944SGiacomo.Gabrielli@arm.com inform("VNC client detached\n"); 3597944SGiacomo.Gabrielli@arm.com DPRINTF(VNC, "detach vnc client %d\n", number); 3609046SAli.Saidi@ARM.com} 3617944SGiacomo.Gabrielli@arm.com 3627944SGiacomo.Gabrielli@arm.comvoid 3631060SN/AVncServer::sendError(const char* error_msg) 3642292SN/A{ 3652292SN/A uint32_t len = strlen(error_msg); 3662292SN/A write(&len); 3672292SN/A write(error_msg); 3683770Sgblack@eecs.umich.edu} 3693770Sgblack@eecs.umich.edu 3703770Sgblack@eecs.umich.eduvoid 3713770Sgblack@eecs.umich.eduVncServer::checkProtocolVersion() 3723770Sgblack@eecs.umich.edu{ 3733770Sgblack@eecs.umich.edu assert(curState == WaitForProtocolVersion); 3743770Sgblack@eecs.umich.edu 3753770Sgblack@eecs.umich.edu size_t len M5_VAR_USED; 3763770Sgblack@eecs.umich.edu char version_string[13]; 3773770Sgblack@eecs.umich.edu 3783770Sgblack@eecs.umich.edu // Null terminate the message so it's easier to work with 3799046SAli.Saidi@ARM.com version_string[12] = 0; 3803770Sgblack@eecs.umich.edu 3813770Sgblack@eecs.umich.edu len = read((uint8_t*)version_string, 12); 3823770Sgblack@eecs.umich.edu assert(len == 12); 3833770Sgblack@eecs.umich.edu 3843770Sgblack@eecs.umich.edu uint32_t major, minor; 3853770Sgblack@eecs.umich.edu 3863770Sgblack@eecs.umich.edu // Figure out the major/minor numbers 3873770Sgblack@eecs.umich.edu if (sscanf(version_string, "RFB %03d.%03d\n", &major, &minor) != 2) { 3883770Sgblack@eecs.umich.edu warn(" Malformed protocol version %s\n", version_string); 3893770Sgblack@eecs.umich.edu sendError("Malformed protocol version\n"); 3903770Sgblack@eecs.umich.edu detach(); 3913770Sgblack@eecs.umich.edu } 3923770Sgblack@eecs.umich.edu 3933770Sgblack@eecs.umich.edu DPRINTF(VNC, "Client request protocol version %d.%d\n", major, minor); 3943770Sgblack@eecs.umich.edu 3953770Sgblack@eecs.umich.edu // If it's not 3.X we don't support it 3963770Sgblack@eecs.umich.edu if (major != 3 || minor < 2) { 3973770Sgblack@eecs.umich.edu warn("Unsupported VNC client version... disconnecting\n"); 3983770Sgblack@eecs.umich.edu uint8_t err = AuthInvalid; 3993770Sgblack@eecs.umich.edu write(&err); 4003770Sgblack@eecs.umich.edu detach(); 4013770Sgblack@eecs.umich.edu } 4023770Sgblack@eecs.umich.edu // Auth is different based on version number 4033770Sgblack@eecs.umich.edu if (minor < 7) { 4043770Sgblack@eecs.umich.edu uint32_t sec_type = htobe((uint32_t)AuthNone); 4053770Sgblack@eecs.umich.edu write(&sec_type); 4063770Sgblack@eecs.umich.edu } else { 4073770Sgblack@eecs.umich.edu uint8_t sec_cnt = 1; 4083770Sgblack@eecs.umich.edu uint8_t sec_type = htobe((uint8_t)AuthNone); 4093770Sgblack@eecs.umich.edu write(&sec_cnt); 4103770Sgblack@eecs.umich.edu write(&sec_type); 4113770Sgblack@eecs.umich.edu } 4123770Sgblack@eecs.umich.edu 4133770Sgblack@eecs.umich.edu // Wait for client to respond 4143770Sgblack@eecs.umich.edu curState = WaitForSecurityResponse; 4153770Sgblack@eecs.umich.edu} 4163770Sgblack@eecs.umich.edu 4173770Sgblack@eecs.umich.eduvoid 4183770Sgblack@eecs.umich.eduVncServer::checkSecurity() 4193770Sgblack@eecs.umich.edu{ 4203770Sgblack@eecs.umich.edu assert(curState == WaitForSecurityResponse); 4213770Sgblack@eecs.umich.edu 4223770Sgblack@eecs.umich.edu uint8_t security_type; 4233770Sgblack@eecs.umich.edu size_t len M5_VAR_USED = read(&security_type); 4243770Sgblack@eecs.umich.edu 4253770Sgblack@eecs.umich.edu assert(len == 1); 4264636Sgblack@eecs.umich.edu 4274636Sgblack@eecs.umich.edu if (security_type != AuthNone) { 4287720Sgblack@eecs.umich.edu warn("Unknown VNC security type\n"); 4297720Sgblack@eecs.umich.edu sendError("Unknown security type\n"); 4304636Sgblack@eecs.umich.edu } 4314636Sgblack@eecs.umich.edu 4324636Sgblack@eecs.umich.edu DPRINTF(VNC, "Sending security auth OK\n"); 4338502Sgblack@eecs.umich.edu 4348502Sgblack@eecs.umich.edu uint32_t success = htobe(VncOK); 4358502Sgblack@eecs.umich.edu write(&success); 4363770Sgblack@eecs.umich.edu curState = WaitForClientInit; 4372292SN/A} 4382292SN/A 4392292SN/Avoid 4408502Sgblack@eecs.umich.eduVncServer::sendServerInit() 4411060SN/A{ 4421060SN/A ServerInitMsg msg; 4431060SN/A 4441060SN/A DPRINTF(VNC, "Sending server init message to client\n"); 4451464SN/A 4461684SN/A msg.fbWidth = htobe(videoWidth()); 4471464SN/A msg.fbHeight = htobe(videoHeight()); 4481060SN/A 4491464SN/A msg.px.bpp = htobe(pixelFormat.bpp); 4501060SN/A msg.px.depth = htobe(pixelFormat.depth); 4511060SN/A msg.px.bigendian = htobe(pixelFormat.bigendian); 4521060SN/A msg.px.truecolor = htobe(pixelFormat.truecolor); 4531060SN/A msg.px.redmax = htobe(pixelFormat.redmax); 4541060SN/A msg.px.greenmax = htobe(pixelFormat.greenmax); 4551060SN/A msg.px.bluemax = htobe(pixelFormat.bluemax); 4563326Sktlim@umich.edu msg.px.redshift = htobe(pixelFormat.redshift); 45710110Sandreas.hansson@arm.com msg.px.greenshift = htobe(pixelFormat.greenshift); 4583326Sktlim@umich.edu msg.px.blueshift = htobe(pixelFormat.blueshift); 4598832SAli.Saidi@ARM.com memset(msg.px.padding, 0, 3); 46010110Sandreas.hansson@arm.com msg.namelen = 2; 4618832SAli.Saidi@ARM.com msg.namelen = htobe(msg.namelen); 4625714Shsul@eecs.umich.edu memcpy(msg.name, "M5", 2); 46310110Sandreas.hansson@arm.com 4645714Shsul@eecs.umich.edu write(&msg); 4651060SN/A curState = NormalPhase; 46610110Sandreas.hansson@arm.com} 4671060SN/A 4681060SN/A 4691060SN/Avoid 4701060SN/AVncServer::setPixelFormat() 4712292SN/A{ 4721060SN/A DPRINTF(VNC, "Received pixel format from client message\n"); 4731060SN/A 4741060SN/A PixelFormatMessage pfm; 4757720Sgblack@eecs.umich.edu read1((uint8_t*)&pfm, sizeof(PixelFormatMessage)); 4767720Sgblack@eecs.umich.edu 4773965Sgblack@eecs.umich.edu DPRINTF(VNC, " -- bpp = %d; depth = %d; be = %d\n", pfm.px.bpp, 4787720Sgblack@eecs.umich.edu pfm.px.depth, pfm.px.bigendian); 4793965Sgblack@eecs.umich.edu DPRINTF(VNC, " -- true color = %d red,green,blue max = %d,%d,%d\n", 4802935Sksewell@umich.edu pfm.px.truecolor, betoh(pfm.px.redmax), betoh(pfm.px.greenmax), 4817720Sgblack@eecs.umich.edu betoh(pfm.px.bluemax)); 4821060SN/A DPRINTF(VNC, " -- red,green,blue shift = %d,%d,%d\n", pfm.px.redshift, 4833794Sgblack@eecs.umich.edu pfm.px.greenshift, pfm.px.blueshift); 4847720Sgblack@eecs.umich.edu 4853794Sgblack@eecs.umich.edu if (betoh(pfm.px.bpp) != pixelFormat.bpp || 4863794Sgblack@eecs.umich.edu betoh(pfm.px.depth) != pixelFormat.depth || 4877720Sgblack@eecs.umich.edu betoh(pfm.px.bigendian) != pixelFormat.bigendian || 4881060SN/A betoh(pfm.px.truecolor) != pixelFormat.truecolor || 4894636Sgblack@eecs.umich.edu betoh(pfm.px.redmax) != pixelFormat.redmax || 4907720Sgblack@eecs.umich.edu betoh(pfm.px.greenmax) != pixelFormat.greenmax || 4914636Sgblack@eecs.umich.edu betoh(pfm.px.bluemax) != pixelFormat.bluemax || 4921060SN/A betoh(pfm.px.redshift) != pixelFormat.redshift || 4933794Sgblack@eecs.umich.edu betoh(pfm.px.greenshift) != pixelFormat.greenshift || 4943794Sgblack@eecs.umich.edu betoh(pfm.px.blueshift) != pixelFormat.blueshift) 4959046SAli.Saidi@ARM.com fatal("VNC client doesn't support true color raw encoding\n"); 4963794Sgblack@eecs.umich.edu} 4973794Sgblack@eecs.umich.edu 4983794Sgblack@eecs.umich.eduvoid 4993794Sgblack@eecs.umich.eduVncServer::setEncodings() 5009046SAli.Saidi@ARM.com{ 5013794Sgblack@eecs.umich.edu DPRINTF(VNC, "Received supported encodings from client\n"); 5021060SN/A 5031060SN/A PixelEncodingsMessage pem; 5042935Sksewell@umich.edu read1((uint8_t*)&pem, sizeof(PixelEncodingsMessage)); 5053794Sgblack@eecs.umich.edu 5067720Sgblack@eecs.umich.edu pem.num_encodings = betoh(pem.num_encodings); 5077720Sgblack@eecs.umich.edu 5087720Sgblack@eecs.umich.edu DPRINTF(VNC, " -- %d encoding present\n", pem.num_encodings); 5093794Sgblack@eecs.umich.edu supportsRawEnc = supportsResizeEnc = false; 5103794Sgblack@eecs.umich.edu 5111060SN/A for (int x = 0; x < pem.num_encodings; x++) { 5121060SN/A int32_t encoding; 5131060SN/A size_t len M5_VAR_USED; 5145543Ssaidi@eecs.umich.edu len = read(&encoding); 5155543Ssaidi@eecs.umich.edu assert(len == sizeof(encoding)); 5165543Ssaidi@eecs.umich.edu DPRINTF(VNC, " -- supports %d\n", betoh(encoding)); 5175543Ssaidi@eecs.umich.edu 5182336SN/A switch (betoh(encoding)) { 5192336SN/A case EncodingRaw: 5201060SN/A supportsRawEnc = true; 5211060SN/A break; 5225543Ssaidi@eecs.umich.edu case EncodingDesktopSize: 5235543Ssaidi@eecs.umich.edu supportsResizeEnc = true; 5245543Ssaidi@eecs.umich.edu break; 5255543Ssaidi@eecs.umich.edu } 5265543Ssaidi@eecs.umich.edu } 5275543Ssaidi@eecs.umich.edu 5281060SN/A if (!supportsRawEnc) 5295543Ssaidi@eecs.umich.edu fatal("VNC clients must always support raw encoding\n"); 5305543Ssaidi@eecs.umich.edu} 5312935Sksewell@umich.edu 5321060SN/Avoid 5331060SN/AVncServer::requestFbUpdate() 5342292SN/A{ 5352731Sktlim@umich.edu DPRINTF(VNC, "Received frame buffer update request from client\n"); 5362292SN/A 5372731Sktlim@umich.edu FrameBufferUpdateReq fbr; 5387784SAli.Saidi@ARM.com read1((uint8_t*)&fbr, sizeof(FrameBufferUpdateReq)); 5391060SN/A 5401060SN/A fbr.x = betoh(fbr.x); 5411060SN/A fbr.y = betoh(fbr.y); 5422292SN/A fbr.width = betoh(fbr.width); 5432336SN/A fbr.height = betoh(fbr.height); 5442308SN/A 5454828Sgblack@eecs.umich.edu DPRINTF(VNC, " -- x = %d y = %d w = %d h = %d\n", fbr.x, fbr.y, fbr.width, 5464654Sgblack@eecs.umich.edu fbr.height); 5474654Sgblack@eecs.umich.edu 5484636Sgblack@eecs.umich.edu sendFrameBufferUpdate(); 5494654Sgblack@eecs.umich.edu} 5504654Sgblack@eecs.umich.edu 5514636Sgblack@eecs.umich.eduvoid 5522292SN/AVncServer::recvKeyboardInput() 5532292SN/A{ 5542731Sktlim@umich.edu DPRINTF(VNC, "Received keyboard input from client\n"); 5552292SN/A KeyEventMessage kem; 5562292SN/A read1((uint8_t*)&kem, sizeof(KeyEventMessage)); 5572731Sktlim@umich.edu 5582292SN/A kem.key = betoh(kem.key); 5592292SN/A DPRINTF(VNC, " -- received key code %d (%s)\n", kem.key, kem.down_flag ? 5602731Sktlim@umich.edu "down" : "up"); 5612292SN/A 5622292SN/A if (keyboard) 5632731Sktlim@umich.edu keyboard->keyPress(kem.key, kem.down_flag); 5642292SN/A} 5652292SN/A 5662731Sktlim@umich.eduvoid 5672292SN/AVncServer::recvPointerInput() 5682292SN/A{ 5692731Sktlim@umich.edu DPRINTF(VNC, "Received pointer input from client\n"); 5702292SN/A PointerEventMessage pem; 5712731Sktlim@umich.edu 5722731Sktlim@umich.edu read1((uint8_t*)&pem, sizeof(PointerEventMessage));; 5732292SN/A 5742292SN/A pem.x = betoh(pem.x); 5752292SN/A pem.y = betoh(pem.y); 5762292SN/A DPRINTF(VNC, " -- pointer at x = %d y = %d buttons = %#x\n", pem.x, pem.y, 5772292SN/A pem.button_mask); 5782292SN/A 5792731Sktlim@umich.edu if (mouse) 5801060SN/A mouse->mouseAt(pem.x, pem.y, pem.button_mask); 5811464SN/A} 5821464SN/A 5831464SN/Avoid 5841464SN/AVncServer::recvCutText() 5857720Sgblack@eecs.umich.edu{ 5867720Sgblack@eecs.umich.edu DPRINTF(VNC, "Received client copy buffer message\n"); 5871464SN/A 5882292SN/A ClientCutTextMessage cct; 5895543Ssaidi@eecs.umich.edu read1((uint8_t*)&cct, sizeof(ClientCutTextMessage)); 5901684SN/A 5912292SN/A char str[1025]; 5921060SN/A size_t data_len = betoh(cct.length); 5931060SN/A DPRINTF(VNC, "String length %d\n", data_len); 5941060SN/A while (data_len > 0) { 5951060SN/A size_t len; 5961060SN/A size_t bytes_to_read = data_len > 1024 ? 1024 : data_len; 5971060SN/A len = read((uint8_t*)&str, bytes_to_read); 5981060SN/A str[bytes_to_read] = 0; 5991060SN/A data_len -= len; 6002292SN/A assert(data_len >= 0); 6011060SN/A DPRINTF(VNC, "Buffer: %s\n", str); 6021060SN/A } 6032292SN/A 6041060SN/A} 6058733Sgeoffrey.blake@arm.com 6068733Sgeoffrey.blake@arm.com 6078733Sgeoffrey.blake@arm.comvoid 6088733Sgeoffrey.blake@arm.comVncServer::sendFrameBufferUpdate() 6098733Sgeoffrey.blake@arm.com{ 6108733Sgeoffrey.blake@arm.com 6118733Sgeoffrey.blake@arm.com if (!clientRfb || dataFd <= 0 || curState != NormalPhase || !sendUpdate) { 6128733Sgeoffrey.blake@arm.com DPRINTF(VNC, "NOT sending framebuffer update\n"); 6138733Sgeoffrey.blake@arm.com return; 6141684SN/A } 6158733Sgeoffrey.blake@arm.com 6168733Sgeoffrey.blake@arm.com assert(vc); 6178733Sgeoffrey.blake@arm.com 6188733Sgeoffrey.blake@arm.com // The client will request data constantly, unless we throttle it 6198733Sgeoffrey.blake@arm.com sendUpdate = false; 6208733Sgeoffrey.blake@arm.com 6211684SN/A DPRINTF(VNC, "Sending framebuffer update\n"); 6228733Sgeoffrey.blake@arm.com 6238733Sgeoffrey.blake@arm.com FrameBufferUpdate fbu; 6248733Sgeoffrey.blake@arm.com FrameBufferRect fbr; 6258733Sgeoffrey.blake@arm.com 6269046SAli.Saidi@ARM.com fbu.type = ServerFrameBufferUpdate; 6278733Sgeoffrey.blake@arm.com fbu.num_rects = 1; 6288733Sgeoffrey.blake@arm.com fbr.x = 0; 6298733Sgeoffrey.blake@arm.com fbr.y = 0; 6308733Sgeoffrey.blake@arm.com fbr.width = videoWidth(); 6318733Sgeoffrey.blake@arm.com fbr.height = videoHeight(); 6321060SN/A fbr.encoding = EncodingRaw; 6332702Sktlim@umich.edu 6343735Sstever@eecs.umich.edu // fix up endian 6351060SN/A fbu.num_rects = htobe(fbu.num_rects); 6368733Sgeoffrey.blake@arm.com fbr.x = htobe(fbr.x); 6371060SN/A fbr.y = htobe(fbr.y); 6381060SN/A fbr.width = htobe(fbr.width); 6399920Syasuko.eckert@amd.com fbr.height = htobe(fbr.height); 6409920Syasuko.eckert@amd.com fbr.encoding = htobe(fbr.encoding); 6419920Syasuko.eckert@amd.com 6429920Syasuko.eckert@amd.com // send headers to client 6439920Syasuko.eckert@amd.com write(&fbu); 6449920Syasuko.eckert@amd.com write(&fbr); 6452702Sktlim@umich.edu 6463735Sstever@eecs.umich.edu assert(clientRfb); 6473735Sstever@eecs.umich.edu 6482690Sktlim@umich.edu uint8_t *tmp = vc->convert(clientRfb); 6498733Sgeoffrey.blake@arm.com write(tmp, videoWidth() * videoHeight() * sizeof(uint32_t)); 6508733Sgeoffrey.blake@arm.com delete [] tmp; 6518733Sgeoffrey.blake@arm.com 6528733Sgeoffrey.blake@arm.com} 6533326Sktlim@umich.edu 6542690Sktlim@umich.eduvoid 6552690Sktlim@umich.eduVncServer::sendFrameBufferResized() 6562702Sktlim@umich.edu{ 6573735Sstever@eecs.umich.edu assert(clientRfb && dataFd > 0 && curState == NormalPhase); 6581060SN/A DPRINTF(VNC, "Sending framebuffer resize\n"); 6598733Sgeoffrey.blake@arm.com 6602308SN/A FrameBufferUpdate fbu; 6611060SN/A FrameBufferRect fbr; 6622702Sktlim@umich.edu 6633735Sstever@eecs.umich.edu fbu.type = ServerFrameBufferUpdate; 6643735Sstever@eecs.umich.edu fbu.num_rects = 1; 6652308SN/A fbr.x = 0; 6668733Sgeoffrey.blake@arm.com fbr.y = 0; 6672308SN/A fbr.width = videoWidth(); 6681060SN/A fbr.height = videoHeight(); 6692702Sktlim@umich.edu fbr.encoding = EncodingDesktopSize; 6703735Sstever@eecs.umich.edu 6712308SN/A // fix up endian 6728733Sgeoffrey.blake@arm.com fbu.num_rects = htobe(fbu.num_rects); 6731060SN/A fbr.x = htobe(fbr.x); 6741060SN/A fbr.y = htobe(fbr.y); 6752190SN/A fbr.width = htobe(fbr.width); 6762292SN/A fbr.height = htobe(fbr.height); 6772190SN/A fbr.encoding = htobe(fbr.encoding); 6782331SN/A 6792292SN/A // send headers to client 6802190SN/A write(&fbu); 6811684SN/A write(&fbr); 6821464SN/A 6831464SN/A // No actual data is sent in this message 6841464SN/A} 6851464SN/A 6861464SN/Avoid 6871684SN/AVncServer::setFrameBufferParams(VideoConvert::Mode mode, int width, int height) 6882731Sktlim@umich.edu{ 6891464SN/A DPRINTF(VNC, "Updating video params: mode: %d width: %d height: %d\n", mode, 6902292SN/A width, height); 6912731Sktlim@umich.edu 6921464SN/A if (mode != videoMode || width != videoWidth() || height != videoHeight()) { 6932731Sktlim@umich.edu videoMode = mode; 6942731Sktlim@umich.edu _videoWidth = width; 6952308SN/A _videoHeight = height; 6962731Sktlim@umich.edu 6972731Sktlim@umich.edu if (vc) 6982308SN/A delete vc; 6991060SN/A 7002731Sktlim@umich.edu vc = new VideoConvert(mode, VideoConvert::rgb8888, videoWidth(), 7011060SN/A videoHeight()); 7021060SN/A 7032731Sktlim@umich.edu if (captureEnabled) { 7041060SN/A // create bitmap of the frame with new attributes 7054032Sktlim@umich.edu if (captureBitmap) 7064032Sktlim@umich.edu delete captureBitmap; 7074032Sktlim@umich.edu 7081060SN/A assert(clientRfb); 7092731Sktlim@umich.edu captureBitmap = new Bitmap(videoMode, width, height, clientRfb); 7101060SN/A assert(captureBitmap); 7111060SN/A } 7122731Sktlim@umich.edu 7131060SN/A if (dataFd > 0 && clientRfb && curState == NormalPhase) { 7144032Sktlim@umich.edu if (supportsResizeEnc) 7154032Sktlim@umich.edu sendFrameBufferResized(); 7164032Sktlim@umich.edu else 7171060SN/A // The frame buffer changed size and we can't update the client 7182731Sktlim@umich.edu detach(); 7191060SN/A } 7201060SN/A } 7212731Sktlim@umich.edu} 7221060SN/A 7231060SN/A// create the VNC server object 7242731Sktlim@umich.eduVncServer * 7251060SN/AVncServerParams::create() 7261061SN/A{ 7272731Sktlim@umich.edu return new VncServer(this); 7281061SN/A} 7291060SN/A 7302731Sktlim@umich.eduvoid 7312731Sktlim@umich.eduVncServer::captureFrameBuffer() 7322731Sktlim@umich.edu{ 7332731Sktlim@umich.edu assert(captureBitmap); 7342731Sktlim@umich.edu 7351060SN/A // skip identical frames 7362292SN/A uint64_t new_hash = captureBitmap->getHash(); 7372731Sktlim@umich.edu if (captureLastHash == new_hash) 7382292SN/A return; 7392292SN/A captureLastHash = new_hash; 7402731Sktlim@umich.edu 7412292SN/A // get the filename for the current frame 7421060SN/A char frameFilenameBuffer[64]; 7432731Sktlim@umich.edu snprintf(frameFilenameBuffer, 64, "fb.%06d.%lld.bmp.gz", 7441060SN/A captureCurrentFrame, static_cast<long long int>(curTick())); 7451060SN/A const string frameFilename(frameFilenameBuffer); 7462731Sktlim@umich.edu 7471060SN/A // create the compressed framebuffer file 7482292SN/A ostream *fb_out = simout.create(captureOutputDirectory + frameFilename, 7492292SN/A true); 7502292SN/A captureBitmap->write(fb_out); 7512731Sktlim@umich.edu simout.close(fb_out); 7522292SN/A 7532292SN/A ++captureCurrentFrame; 7542731Sktlim@umich.edu} 7552731Sktlim@umich.edu