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 data_offset,
65                                            unsigned int data_size) {
66  CHECK(engine_);
67  scoped_refptr<gpu::Buffer> buffer = engine_->GetSharedMemoryBuffer(shm_id);
68  if (!buffer.get())
69    return NULL;
70  return buffer->GetDataAddress(data_offset, data_size);
71}
72
73scoped_refptr<gpu::Buffer> CommonDecoder::GetSharedMemoryBuffer(
74    unsigned int shm_id) {
75  return engine_->GetSharedMemoryBuffer(shm_id);
76}
77
78const char* CommonDecoder::GetCommonCommandName(
79    cmd::CommandId command_id) const {
80  return cmd::GetCommandName(command_id);
81}
82
83CommonDecoder::Bucket* CommonDecoder::GetBucket(uint32 bucket_id) const {
84  BucketMap::const_iterator iter(buckets_.find(bucket_id));
85  return iter != buckets_.end() ? &(*iter->second) : NULL;
86}
87
88CommonDecoder::Bucket* CommonDecoder::CreateBucket(uint32 bucket_id) {
89  Bucket* bucket = GetBucket(bucket_id);
90  if (!bucket) {
91    bucket = new Bucket();
92    buckets_[bucket_id] = linked_ptr<Bucket>(bucket);
93  }
94  return bucket;
95}
96
97namespace {
98
99// Returns the address of the first byte after a struct.
100template <typename T>
101const void* AddressAfterStruct(const T& pod) {
102  return reinterpret_cast<const uint8*>(&pod) + sizeof(pod);
103}
104
105// Returns the address of the frst byte after the struct.
106template <typename RETURN_TYPE, typename COMMAND_TYPE>
107RETURN_TYPE GetImmediateDataAs(const COMMAND_TYPE& pod) {
108  return static_cast<RETURN_TYPE>(const_cast<void*>(AddressAfterStruct(pod)));
109}
110
111// TODO(vmiura): Looks like this g_command_info is duplicated in
112// common_decoder.cc
113// and gles2_cmd_decoder.cc.  Fix it!
114
115// A struct to hold info about each command.
116struct CommandInfo {
117  uint8 arg_flags;   // How to handle the arguments for this command
118  uint8 cmd_flags;   // How to handle this command
119  uint16 arg_count;  // How many arguments are expected for this command.
120};
121
122// A table of CommandInfo for all the commands.
123const CommandInfo g_command_info[] = {
124  #define COMMON_COMMAND_BUFFER_CMD_OP(name) {                           \
125    cmd::name::kArgFlags,                                                \
126    cmd::name::cmd_flags,                                                \
127    sizeof(cmd::name) / sizeof(CommandBufferEntry) - 1, },  /* NOLINT */
128
129  COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP)
130
131  #undef COMMON_COMMAND_BUFFER_CMD_OP
132};
133
134}  // anonymous namespace.
135
136// Decode command with its arguments, and call the corresponding method.
137// Note: args is a pointer to the command buffer. As such, it could be changed
138// by a (malicious) client at any time, so if validation has to happen, it
139// should operate on a copy of them.
140error::Error CommonDecoder::DoCommonCommand(
141    unsigned int command,
142    unsigned int arg_count,
143    const void* cmd_data) {
144  if (command < arraysize(g_command_info)) {
145    const CommandInfo& info = g_command_info[command];
146    unsigned int info_arg_count = static_cast<unsigned int>(info.arg_count);
147    if ((info.arg_flags == cmd::kFixed && arg_count == info_arg_count) ||
148        (info.arg_flags == cmd::kAtLeastN && arg_count >= info_arg_count)) {
149      uint32 immediate_data_size =
150          (arg_count - info_arg_count) * sizeof(CommandBufferEntry);  // NOLINT
151      switch (command) {
152        #define COMMON_COMMAND_BUFFER_CMD_OP(name)                      \
153          case cmd::name::kCmdId:                                       \
154            return Handle ## name(                                      \
155                immediate_data_size,                                    \
156                *static_cast<const cmd::name*>(cmd_data));              \
157
158        COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP)
159
160        #undef COMMON_COMMAND_BUFFER_CMD_OP
161      }
162    } else {
163      return error::kInvalidArguments;
164    }
165  }
166  return error::kUnknownCommand;
167}
168
169error::Error CommonDecoder::HandleNoop(
170    uint32 immediate_data_size,
171    const cmd::Noop& args) {
172  return error::kNoError;
173}
174
175error::Error CommonDecoder::HandleSetToken(
176    uint32 immediate_data_size,
177    const cmd::SetToken& args) {
178  engine_->set_token(args.token);
179  return error::kNoError;
180}
181
182error::Error CommonDecoder::HandleSetBucketSize(
183    uint32 immediate_data_size,
184    const cmd::SetBucketSize& args) {
185  uint32 bucket_id = args.bucket_id;
186  uint32 size = args.size;
187
188  Bucket* bucket = CreateBucket(bucket_id);
189  bucket->SetSize(size);
190  return error::kNoError;
191}
192
193error::Error CommonDecoder::HandleSetBucketData(
194    uint32 immediate_data_size,
195    const cmd::SetBucketData& args) {
196  uint32 bucket_id = args.bucket_id;
197  uint32 offset = args.offset;
198  uint32 size = args.size;
199  const void* data = GetSharedMemoryAs<const void*>(
200      args.shared_memory_id, args.shared_memory_offset, size);
201  if (!data) {
202    return error::kInvalidArguments;
203  }
204  Bucket* bucket = GetBucket(bucket_id);
205  if (!bucket) {
206    return error::kInvalidArguments;
207  }
208  if (!bucket->SetData(data, offset, size)) {
209    return error::kInvalidArguments;
210  }
211
212  return error::kNoError;
213}
214
215error::Error CommonDecoder::HandleSetBucketDataImmediate(
216    uint32 immediate_data_size,
217    const cmd::SetBucketDataImmediate& args) {
218  const void* data = GetImmediateDataAs<const void*>(args);
219  uint32 bucket_id = args.bucket_id;
220  uint32 offset = args.offset;
221  uint32 size = args.size;
222  if (size > immediate_data_size) {
223    return error::kInvalidArguments;
224  }
225  Bucket* bucket = GetBucket(bucket_id);
226  if (!bucket) {
227    return error::kInvalidArguments;
228  }
229  if (!bucket->SetData(data, offset, size)) {
230    return error::kInvalidArguments;
231  }
232  return error::kNoError;
233}
234
235error::Error CommonDecoder::HandleGetBucketStart(
236    uint32 immediate_data_size,
237    const cmd::GetBucketStart& args) {
238  uint32 bucket_id = args.bucket_id;
239  uint32* result = GetSharedMemoryAs<uint32*>(
240      args.result_memory_id, args.result_memory_offset, sizeof(*result));
241  int32 data_memory_id = args.data_memory_id;
242  uint32 data_memory_offset = args.data_memory_offset;
243  uint32 data_memory_size = args.data_memory_size;
244  uint8* data = NULL;
245  if (data_memory_size != 0 || data_memory_id != 0 || data_memory_offset != 0) {
246    data = GetSharedMemoryAs<uint8*>(
247        args.data_memory_id, args.data_memory_offset, args.data_memory_size);
248    if (!data) {
249      return error::kInvalidArguments;
250    }
251  }
252  if (!result) {
253    return error::kInvalidArguments;
254  }
255  // Check that the client initialized the result.
256  if (*result != 0) {
257    return error::kInvalidArguments;
258  }
259  Bucket* bucket = GetBucket(bucket_id);
260  if (!bucket) {
261    return error::kInvalidArguments;
262  }
263  uint32 bucket_size = bucket->size();
264  *result = bucket_size;
265  if (data) {
266    uint32 size = std::min(data_memory_size, bucket_size);
267    memcpy(data, bucket->GetData(0, size), size);
268  }
269  return error::kNoError;
270}
271
272error::Error CommonDecoder::HandleGetBucketData(
273    uint32 immediate_data_size,
274    const cmd::GetBucketData& args) {
275  uint32 bucket_id = args.bucket_id;
276  uint32 offset = args.offset;
277  uint32 size = args.size;
278  void* data = GetSharedMemoryAs<void*>(
279      args.shared_memory_id, args.shared_memory_offset, size);
280  if (!data) {
281    return error::kInvalidArguments;
282  }
283  Bucket* bucket = GetBucket(bucket_id);
284  if (!bucket) {
285    return error::kInvalidArguments;
286  }
287  const void* src = bucket->GetData(offset, size);
288  if (!src) {
289      return error::kInvalidArguments;
290  }
291  memcpy(data, src, size);
292  return error::kNoError;
293}
294
295}  // namespace gpu
296