I've been doing some research on adding a proper validating demarshaller to spice. What i mean by this is a piece of code that parses the bytes recieved from the network, validates that it is a proper spice message and then generates an internal structure describing the request.
Right now we more or less read packed structures from the network and push validation out into any place that uses it. This has multiple issues. For instance the structs are not aligneds properly for performance, the validation is hard to review, its hard to extend messages (add to the structs) while being backwards compatible. I have constructed a simple language to describe the current spice protocol. Its attached to this mail, please report any strangeness you find. I have also written a parser in python for this language, and I plan to write a code generator based on this that can generate validating (de)marshaller code which we will use in spice. The plan to get this into spice is then this: 1. Make the code use the generated (de)marshallers to marshal the network data from/into the current spice structures. 2. Split all current structures used in both the Qxl pci commands and for network marshalling into two copies, one QxlFoo used for qxl and one SpiceFoo used internally. 3. Now no external entity depends on the spice structs, so we can change them as we please into regular non-packed structs with pointers instead of offsets, etc. 4. Make a copy of the spice protocol and use it for a new (de)marshaller with major number == 2, picking implementation in the client based on the server major number. 5. Start modifying the protocol description for the new major. I have a bunch of protocol changes I want to do in the new major. For instance, on the network all "offsets" in the current protocol can be removed and instead we can put the relevant data inline. We can also make parts of messages totally optional if not used (like clip region) such that we send no data if they are not used. I also want to simplify some the stroke operation and remove the clip-by-path mode. Additionally there are some changes i want to do to the internal structures. For instance, SpicePoint members should be reordered to be compatible with pixman points, and Clip regions should always be stored as pointers to pixman regions. -- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Alexander Larsson Red Hat, Inc al...@redhat.com alexander.lars...@gmail.com He's a notorious alcoholic jungle king who must take medication to keep him sane. She's a strong-willed mute former first lady who believes she is the reincarnation of an ancient Egyptian queen. They fight crime!
/* built in types: int8, uint8, 16, 32, 64 */ typedef message_offset uint64; typedef fixed28_4 int32; struct Point { int32 x; int32 y; }; struct Point16 { int16 x; int16 y; }; struct PointFix { fixed28_4 x; fixed28_4 y; }; struct Rect { int32 top; int32 left; int32 bottom; int32 right; }; enum32 error_code { ERROR_OK, ERROR_ERROR, ERROR_INVALID_MAGIC, ERROR_INVALID_DATA, ERROR_VERSION_MISMATCH, ERROR_NEED_SECURED, ERROR_NEED_UNSECURED, ERROR_PERMISSION_DENIED, ERROR_BAD_CONNECTION_ID, ERROR_CHANNEL_NOT_AVAILABLE }; enum32 warn_code { WARN_GENERAL }; enum32 info_code { INFO_GENERAL }; flags32 migrate_flags { MIGRATE_NEED_FLUSH, MIGRATE_NEED_DATA_TRANSFER, }; enum32 notify_severity { NOTIFY_SEVERITY_INFO, NOTIFY_SEVERITY_WARN, NOTIFY_SEVERITY_ERROR, }; enum32 notify_visibility { NOTIFY_VISIBILITY_LOW, NOTIFY_VISIBILITY_MEDIUM, NOTIFY_VISIBILITY_HIGH, }; flags32 mouse_mode { MOUSE_MODE_SERVER, MOUSE_MODE_CLIENT, }; message EmptyMessage { }; message DataMessage { uint8 data[]; }; channel BaseChannel { server: message { migrate_flags flags; } migrate; DataMessage migrate_data; message { uint32 generation; uint32 window; } set_ack; message { uint32 id; uint64 timestamp; uint8 data[]; } ping; message { uint8 wait_count; struct { uint8 channel_type; uint8 channel_id; uint64 message_serial; } wait_list[wait_count]; } wait_for_channels; message { uint64 time_stamp; error_code reason; } disconnecting; message { uint64 time_stamp; notify_severity severty; notify_visibility visibilty; uint32 what; /* error_code/warn_code/info_code */ uint32 message_len; uint8 message[message_len]; uint8 zero; /* Must be zero for validation! */ } notify; client: message { uint32 generation; } ack_sync; EmptyMessage ack; EmptyMessage migrate_flush_mark; DataMessage migrate_data; message { uint64 time_stamp; error_code reason; } disconnecting; }; struct ChannelId { uint8 type; uint8 id; }; channel MainChannel : BaseChannel { server: message { uint16 port; uint16 sport; uint32 host_offset; uint32 host_size; uint16 pub_key_type @minor(2); uint32 pub_key_offset @minor(2); uint32 pub_key_size @minor(2); } migrate_begin = 101; message { } migrate_cancel; message { uint32 session_id; uint32 display_channels_hint; uint32 supported_mouse_modes; uint32 current_mouse_mode; uint32 agent_connected; uint32 agent_tokens; uint32 multi_media_time; uint32 ram_hint; } init; message { uint32 num_of_channels; ChannelId channels[num_of_channels]; } channels_list; message { mouse_mode supported_modes; mouse_mode current_mode @unique_flag; } mouse_mode; message { uint32 time; } multimedia_time; EmptyMessage agent_connected; message { error_code reason; } agent_disconnected; DataMessage agent_data; message { uint32 num_tokens; } agent_token; client: message { uint64 cache_size; } client_info = 101; EmptyMessage migrate_connected; EmptyMessage migrate_connect_error; EmptyMessage attach_channels; message { mouse_mode mode; } mouse_mode_request; message { uint32 num_tokens; } agent_start; DataMessage agent_data; message { uint32 num_tokens; } agent_token; }; enum32 clip_type { CLIP_TYPE_NONE, CLIP_TYPE_RECTS, CLIP_TYPE_PATH, }; enum32 path_flags { PATH_BEGIN = 0, PATH_END = 1, PATH_CLOSE = 3, PATH_BEZIER = 4, }; enum32 video_codec_type { VIDEO_CODEC_TYPE_MJPEG = 1, }; enum32 stream_flags { STREAM_FLAGS_TOP_DOWN = 0, }; enum32 brush_type { BRUSH_TYPE_NONE, BRUSH_TYPE_SOLID, BRUSH_TYPE_PATTERN, }; flags8 mask_flags { MASK_FLAGS_INVERS, }; enum8 image_type { IMAGE_TYPE_BITMAP, IMAGE_TYPE_QUIC, IMAGE_TYPE_RESERVED, IMAGE_TYPE_LZ_PLT = 100, IMAGE_TYPE_LZ_RGB, IMAGE_TYPE_GLZ_RGB, IMAGE_TYPE_FROM_CACHE, }; flags8 image_flags { IMAGE_FLAGS_CACHE_ME, }; flags8 bitmap_format { BITMAP_FMT_INVALID, BITMAP_FMT_1BIT_LE, BITMAP_FMT_1BIT_BE, BITMAP_FMT_4BIT_LE, BITMAP_FMT_4BIT_BE, BITMAP_FMT_8BIT, BITMAP_FMT_16BIT, /* 0555 mode */ BITMAP_FMT_24BIT, BITMAP_FMT_32BIT, BITMAP_FMT_RGBA, }; flags8 bitmap_flags { BITMAP_FLAGS_PAL_CACHE_ME, BITMAP_FLAGS_PAL_FROM_CACHE, BITMAP_FLAGS_TOP_DOWN, }; enum8 scale_mode { IMAGE_SCALE_MODE_INTERPOLATE, IMAGE_SCALE_MODE_NEAREST, }; flags16 rop_descriptor { ROPD_INVERS_SRC, ROPD_INVERS_BRUSH, ROPD_INVERS_DEST, ROPD_OP_PUT, ROPD_OP_OR, ROPD_OP_AND, ROPD_OP_XOR, ROPD_OP_BLACKNESS, ROPD_OP_WHITENESS, ROPD_OP_INVERS, ROPD_INVERS_RES, }; flags8 line_flags { LINE_FLAGS_STYLED = 3, LINE_FLAGS_START_WITH_GAP = 2, }; enum8 line_cap_style { LINE_CAP_ROUND, LINE_CAP_SQUARE, LINE_CAP_BUTT, }; enum8 line_join_style { LINE_JOIN_ROUND, LINE_JOIN_BEVEL, LINE_JOIN_MITER, }; flags16 string_flags { STRING_FLAGS_RASTER_A1, STRING_FLAGS_RASTER_A4, STRING_FLAGS_RASTER_A8, STRING_FLAGS_RASTER_TOP_DOWN, }; struct ClipRects { uint32 num_rects; Rect rects[num_rects]; }; struct PathSegment { path_flags flags; uint32 count; fixed28_4 points[count]; }; struct Path { uint32 count; PathSegment segments[count]; }; struct Clip { clip_type type; switch (type) { case CLIP_TYPE_RECTS: ClipRects *rects; case CLIP_TYPE_PATH: Path *data; } u; }; struct DisplayBase { Rect box; Clip clip; }; struct ResorceID { uint8 type; uint64 id; }; struct WaitForChannel { uint8 channel_type; uint8 channel_id; uint64 message_serial; }; struct Palette { uint64 unique; uint16 num_ents; uint32 ents[num_ents]; }; struct BitmapData { bitmap_format format; bitmap_flags flags; uint32 x; uint32 y; uint32 stride; Palette *palette; uint8 *data[image_size(8, stride, y)] @nocopy; /* pointer to array, not array of pointers as in C */ }; struct BinaryData { uint32 data_size; uint8 data[data_size] @nocopy; }; struct LZPLTData { bitmap_flags flags; uint32 data_size; Palette *palette; uint8 data[data_size] @nocopy; }; struct Image { uint64 id; image_type type; image_flags flags; uint32 width; uint32 height; switch (image_type) { case IMAGE_TYPE_BITMAP: BitmapData bitmap_data; case IMAGE_TYPE_QUIC: case IMAGE_TYPE_LZ_RGB: case IMAGE_TYPE_GLZ_RGB: BinaryData binary_data; case IMAGE_TYPE_LZ_PLT: LZPLTData lzplt_data; } u; }; struct Pattern { Image *pat; Point pos; }; struct Brush { uint32 type; switch (type) { case BRUSH_TYPE_SOLID: uint32 color; case BRUSH_TYPE_PATTERN: Pattern pattern; } u; }; struct QMask { mask_flags flags; Point pos; Image *bitmap; }; struct LineAttr { line_flags flags; line_join_style join_style; line_cap_style end_style; uint8 style_nseg; fixed28_4 width; fixed28_4 miter_limit; fixed28_4 *style[style_nseg]; }; struct RasterGlyphA1 { Point render_pos; Point glyph_origin; uint16 width; uint16 height; uint8 data[image_size(1, width, height)] @nocopy; }; struct RasterGlyphA4 { Point render_pos; Point glyph_origin; uint16 width; uint16 height; uint8 data[image_size(4, width, height)] @nocopy; }; struct RasterGlyphA8 { Point render_pos; Point glyph_origin; uint16 width; uint16 height; uint8 data[image_size(8, width, height)] @nocopy; }; struct String { uint16 length; string_flags flags; /* Special: Only one of a1/a4/a8 set */ switch (flags) { case SPICE_STRING_FLAGS_RASTER_A1: RasterGlyphA1 glyphs[length]; case SPICE_STRING_FLAGS_RASTER_A4: RasterGlyphA4 glyphs[length]; case SPICE_STRING_FLAGS_RASTER_A8: RasterGlyphA8 glyphs[length]; } u; }; channel DisplayChannel : BaseChannel { server: message { uint32 x_res; uint32 y_res; uint32 bits; } mode = 101; EmptyMessage mark; EmptyMessage reset; message { DisplayBase base; Point src_pos; } copy_bits; message { uint16 count; ResorceID resorces[count]; } inval_list; message { uint8 wait_count; WaitForChannel wait_list[wait_count]; } inval_all_pixmaps; message { uint64 id; } inval_palette; EmptyMessage inval_all_palettes; message { uint32 id; stream_flags flags; video_codec_type codec_type; uint64 stamp; uint32 stream_width; uint32 stream_height; uint32 src_width; uint32 src_height; Rect dest; Clip clip; } stream_create = 122; message { uint32 id; uint32 multi_media_time; uint32 data_size; uint32 pad_size; uint8 data[data_size]; uint8 padding[pad_size]; /* Uhm, why are we sending padding over network? */ } stream_data; message { uint32 id; Clip clip; } stream_clip; message { uint32 id; } stream_destroy; EmptyMessage stream_destroy_all; message { DisplayBase base; Brush brush; uint16 rop_decriptor; QMask mask; } draw_fill = 302; message { DisplayBase base; Image *src_bitmap; Rect src_area; Brush brush; rop_descriptor rop_decriptor; scale_mode scale_mode; QMask mask; } draw_opaque; message { DisplayBase base; Image *src_bitmap; Rect src_area; rop_descriptor rop_decriptor; scale_mode scale_mode; QMask mask; } draw_copy; message { DisplayBase base; Image *src_bitmap; Rect src_area; rop_descriptor rop_decriptor; scale_mode scale_mode; QMask mask; } draw_blend; message { DisplayBase base; QMask mask; } draw_blackness; message { DisplayBase base; QMask mask; } draw_whiteness; message { DisplayBase base; QMask mask; } draw_invers; message { DisplayBase base; Image *src_bitmap; Rect src_area; Brush brush; uint8 rop3; scale_mode scale_mode; QMask mask; } draw_rop3; message { DisplayBase base; Path *path; LineAttr attr; Brush brush; uint16 fore_mode; uint16 back_mode; } draw_stroke; message { DisplayBase base; String *str; Rect back_area; Brush fore_brush; Brush back_brush; uint16 fore_mode; uint16 back_mode; } draw_text; message { DisplayBase base; Image *src_bitmap; Rect src_area; uint32 src_color; uint32 true_color; } draw_transparent; message { DisplayBase base; uint8 alpha; Image *src_bitmap; Rect src_area; } draw_alpha_blend; client: message { uint8 pixmap_cache_id; int64 pixmap_cache_size; //in pixels uint8 glz_dictionary_id; int32 glz_dictionary_window_size; // in pixels } init = 101; }; flags32 keyboard_modifier_flags { SCROLL_LOCK_MODIFIER, NUM_LOCK_MODIFIER, CAPS_LOCK_MODIFIER }; enum32 mouse_button { MOUSE_BUTTON_INVALID, MOUSE_BUTTON_LEFT, MOUSE_BUTTON_MIDDLE, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_UP, MOUSE_BUTTON_DOWN, }; flags32 mouse_button_mask { MOUSE_BUTTON_MASK_LEFT, MOUSE_BUTTON_MASK_MIDDLE, MOUSE_BUTTON_MASK_RIGHT }; channel InputsChannel : BaseChannel { client: message { uint32 code; } key_down = 101; message { uint32 code; } key_up; message { keyboard_modifier_flags keyboard_modifiers; } key_modifiers; message { int32 dx; int32 dy; mouse_button_mask buttons_state; } mouse_motion = 111; message { uint32 x; uint32 y; mouse_button_mask buttons_state; uint8 display_id; } mouse_position; message { mouse_button button; mouse_button_mask buttons_state; } mouse_press; message { mouse_button button; mouse_button_mask buttons_state; } mouse_release; server: message { keyboard_modifier_flags keyboard_modifiers; } init = 101; message { keyboard_modifier_flags keyboard_modifiers; } key_modifiers; EmptyMessage mouse_motion_ack = 111; }; enum16 cursor_type { CURSOR_TYPE_ALPHA, CURSOR_TYPE_MONO, CURSOR_TYPE_COLOR4, CURSOR_TYPE_COLOR8, CURSOR_TYPE_COLOR16, CURSOR_TYPE_COLOR24, CURSOR_TYPE_COLOR32, }; enum32 cursor_flags { CURSOR_FLAGS_NONE, /* Eh? why is this a non-zero flag? */ CURSOR_FLAGS_CACHE_ME, CURSOR_FLAGS_FROM_CACHE, }; struct CursorHeader { uint64 unique; cursor_type type; uint16 width; uint16 height; uint16 hot_spot_x; uint16 hot_spot_y; }; struct Cursor { cursor_flags flags; CursorHeader header; uint8 data[]; }; channel CursorChannel : BaseChannel { server: message { Point16 position; uint16 trail_length; uint16 trail_frequency; uint8 visible; Cursor cursor; } init = 101; EmptyMessage reset; message { Point16 position; uint8 visible; Cursor cursor; } set; message { Point16 postition; } move; EmptyMessage hide; message { uint16 length; uint16 frequency; } trail; message { uint64 id; } inval_one; EmptyMessage inval_all; }; enum32 audio_data_mode { AUDIO_DATA_MODE_INVALD, AUDIO_DATA_MODE_RAW, AUDIO_DATA_MODE_CELT_0_5_1, }; enum32 audio_format { AUDIO_FMT_INVALD, AUDIO_FMT_S16, }; channel PlaybackChannel : BaseChannel { server: message { uint32 time; uint8 data[]; } data = 101; message { uint32 time; audio_data_mode mode; uint8 data[]; } mode; message { uint32 channels; audio_format format; uint32 frequency; uint32 time; } start; EmptyMessage stop; }; channel RecordChannel : BaseChannel { server: message { uint32 channels; audio_format format; uint32 frequency; } start = 101; EmptyMessage stop; client: message { uint32 time; uint8 data[]; } data = 101; message { uint32 time; audio_data_mode mode; uint8 data[]; } mode; message { uint32 time; } start_mark; }; enum32 service_type { TUNNEL_SERVICE_TYPE_INVALID, TUNNEL_SERVICE_TYPE_GENERIC, TUNNEL_SERVICE_TYPE_IPP, }; enum16 ip_type { TUNNEL_IP_TYPE_INVALID, TUNNEL_IP_TYPE_IPv4, }; struct TunnelIpInfo { ip_type type; switch (type) { case TUNNEL_IP_TYPE_IPv4: uint8 ipv4[4]; } u; }; channel TunnelChannel : BaseChannel { server: message { uint16 max_num_of_sockets; uint32 max_socket_data_size; } init = 101; message { uint32 service_id; TunnelIpInfo virtual_ip; } service_ip_map; message { uint16 connection_id; uint32 service_id; uint32 tokens; } socket_open; message { uint16 connection_id; } socket_fin; message { uint16 connection_id; } socket_close; message { uint16 connection_id; uint8 data[]; } socket_data; message { uint16 connection_id; } socket_closed_ack; message { uint16 connection_id; uint32 num_tokens; } socket_token; client: message { service_type type; uint32 id; uint32 group; uint32 port; uint32 name; uint32 description; switch (type) { case TUNNEL_SERVICE_TYPE_IPP: TunnelIpInfo ip; } u; } service_add = 101; message { uint32 id; } service_remove; message { uint16 connection_id; uint32 tokens; } socket_open_ack; message { uint16 connection_id; } socket_open_nack; message { uint16 connection_id; } socket_fin; message { uint16 connection_id; } socket_closed; message { uint16 connection_id; } socket_closed_ack; message { uint16 connection_id; uint8 data[]; } socket_data; message { uint16 connection_id; uint32 num_tokens; } socket_token; }; protocol Spice { MainChannel main = 1; DisplayChannel display; InputsChannel inputs; CursorChannel cursor; PlaybackChannel playback; RecordChannel record; TunnelChannel tunnel; };
_______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/spice-devel