1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "gpu/command_buffer/service/common_decoder.h"
6#include "gpu/command_buffer/service/cmd_buffer_engine.h"
7
8namespace gpu {
9
10CommonDecoder::Bucket::Bucket() : size_(0) {}
11
12CommonDecoder::Bucket::~Bucket() {}
13
14void* CommonDecoder::Bucket::GetData(size_t offset, size_t size) const {
15  if (OffsetSizeValid(offset, size)) {
16    return data_.get() + offset;
17  }
18  return NULL;
19}
20
21void CommonDecoder::Bucket::SetSize(size_t size) {
22  if (size != size_) {
23    data_.reset(size ? new int8[size] : NULL);
24    size_ = size;
25    memset(data_.get(), 0, size);
26  }
27}
28
29bool CommonDecoder::Bucket::SetData(
30    const void* src, size_t offset, size_t size) {
31  if (OffsetSizeValid(offset, size)) {
32    memcpy(data_.get() + offset, src, size);
33    return true;
34  }
35  return false;
36}
37
38void CommonDecoder::Bucket::SetFromString(const char* str) {
39  // Strings are passed NULL terminated to distinguish between empty string
40  // and no string.
41  if (!str) {
42    SetSize(0);
43  } else {
44    size_t size = strlen(str) + 1;
45    SetSize(size);
46    SetData(str, 0, size);
47  }
48}
49
50bool CommonDecoder::Bucket::GetAsString(std::string* str) {
51  DCHECK(str);
52  if (size_ == 0) {
53    return false;
54  }
55  str->assign(GetDataAs<const char*>(0, size_ - 1), size_ - 1);
56  return true;
57}
58
59CommonDecoder::CommonDecoder() : engine_(NULL) {}
60
61CommonDecoder::~CommonDecoder() {}
62
63void* CommonDecoder::GetAddressAndCheckSize(unsigned int shm_id,
64                                            unsigned int offset,
65                                            unsigned int size) {
66  CHECK(engine_);
67  Buffer buffer = engine_->GetSharedMemoryBuffer(shm_id);
68  if (!buffer.ptr)
69    return NULL;
70  unsigned int end = offset + size;
71  if (end > buffer.size || end < offset) {
72    return NULL;
73  }
74  return static_cast<int8*>(buffer.ptr) + offset;
75}
76
77Buffer CommonDecoder::GetSharedMemoryBuffer(unsigned int shm_id) {
78  return engine_->GetSharedMemoryBuffer(shm_id);
79}
80
81const char* CommonDecoder::GetCommonCommandName(
82    cmd::CommandId command_id) const {
83  return cmd::GetCommandName(command_id);
84}
85
86CommonDecoder::Bucket* CommonDecoder::GetBucket(uint32 bucket_id) const {
87  BucketMap::const_iterator iter(buckets_.find(bucket_id));
88  return iter != buckets_.end() ? &(*iter->second) : NULL;
89}
90
91CommonDecoder::Bucket* CommonDecoder::CreateBucket(uint32 bucket_id) {
92  Bucket* bucket = GetBucket(bucket_id);
93  if (!bucket) {
94    bucket = new Bucket();
95    buckets_[bucket_id] = linked_ptr<Bucket>(bucket);
96  }
97  return bucket;
98}
99
100namespace {
101
102// Returns the address of the first byte after a struct.
103template <typename T>
104const void* AddressAfterStruct(const T& pod) {
105  return reinterpret_cast<const uint8*>(&pod) + sizeof(pod);
106}
107
108// Returns the address of the frst byte after the struct.
109template <typename RETURN_TYPE, typename COMMAND_TYPE>
110RETURN_TYPE GetImmediateDataAs(const COMMAND_TYPE& pod) {
111  return static_cast<RETURN_TYPE>(const_cast<void*>(AddressAfterStruct(pod)));
112}
113
114// A struct to hold info about each command.
115struct CommandInfo {
116  int arg_flags;  // How to handle the arguments for this command
117  int arg_count;  // How many arguments are expected for this command.
118};
119
120// A table of CommandInfo for all the commands.
121const CommandInfo g_command_info[] = {
122  #define COMMON_COMMAND_BUFFER_CMD_OP(name) {                           \
123    cmd::name::kArgFlags,                                                \
124    sizeof(cmd::name) / sizeof(CommandBufferEntry) - 1, },  /* NOLINT */ \
125
126  COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP)
127
128  #undef COMMON_COMMAND_BUFFER_CMD_OP
129};
130
131}  // anonymous namespace.
132
133// Decode command with its arguments, and call the corresponding method.
134// Note: args is a pointer to the command buffer. As such, it could be changed
135// by a (malicious) client at any time, so if validation has to happen, it
136// should operate on a copy of them.
137error::Error CommonDecoder::DoCommonCommand(
138    unsigned int command,
139    unsigned int arg_count,
140    const void* cmd_data) {
141  if (command < arraysize(g_command_info)) {
142    const CommandInfo& info = g_command_info[command];
143    unsigned int info_arg_count = static_cast<unsigned int>(info.arg_count);
144    if ((info.arg_flags == cmd::kFixed && arg_count == info_arg_count) ||
145        (info.arg_flags == cmd::kAtLeastN && arg_count >= info_arg_count)) {
146      uint32 immediate_data_size =
147          (arg_count - info_arg_count) * sizeof(CommandBufferEntry);  // NOLINT
148      switch (command) {
149        #define COMMON_COMMAND_BUFFER_CMD_OP(name)                      \
150          case cmd::name::kCmdId:                                       \
151            return Handle ## name(                                      \
152                immediate_data_size,                                    \
153                *static_cast<const cmd::name*>(cmd_data));              \
154
155        COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP)
156
157        #undef COMMON_COMMAND_BUFFER_CMD_OP
158      }
159    } else {
160      return error::kInvalidArguments;
161    }
162  }
163  return error::kUnknownCommand;
164}
165
166error::Error CommonDecoder::HandleNoop(
167    uint32 immediate_data_size,
168    const cmd::Noop& args) {
169  return error::kNoError;
170}
171
172error::Error CommonDecoder::HandleSetToken(
173    uint32 immediate_data_size,
174    const cmd::SetToken& args) {
175  engine_->set_token(args.token);
176  return error::kNoError;
177}
178
179error::Error CommonDecoder::HandleSetBucketSize(
180    uint32 immediate_data_size,
181    const cmd::SetBucketSize& args) {
182  uint32 bucket_id = args.bucket_id;
183  uint32 size = args.size;
184
185  Bucket* bucket = CreateBucket(bucket_id);
186  bucket->SetSize(size);
187  return error::kNoError;
188}
189
190error::Error CommonDecoder::HandleSetBucketData(
191    uint32 immediate_data_size,
192    const cmd::SetBucketData& args) {
193  uint32 bucket_id = args.bucket_id;
194  uint32 offset = args.offset;
195  uint32 size = args.size;
196  const void* data = GetSharedMemoryAs<const void*>(
197      args.shared_memory_id, args.shared_memory_offset, size);
198  if (!data) {
199    return error::kInvalidArguments;
200  }
201  Bucket* bucket = GetBucket(bucket_id);
202  if (!bucket) {
203    return error::kInvalidArguments;
204  }
205  if (!bucket->SetData(data, offset, size)) {
206    return error::kInvalidArguments;
207  }
208
209  return error::kNoError;
210}
211
212error::Error CommonDecoder::HandleSetBucketDataImmediate(
213    uint32 immediate_data_size,
214    const cmd::SetBucketDataImmediate& args) {
215  const void* data = GetImmediateDataAs<const void*>(args);
216  uint32 bucket_id = args.bucket_id;
217  uint32 offset = args.offset;
218  uint32 size = args.size;
219  if (size > immediate_data_size) {
220    return error::kInvalidArguments;
221  }
222  Bucket* bucket = GetBucket(bucket_id);
223  if (!bucket) {
224    return error::kInvalidArguments;
225  }
226  if (!bucket->SetData(data, offset, size)) {
227    return error::kInvalidArguments;
228  }
229  return error::kNoError;
230}
231
232error::Error CommonDecoder::HandleGetBucketStart(
233    uint32 immediate_data_size,
234    const cmd::GetBucketStart& args) {
235  uint32 bucket_id = args.bucket_id;
236  uint32* result = GetSharedMemoryAs<uint32*>(
237      args.result_memory_id, args.result_memory_offset, sizeof(*result));
238  int32 data_memory_id = args.data_memory_id;
239  uint32 data_memory_offset = args.data_memory_offset;
240  uint32 data_memory_size = args.data_memory_size;
241  uint8* data = NULL;
242  if (data_memory_size != 0 || data_memory_id != 0 || data_memory_offset != 0) {
243    data = GetSharedMemoryAs<uint8*>(
244        args.data_memory_id, args.data_memory_offset, args.data_memory_size);
245    if (!data) {
246      return error::kInvalidArguments;
247    }
248  }
249  if (!result) {
250    return error::kInvalidArguments;
251  }
252  // Check that the client initialized the result.
253  if (*result != 0) {
254    return error::kInvalidArguments;
255  }
256  Bucket* bucket = GetBucket(bucket_id);
257  if (!bucket) {
258    return error::kInvalidArguments;
259  }
260  uint32 bucket_size = bucket->size();
261  *result = bucket_size;
262  if (data) {
263    uint32 size = std::min(data_memory_size, bucket_size);
264    memcpy(data, bucket->GetData(0, size), size);
265  }
266  return error::kNoError;
267}
268
269error::Error CommonDecoder::HandleGetBucketData(
270    uint32 immediate_data_size,
271    const cmd::GetBucketData& args) {
272  uint32 bucket_id = args.bucket_id;
273  uint32 offset = args.offset;
274  uint32 size = args.size;
275  void* data = GetSharedMemoryAs<void*>(
276      args.shared_memory_id, args.shared_memory_offset, size);
277  if (!data) {
278    return error::kInvalidArguments;
279  }
280  Bucket* bucket = GetBucket(bucket_id);
281  if (!bucket) {
282    return error::kInvalidArguments;
283  }
284  const void* src = bucket->GetData(offset, size);
285  if (!src) {
286      return error::kInvalidArguments;
287  }
288  memcpy(data, src, size);
289  return error::kNoError;
290}
291
292}  // namespace gpu
293