1 | // SPDX-License-Identifier: MIT |
2 | /* |
3 | * Copyright (C) 2017 Oracle Corporation |
4 | * Authors: Hans de Goede <hdegoede@redhat.com> |
5 | */ |
6 | |
7 | #include "vbox_drv.h" |
8 | #include "vboxvideo_vbe.h" |
9 | #include "hgsmi_defs.h" |
10 | |
11 | /* One-at-a-Time Hash from https://www.burtleburtle.net/bob/hash/doobs.html */ |
12 | static u32 hgsmi_hash_process(u32 hash, const u8 *data, int size) |
13 | { |
14 | while (size--) { |
15 | hash += *data++; |
16 | hash += (hash << 10); |
17 | hash ^= (hash >> 6); |
18 | } |
19 | |
20 | return hash; |
21 | } |
22 | |
23 | static u32 hgsmi_hash_end(u32 hash) |
24 | { |
25 | hash += (hash << 3); |
26 | hash ^= (hash >> 11); |
27 | hash += (hash << 15); |
28 | |
29 | return hash; |
30 | } |
31 | |
32 | /* Not really a checksum but that is the naming used in all vbox code */ |
33 | static u32 hgsmi_checksum(u32 offset, |
34 | const struct hgsmi_buffer_header *, |
35 | const struct hgsmi_buffer_tail *tail) |
36 | { |
37 | u32 checksum; |
38 | |
39 | checksum = hgsmi_hash_process(hash: 0, data: (u8 *)&offset, size: sizeof(offset)); |
40 | checksum = hgsmi_hash_process(hash: checksum, data: (u8 *)header, size: sizeof(*header)); |
41 | /* 4 -> Do not checksum the checksum itself */ |
42 | checksum = hgsmi_hash_process(hash: checksum, data: (u8 *)tail, size: 4); |
43 | |
44 | return hgsmi_hash_end(hash: checksum); |
45 | } |
46 | |
47 | void *hgsmi_buffer_alloc(struct gen_pool *guest_pool, size_t size, |
48 | u8 channel, u16 channel_info) |
49 | { |
50 | struct hgsmi_buffer_header *h; |
51 | struct hgsmi_buffer_tail *t; |
52 | size_t total_size; |
53 | dma_addr_t offset; |
54 | |
55 | total_size = size + sizeof(*h) + sizeof(*t); |
56 | h = gen_pool_dma_alloc(pool: guest_pool, size: total_size, dma: &offset); |
57 | if (!h) |
58 | return NULL; |
59 | |
60 | t = (struct hgsmi_buffer_tail *)((u8 *)h + sizeof(*h) + size); |
61 | |
62 | h->flags = HGSMI_BUFFER_HEADER_F_SEQ_SINGLE; |
63 | h->data_size = size; |
64 | h->channel = channel; |
65 | h->channel_info = channel_info; |
66 | memset(&h->u.header_data, 0, sizeof(h->u.header_data)); |
67 | |
68 | t->reserved = 0; |
69 | t->checksum = hgsmi_checksum(offset, header: h, tail: t); |
70 | |
71 | return (u8 *)h + sizeof(*h); |
72 | } |
73 | |
74 | void hgsmi_buffer_free(struct gen_pool *guest_pool, void *buf) |
75 | { |
76 | struct hgsmi_buffer_header *h = |
77 | (struct hgsmi_buffer_header *)((u8 *)buf - sizeof(*h)); |
78 | size_t total_size = h->data_size + sizeof(*h) + |
79 | sizeof(struct hgsmi_buffer_tail); |
80 | |
81 | gen_pool_free(pool: guest_pool, addr: (unsigned long)h, size: total_size); |
82 | } |
83 | |
84 | int hgsmi_buffer_submit(struct gen_pool *guest_pool, void *buf) |
85 | { |
86 | phys_addr_t offset; |
87 | |
88 | offset = gen_pool_virt_to_phys(pool: guest_pool, (unsigned long)buf - |
89 | sizeof(struct hgsmi_buffer_header)); |
90 | outl(value: offset, VGA_PORT_HGSMI_GUEST); |
91 | /* Make the compiler aware that the host has changed memory. */ |
92 | mb(); |
93 | |
94 | return 0; |
95 | } |
96 | |