diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/hwstub/include/hwstub.hpp | 18 | ||||
-rw-r--r-- | utils/hwstub/include/hwstub_net.hpp | 2 | ||||
-rw-r--r-- | utils/hwstub/include/hwstub_protocol.h | 32 | ||||
-rw-r--r-- | utils/hwstub/include/hwstub_usb.hpp | 4 | ||||
-rw-r--r-- | utils/hwstub/include/hwstub_virtual.hpp | 2 | ||||
-rw-r--r-- | utils/hwstub/lib/hwstub.cpp | 46 | ||||
-rw-r--r-- | utils/hwstub/lib/hwstub_net.cpp | 12 | ||||
-rw-r--r-- | utils/hwstub/lib/hwstub_usb.cpp | 52 | ||||
-rw-r--r-- | utils/hwstub/lib/hwstub_virtual.cpp | 12 | ||||
-rw-r--r-- | utils/hwstub/stub/main.c | 188 | ||||
-rw-r--r-- | utils/hwstub/tools/hwstub_shell.cpp | 72 |
11 files changed, 423 insertions, 17 deletions
diff --git a/utils/hwstub/include/hwstub.hpp b/utils/hwstub/include/hwstub.hpp index deac976240..e403857200 100644 --- a/utils/hwstub/include/hwstub.hpp +++ b/utils/hwstub/include/hwstub.hpp | |||
@@ -62,6 +62,8 @@ enum class error | |||
62 | NET_ERROR, /** Network error */ | 62 | NET_ERROR, /** Network error */ |
63 | TIMEOUT, /** Operation timed out */ | 63 | TIMEOUT, /** Operation timed out */ |
64 | OVERFLW, /** Operation stopped to prevent buffer overflow */ | 64 | OVERFLW, /** Operation stopped to prevent buffer overflow */ |
65 | UNIMPLEMENTED, /** Operation has not been implemented */ | ||
66 | UNSUPPORTED, /** Operation is not supported by device/protocol */ | ||
65 | }; | 67 | }; |
66 | 68 | ||
67 | /** Return a string explaining the error */ | 69 | /** Return a string explaining the error */ |
@@ -266,6 +268,15 @@ public: | |||
266 | * according to the buffer size and call read_dev() and write_dev() */ | 268 | * according to the buffer size and call read_dev() and write_dev() */ |
267 | error read(uint32_t addr, void *buf, size_t& sz, bool atomic); | 269 | error read(uint32_t addr, void *buf, size_t& sz, bool atomic); |
268 | error write(uint32_t addr, const void *buf, size_t& sz, bool atomic); | 270 | error write(uint32_t addr, const void *buf, size_t& sz, bool atomic); |
271 | /** Execute a general cop operation: if out_data is not null, data is appended to header, | ||
272 | * if in_data is not null, a read operation follows to retrieve some data. | ||
273 | * The in_data parameters is updated to reflect the number of transfered bytes */ | ||
274 | error cop_op(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data, | ||
275 | size_t out_size, void *in_data, size_t *in_size); | ||
276 | /** Execute a coprocessor read operation */ | ||
277 | error read32_cop(uint8_t args[HWSTUB_COP_ARGS], uint32_t& value); | ||
278 | /** Execute a coprocessor write operation */ | ||
279 | error write32_cop(uint8_t args[HWSTUB_COP_ARGS], uint32_t value); | ||
269 | /** Get device buffer size: any read() or write() greater than this size | 280 | /** Get device buffer size: any read() or write() greater than this size |
270 | * will be split into several transaction to avoid overflowing device | 281 | * will be split into several transaction to avoid overflowing device |
271 | * buffer. */ | 282 | * buffer. */ |
@@ -303,6 +314,11 @@ protected: | |||
303 | virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz) = 0; | 314 | virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz) = 0; |
304 | virtual error get_dev_log(void *buf, size_t& buf_sz) = 0; | 315 | virtual error get_dev_log(void *buf, size_t& buf_sz) = 0; |
305 | virtual error exec_dev(uint32_t addr, uint16_t flags) = 0; | 316 | virtual error exec_dev(uint32_t addr, uint16_t flags) = 0; |
317 | /* cop operation: if out_data is not null, data is appended to header, if | ||
318 | * in_data is not null, a READ2 operation follows to retrieve some data | ||
319 | * The in_data parameters is updated to reflect the number of transfered bytes*/ | ||
320 | virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data, | ||
321 | size_t out_size, void *in_data, size_t *in_size) = 0; | ||
306 | 322 | ||
307 | std::shared_ptr<device> m_dev; /* pointer to device */ | 323 | std::shared_ptr<device> m_dev; /* pointer to device */ |
308 | std::atomic<int> m_refcnt; /* reference count */ | 324 | std::atomic<int> m_refcnt; /* reference count */ |
@@ -338,6 +354,8 @@ protected: | |||
338 | virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); | 354 | virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); |
339 | virtual error get_dev_log(void *buf, size_t& buf_sz); | 355 | virtual error get_dev_log(void *buf, size_t& buf_sz); |
340 | virtual error exec_dev(uint32_t addr, uint16_t flags); | 356 | virtual error exec_dev(uint32_t addr, uint16_t flags); |
357 | virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data, | ||
358 | size_t out_size, void *in_data, size_t *in_size); | ||
341 | virtual error status() const; | 359 | virtual error status() const; |
342 | virtual size_t get_buffer_size(); | 360 | virtual size_t get_buffer_size(); |
343 | 361 | ||
diff --git a/utils/hwstub/include/hwstub_net.hpp b/utils/hwstub/include/hwstub_net.hpp index 2cf6e07ccb..ae0e09e920 100644 --- a/utils/hwstub/include/hwstub_net.hpp +++ b/utils/hwstub/include/hwstub_net.hpp | |||
@@ -184,6 +184,8 @@ protected: | |||
184 | virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); | 184 | virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); |
185 | virtual error get_dev_log(void *buf, size_t& buf_sz); | 185 | virtual error get_dev_log(void *buf, size_t& buf_sz); |
186 | virtual error exec_dev(uint32_t addr, uint16_t flags); | 186 | virtual error exec_dev(uint32_t addr, uint16_t flags); |
187 | virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data, | ||
188 | size_t out_size, void *in_data, size_t *in_size); | ||
187 | virtual error status() const; | 189 | virtual error status() const; |
188 | virtual size_t get_buffer_size(); | 190 | virtual size_t get_buffer_size(); |
189 | 191 | ||
diff --git a/utils/hwstub/include/hwstub_protocol.h b/utils/hwstub/include/hwstub_protocol.h index f767e50571..ed26ee78e4 100644 --- a/utils/hwstub/include/hwstub_protocol.h +++ b/utils/hwstub/include/hwstub_protocol.h | |||
@@ -33,7 +33,7 @@ | |||
33 | */ | 33 | */ |
34 | 34 | ||
35 | #define HWSTUB_VERSION_MAJOR 4 | 35 | #define HWSTUB_VERSION_MAJOR 4 |
36 | #define HWSTUB_VERSION_MINOR 2 | 36 | #define HWSTUB_VERSION_MINOR 3 |
37 | 37 | ||
38 | #define HWSTUB_VERSION__(maj, min) #maj"."#min | 38 | #define HWSTUB_VERSION__(maj, min) #maj"."#min |
39 | #define HWSTUB_VERSION_(maj, min) HWSTUB_VERSION__(maj, min) | 39 | #define HWSTUB_VERSION_(maj, min) HWSTUB_VERSION__(maj, min) |
@@ -169,6 +169,7 @@ struct hwstub_net_hdr_t | |||
169 | #define HWSTUB_EXEC 0x44 | 169 | #define HWSTUB_EXEC 0x44 |
170 | #define HWSTUB_READ2_ATOMIC 0x45 | 170 | #define HWSTUB_READ2_ATOMIC 0x45 |
171 | #define HWSTUB_WRITE_ATOMIC 0x46 | 171 | #define HWSTUB_WRITE_ATOMIC 0x46 |
172 | #define HWSTUB_COPROCESSOR_OP 0x47 | ||
172 | 173 | ||
173 | /* the following commands and the ACK/NACK mechanism are net only */ | 174 | /* the following commands and the ACK/NACK mechanism are net only */ |
174 | #define HWSERVER_ACK(n) (0x100|(n)) | 175 | #define HWSERVER_ACK(n) (0x100|(n)) |
@@ -252,6 +253,35 @@ struct hwstub_exec_req_t | |||
252 | } __attribute__((packed)); | 253 | } __attribute__((packed)); |
253 | 254 | ||
254 | /** | 255 | /** |
256 | * HWSTUB_COPROCESSOR_OP | ||
257 | * Execute a coprocessor operation. The operation is describe in the header of | ||
258 | * the structure. There are currently two supported operations: | ||
259 | * - read: following the HWSTUB_COPROCESSOR_OP, the host can retrieve the data | ||
260 | * by sending a HWSTUB_READ2 request (just like a regular read) of | ||
261 | * the appropriate size | ||
262 | * - write: the header is followed by the data to write. | ||
263 | * If the request has two parts (second being READ2) then both requests must use | ||
264 | * the same ID in wValue, otherwise the second request will be STALLed. | ||
265 | * If a particular operation is not supported, it must be STALLed by the device. | ||
266 | */ | ||
267 | |||
268 | #define HWSTUB_COP_READ 0 /* read operation */ | ||
269 | #define HWSTUB_COP_WRITE 1 /* write operation */ | ||
270 | /* for MIPS */ | ||
271 | #define HWSTUB_COP_MIPS_COP 0 /* coprocessor number */ | ||
272 | #define HWSTUB_COP_MIPS_REG 1 /* coprocessor register */ | ||
273 | #define HWSTUB_COP_MIPS_SEL 2 /* coprocessor select */ | ||
274 | |||
275 | #define HWSTUB_COP_ARGS 7 /* maximum number of arguments */ | ||
276 | |||
277 | struct hwstub_cop_req_t | ||
278 | { | ||
279 | uint8_t bOp; /* operation to execute */ | ||
280 | uint8_t bArgs[HWSTUB_COP_ARGS]; /* arguments to the operation */ | ||
281 | /* followed by data for WRITE operation */ | ||
282 | }; | ||
283 | |||
284 | /** | ||
255 | * HWSERVER_HELLO: | 285 | * HWSERVER_HELLO: |
256 | * Say hello to the server, give protocol version and get server version. | 286 | * Say hello to the server, give protocol version and get server version. |
257 | * Send: args[0] = major << 8 | minor, no data | 287 | * Send: args[0] = major << 8 | minor, no data |
diff --git a/utils/hwstub/include/hwstub_usb.hpp b/utils/hwstub/include/hwstub_usb.hpp index 6a9d4d8798..15a0fcaaec 100644 --- a/utils/hwstub/include/hwstub_usb.hpp +++ b/utils/hwstub/include/hwstub_usb.hpp | |||
@@ -132,6 +132,8 @@ protected: | |||
132 | virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); | 132 | virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); |
133 | virtual error get_dev_log(void *buf, size_t& buf_sz); | 133 | virtual error get_dev_log(void *buf, size_t& buf_sz); |
134 | virtual error exec_dev(uint32_t addr, uint16_t flags); | 134 | virtual error exec_dev(uint32_t addr, uint16_t flags); |
135 | virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data, | ||
136 | size_t out_size, void *in_data, size_t *in_size); | ||
135 | virtual error status() const; | 137 | virtual error status() const; |
136 | virtual size_t get_buffer_size(); | 138 | virtual size_t get_buffer_size(); |
137 | /* Probe a device to check if it is an hwstub device and return the interface | 139 | /* Probe a device to check if it is an hwstub device and return the interface |
@@ -162,6 +164,8 @@ protected: | |||
162 | virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); | 164 | virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); |
163 | virtual error get_dev_log(void *buf, size_t& buf_sz); | 165 | virtual error get_dev_log(void *buf, size_t& buf_sz); |
164 | virtual error exec_dev(uint32_t addr, uint16_t flags); | 166 | virtual error exec_dev(uint32_t addr, uint16_t flags); |
167 | virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data, | ||
168 | size_t out_size, void *in_data, size_t *in_size); | ||
165 | virtual error status() const; | 169 | virtual error status() const; |
166 | virtual size_t get_buffer_size(); | 170 | virtual size_t get_buffer_size(); |
167 | error probe(); | 171 | error probe(); |
diff --git a/utils/hwstub/include/hwstub_virtual.hpp b/utils/hwstub/include/hwstub_virtual.hpp index d35f98e0ec..1e35378840 100644 --- a/utils/hwstub/include/hwstub_virtual.hpp +++ b/utils/hwstub/include/hwstub_virtual.hpp | |||
@@ -147,6 +147,8 @@ protected: | |||
147 | virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); | 147 | virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz); |
148 | virtual error get_dev_log(void *buf, size_t& buf_sz); | 148 | virtual error get_dev_log(void *buf, size_t& buf_sz); |
149 | virtual error exec_dev(uint32_t addr, uint16_t flags); | 149 | virtual error exec_dev(uint32_t addr, uint16_t flags); |
150 | virtual error cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data, | ||
151 | size_t out_size, void *in_data, size_t *in_size); | ||
150 | virtual error status() const; | 152 | virtual error status() const; |
151 | virtual size_t get_buffer_size(); | 153 | virtual size_t get_buffer_size(); |
152 | 154 | ||
diff --git a/utils/hwstub/lib/hwstub.cpp b/utils/hwstub/lib/hwstub.cpp index 9dd2915903..5e708c3bb8 100644 --- a/utils/hwstub/lib/hwstub.cpp +++ b/utils/hwstub/lib/hwstub.cpp | |||
@@ -45,6 +45,8 @@ std::string error_string(error err) | |||
45 | case error::PROTOCOL_ERROR: return "network protocol error"; | 45 | case error::PROTOCOL_ERROR: return "network protocol error"; |
46 | case error::TIMEOUT: return "timeout"; | 46 | case error::TIMEOUT: return "timeout"; |
47 | case error::OVERFLW: return "overflow"; | 47 | case error::OVERFLW: return "overflow"; |
48 | case error::UNIMPLEMENTED: return "operation is not implemented"; | ||
49 | case error::UNSUPPORTED: return "operation unsupported"; | ||
48 | default: return "unknown error"; | 50 | default: return "unknown error"; |
49 | } | 51 | } |
50 | } | 52 | } |
@@ -457,6 +459,38 @@ error handle::write(uint32_t addr, const void *buf, size_t& sz, bool atomic) | |||
457 | return error::SUCCESS; | 459 | return error::SUCCESS; |
458 | } | 460 | } |
459 | 461 | ||
462 | error handle::cop_op(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], const void *out_data, | ||
463 | size_t out_size, void *in_data, size_t *in_size) | ||
464 | { | ||
465 | std::unique_lock<std::recursive_mutex> lock(m_mutex); | ||
466 | /* get a pointer so that it's not destroyed during the runtime of the function, | ||
467 | * the pointer will be released at the end of the function */ | ||
468 | std::shared_ptr<context> ctx = m_dev->get_context(); | ||
469 | if(!ctx) | ||
470 | return error::NO_CONTEXT; | ||
471 | /* ensure valid status */ | ||
472 | error err = status(); | ||
473 | if(err != error::SUCCESS) | ||
474 | return err; | ||
475 | return cop_dev(op, args, out_data, out_size, in_data, in_size); | ||
476 | } | ||
477 | |||
478 | error handle::read32_cop(uint8_t args[HWSTUB_COP_ARGS], uint32_t& value) | ||
479 | { | ||
480 | size_t sz = sizeof(value); | ||
481 | error err = cop_op(HWSTUB_COP_READ, args, nullptr, 0, &value, &sz); | ||
482 | if(err != error::SUCCESS) | ||
483 | return err; | ||
484 | if(sz != sizeof(value)) | ||
485 | return error::ERROR; | ||
486 | return error::SUCCESS; | ||
487 | } | ||
488 | |||
489 | error handle::write32_cop(uint8_t args[HWSTUB_COP_ARGS], uint32_t value) | ||
490 | { | ||
491 | return cop_op(HWSTUB_COP_WRITE, args, &value, sizeof(value), nullptr, nullptr); | ||
492 | } | ||
493 | |||
460 | error handle::status() const | 494 | error handle::status() const |
461 | { | 495 | { |
462 | /* check context */ | 496 | /* check context */ |
@@ -616,6 +650,18 @@ error dummy_handle::exec_dev(uint32_t addr, uint16_t flags) | |||
616 | return error::DUMMY; | 650 | return error::DUMMY; |
617 | } | 651 | } |
618 | 652 | ||
653 | error dummy_handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], | ||
654 | const void *out_data, size_t out_size, void *in_data, size_t *in_size) | ||
655 | { | ||
656 | (void) op; | ||
657 | (void) args; | ||
658 | (void) out_data; | ||
659 | (void) out_size; | ||
660 | (void) in_data; | ||
661 | (void) in_size; | ||
662 | return error::DUMMY; | ||
663 | } | ||
664 | |||
619 | error dummy_handle::status() const | 665 | error dummy_handle::status() const |
620 | { | 666 | { |
621 | error err = handle::status(); | 667 | error err = handle::status(); |
diff --git a/utils/hwstub/lib/hwstub_net.cpp b/utils/hwstub/lib/hwstub_net.cpp index ddafea6351..c9d201a761 100644 --- a/utils/hwstub/lib/hwstub_net.cpp +++ b/utils/hwstub/lib/hwstub_net.cpp | |||
@@ -735,6 +735,18 @@ error handle::exec_dev(uint32_t addr, uint16_t flags) | |||
735 | return error::SUCCESS; | 735 | return error::SUCCESS; |
736 | } | 736 | } |
737 | 737 | ||
738 | error handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], | ||
739 | const void *out_data, size_t out_size, void *in_data, size_t *in_size) | ||
740 | { | ||
741 | (void) op; | ||
742 | (void) args; | ||
743 | (void) out_data; | ||
744 | (void) out_size; | ||
745 | (void) in_data; | ||
746 | (void) in_size; | ||
747 | return error::UNIMPLEMENTED; | ||
748 | } | ||
749 | |||
738 | error handle::status() const | 750 | error handle::status() const |
739 | { | 751 | { |
740 | return hwstub::handle::status(); | 752 | return hwstub::handle::status(); |
diff --git a/utils/hwstub/lib/hwstub_usb.cpp b/utils/hwstub/lib/hwstub_usb.cpp index 6bb1cfa049..37249f5812 100644 --- a/utils/hwstub/lib/hwstub_usb.cpp +++ b/utils/hwstub/lib/hwstub_usb.cpp | |||
@@ -399,6 +399,46 @@ error rb_handle::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atom | |||
399 | return ret; | 399 | return ret; |
400 | } | 400 | } |
401 | 401 | ||
402 | error rb_handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], | ||
403 | const void *out_data, size_t out_size, void *in_data, size_t *in_size) | ||
404 | { | ||
405 | (void) op; | ||
406 | (void) args; | ||
407 | (void) out_data; | ||
408 | (void) out_size; | ||
409 | (void) in_data; | ||
410 | (void) in_size; | ||
411 | std::shared_ptr<hwstub::context> hctx = get_device()->get_context(); | ||
412 | if(!hctx) | ||
413 | return error::NO_CONTEXT; | ||
414 | |||
415 | /* construct out request: header followed by (optional) data */ | ||
416 | size_t hdr_sz = sizeof(struct hwstub_cop_req_t); | ||
417 | uint8_t *tmp_buf = new uint8_t[out_size + hdr_sz]; | ||
418 | struct hwstub_cop_req_t *req = reinterpret_cast<struct hwstub_cop_req_t *>(tmp_buf); | ||
419 | req->bOp = op; | ||
420 | for(int i = 0; i < HWSTUB_COP_ARGS; i++) | ||
421 | req->bArgs[i] = args[i]; | ||
422 | if(out_size > 0) | ||
423 | memcpy(tmp_buf + hdr_sz, out_data, out_size); | ||
424 | error ret = interpret_libusb_error(libusb_control_transfer(m_handle, | ||
425 | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT, | ||
426 | HWSTUB_COPROCESSOR_OP, m_transac_id++, m_intf, | ||
427 | (unsigned char *)req, out_size + hdr_sz, m_timeout), out_size + hdr_sz); | ||
428 | delete[] tmp_buf; | ||
429 | /* return errors if any */ | ||
430 | if(ret != error::SUCCESS) | ||
431 | return ret; | ||
432 | /* return now if there is no read stage */ | ||
433 | if(in_data == nullptr) | ||
434 | return error::SUCCESS; | ||
435 | /* perform read stage (use the same transaction ID) */ | ||
436 | return interpret_libusb_size(libusb_control_transfer(m_handle, | ||
437 | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN, | ||
438 | HWSTUB_READ2, m_transac_id - 1, m_intf, | ||
439 | (unsigned char *)in_data, *in_size, m_timeout), *in_size); | ||
440 | } | ||
441 | |||
402 | bool rb_handle::find_intf(struct libusb_device_descriptor *dev, | 442 | bool rb_handle::find_intf(struct libusb_device_descriptor *dev, |
403 | struct libusb_config_descriptor *config, int& intf_idx) | 443 | struct libusb_config_descriptor *config, int& intf_idx) |
404 | { | 444 | { |
@@ -674,6 +714,18 @@ error jz_handle::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atom | |||
674 | return ret; | 714 | return ret; |
675 | } | 715 | } |
676 | 716 | ||
717 | error jz_handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], | ||
718 | const void *out_data, size_t out_size, void *in_data, size_t *in_size) | ||
719 | { | ||
720 | (void) op; | ||
721 | (void) args; | ||
722 | (void) out_data; | ||
723 | (void) out_size; | ||
724 | (void) in_data; | ||
725 | (void) in_size; | ||
726 | return error::UNSUPPORTED; | ||
727 | } | ||
728 | |||
677 | bool jz_handle::is_boot_dev(struct libusb_device_descriptor *dev, | 729 | bool jz_handle::is_boot_dev(struct libusb_device_descriptor *dev, |
678 | struct libusb_config_descriptor *config) | 730 | struct libusb_config_descriptor *config) |
679 | { | 731 | { |
diff --git a/utils/hwstub/lib/hwstub_virtual.cpp b/utils/hwstub/lib/hwstub_virtual.cpp index fada56fb83..5c9e79e5a1 100644 --- a/utils/hwstub/lib/hwstub_virtual.cpp +++ b/utils/hwstub/lib/hwstub_virtual.cpp | |||
@@ -221,6 +221,18 @@ error handle::exec_dev(uint32_t addr, uint16_t flags) | |||
221 | return p ? p->exec_dev(addr, flags) : error::DISCONNECTED; | 221 | return p ? p->exec_dev(addr, flags) : error::DISCONNECTED; |
222 | } | 222 | } |
223 | 223 | ||
224 | error handle::cop_dev(uint8_t op, uint8_t args[HWSTUB_COP_ARGS], | ||
225 | const void *out_data, size_t out_size, void *in_data, size_t *in_size) | ||
226 | { | ||
227 | (void) op; | ||
228 | (void) args; | ||
229 | (void) out_data; | ||
230 | (void) out_size; | ||
231 | (void) in_data; | ||
232 | (void) in_size; | ||
233 | return error::UNSUPPORTED; | ||
234 | } | ||
235 | |||
224 | error handle::status() const | 236 | error handle::status() const |
225 | { | 237 | { |
226 | return hwstub::handle::status(); | 238 | return hwstub::handle::status(); |
diff --git a/utils/hwstub/stub/main.c b/utils/hwstub/stub/main.c index c35872f320..3eee8b6a18 100644 --- a/utils/hwstub/stub/main.c +++ b/utils/hwstub/stub/main.c | |||
@@ -400,10 +400,12 @@ static bool read_atomic(void *dst, void *src, size_t sz) | |||
400 | } | 400 | } |
401 | } | 401 | } |
402 | 402 | ||
403 | static void *last_read_addr = 0; | ||
404 | static uint16_t last_read_id = 0xffff; | ||
405 | static size_t last_read_max_size = 0; | ||
406 | |||
403 | static void handle_read(struct usb_ctrlrequest *req) | 407 | static void handle_read(struct usb_ctrlrequest *req) |
404 | { | 408 | { |
405 | static uint32_t last_addr = 0; | ||
406 | static uint16_t last_id = 0xffff; | ||
407 | uint16_t id = req->wValue; | 409 | uint16_t id = req->wValue; |
408 | 410 | ||
409 | if(req->bRequest == HWSTUB_READ) | 411 | if(req->bRequest == HWSTUB_READ) |
@@ -413,26 +415,29 @@ static void handle_read(struct usb_ctrlrequest *req) | |||
413 | return usb_drv_stall(EP_CONTROL, true, true); | 415 | return usb_drv_stall(EP_CONTROL, true, true); |
414 | asm volatile("nop" : : : "memory"); | 416 | asm volatile("nop" : : : "memory"); |
415 | struct hwstub_read_req_t *read = (void *)usb_buffer; | 417 | struct hwstub_read_req_t *read = (void *)usb_buffer; |
416 | last_addr = read->dAddress; | 418 | last_read_addr = (void *)read->dAddress; |
417 | last_id = id; | 419 | last_read_max_size = usb_buffer_size; |
420 | last_read_id = id; | ||
418 | usb_drv_send(EP_CONTROL, NULL, 0); | 421 | usb_drv_send(EP_CONTROL, NULL, 0); |
419 | } | 422 | } |
420 | else | 423 | else |
421 | { | 424 | { |
422 | if(id != last_id) | 425 | /* NOTE: READ2 is also called after a coprocessor operation */ |
426 | if(id != last_read_id) | ||
423 | return usb_drv_stall(EP_CONTROL, true, true); | 427 | return usb_drv_stall(EP_CONTROL, true, true); |
428 | size_t len = MIN(req->wLength, last_read_max_size); | ||
424 | 429 | ||
425 | if(req->bRequest == HWSTUB_READ2_ATOMIC) | 430 | if(req->bRequest == HWSTUB_READ2_ATOMIC) |
426 | { | 431 | { |
427 | if(set_data_abort_jmp() == 0) | 432 | if(set_data_abort_jmp() == 0) |
428 | { | 433 | { |
429 | if(!read_atomic(usb_buffer, (void *)last_addr, req->wLength)) | 434 | if(!read_atomic(usb_buffer, last_read_addr, len)) |
430 | return usb_drv_stall(EP_CONTROL, true, true); | 435 | return usb_drv_stall(EP_CONTROL, true, true); |
431 | } | 436 | } |
432 | else | 437 | else |
433 | { | 438 | { |
434 | logf("trapped read data abort in [0x%x,0x%x]\n", last_addr, | 439 | logf("trapped read data abort in [0x%x,0x%x]\n", last_read_addr, |
435 | last_addr + req->wLength); | 440 | last_read_addr + len); |
436 | return usb_drv_stall(EP_CONTROL, true, true); | 441 | return usb_drv_stall(EP_CONTROL, true, true); |
437 | } | 442 | } |
438 | } | 443 | } |
@@ -440,19 +445,19 @@ static void handle_read(struct usb_ctrlrequest *req) | |||
440 | { | 445 | { |
441 | if(set_data_abort_jmp() == 0) | 446 | if(set_data_abort_jmp() == 0) |
442 | { | 447 | { |
443 | memcpy(usb_buffer, (void *)last_addr, req->wLength); | 448 | memcpy(usb_buffer, last_read_addr, len); |
444 | asm volatile("nop" : : : "memory"); | 449 | asm volatile("nop" : : : "memory"); |
445 | } | 450 | } |
446 | else | 451 | else |
447 | { | 452 | { |
448 | logf("trapped read data abort in [0x%x,0x%x]\n", last_addr, | 453 | logf("trapped read data abort in [0x%x,0x%x]\n", last_read_addr, |
449 | last_addr + req->wLength); | 454 | last_read_addr + len); |
450 | return usb_drv_stall(EP_CONTROL, true, true); | 455 | return usb_drv_stall(EP_CONTROL, true, true); |
451 | } | 456 | } |
452 | 457 | ||
453 | } | 458 | } |
454 | 459 | ||
455 | usb_drv_send(EP_CONTROL, usb_buffer, req->wLength); | 460 | usb_drv_send(EP_CONTROL, usb_buffer, len); |
456 | usb_drv_recv(EP_CONTROL, NULL, 0); | 461 | usb_drv_recv(EP_CONTROL, NULL, 0); |
457 | } | 462 | } |
458 | } | 463 | } |
@@ -562,6 +567,163 @@ static void handle_exec(struct usb_ctrlrequest *req) | |||
562 | } | 567 | } |
563 | } | 568 | } |
564 | 569 | ||
570 | #ifdef CPU_MIPS | ||
571 | static uint32_t rw_cp0_inst_buffer[3]; | ||
572 | typedef uint32_t (*read_cp0_inst_buffer_fn_t)(void); | ||
573 | typedef void (*write_cp0_inst_buffer_fn_t)(uint32_t); | ||
574 | |||
575 | uint32_t mips_read_cp0(unsigned reg, unsigned sel) | ||
576 | { | ||
577 | /* ok this is tricky because the coprocessor read instruction encoding | ||
578 | * contains the register and select, so we need to generate the instruction | ||
579 | * on the fly, we generate a "function like" buffer with three instructions: | ||
580 | * mfc0 v0, reg, sel | ||
581 | * jr ra | ||
582 | * nop | ||
583 | */ | ||
584 | rw_cp0_inst_buffer[0] = 0x40000000 | /*v0*/2 << 16 | (sel & 0x7) | (reg & 0x1f) << 11; | ||
585 | rw_cp0_inst_buffer[1] = /*ra*/31 << 21 | 0x8; /* jr ra */ | ||
586 | rw_cp0_inst_buffer[2] = 0; /* nop */ | ||
587 | #ifdef CONFIG_FLUSH_CACHES | ||
588 | target_flush_caches(); | ||
589 | #endif | ||
590 | read_cp0_inst_buffer_fn_t fn = (read_cp0_inst_buffer_fn_t)rw_cp0_inst_buffer; | ||
591 | return fn(); | ||
592 | } | ||
593 | |||
594 | void mips_write_cp0(unsigned reg, unsigned sel, uint32_t val) | ||
595 | { | ||
596 | /* ok this is tricky because the coprocessor write instruction encoding | ||
597 | * contains the register and select, so we need to generate the instruction | ||
598 | * on the fly, we generate a "function like" buffer with three instructions: | ||
599 | * mtc0 a0, reg, sel | ||
600 | * jr ra | ||
601 | * nop | ||
602 | */ | ||
603 | rw_cp0_inst_buffer[0] = 0x40800000 | /*a0*/4 << 16 | (sel & 0x7) | (reg & 0x1f) << 11; | ||
604 | rw_cp0_inst_buffer[1] = /*ra*/31 << 21 | 0x8; /* jr ra */ | ||
605 | rw_cp0_inst_buffer[2] = 0; /* nop */ | ||
606 | #ifdef CONFIG_FLUSH_CACHES | ||
607 | target_flush_caches(); | ||
608 | #endif | ||
609 | write_cp0_inst_buffer_fn_t fn = (write_cp0_inst_buffer_fn_t)rw_cp0_inst_buffer; | ||
610 | fn(val); | ||
611 | } | ||
612 | #endif | ||
613 | |||
614 | /* coprocessor read: return <0 on error (-2 for dull dump), or size to return | ||
615 | * to host otherwise */ | ||
616 | int cop_read(uint8_t args[HWSTUB_COP_ARGS], void *out_data, size_t out_max_sz) | ||
617 | { | ||
618 | /* virtually all targets do register-based operation, so 32-bit */ | ||
619 | if(out_max_sz < 4) | ||
620 | { | ||
621 | logf("cop read failed: output buffer is too small\n"); | ||
622 | return -1; | ||
623 | } | ||
624 | #ifdef CPU_MIPS | ||
625 | if(args[HWSTUB_COP_MIPS_COP] != 0) | ||
626 | { | ||
627 | logf("cop read failed: only mips cp0 is supported\n"); | ||
628 | return -2; | ||
629 | } | ||
630 | *(uint32_t *)out_data = mips_read_cp0(args[HWSTUB_COP_MIPS_REG], args[HWSTUB_COP_MIPS_SEL]); | ||
631 | return 4; | ||
632 | #else | ||
633 | (void) args; | ||
634 | (void) out_data; | ||
635 | (void) out_max_sz; | ||
636 | logf("cop read failed: unsupported cpu\n"); | ||
637 | return -1; | ||
638 | #endif | ||
639 | } | ||
640 | |||
641 | /* coprocessor write: return <0 on error (-2 for dull dump), or 0 on success */ | ||
642 | int cop_write(uint8_t args[HWSTUB_COP_ARGS], const void *in_data, size_t in_sz) | ||
643 | { | ||
644 | /* virtually all targets do register-based operation, so 32-bit */ | ||
645 | if(in_sz != 4) | ||
646 | { | ||
647 | logf("cop read failed: input buffer has wrong size\n"); | ||
648 | return -1; | ||
649 | } | ||
650 | #ifdef CPU_MIPS | ||
651 | if(args[HWSTUB_COP_MIPS_COP] != 0) | ||
652 | { | ||
653 | logf("cop read failed: only mips cp0 is supported\n"); | ||
654 | return -2; | ||
655 | } | ||
656 | mips_write_cp0(args[HWSTUB_COP_MIPS_REG], args[HWSTUB_COP_MIPS_SEL], *(uint32_t *)in_data); | ||
657 | return 0; | ||
658 | #else | ||
659 | (void) args; | ||
660 | (void) in_data; | ||
661 | (void) in_sz; | ||
662 | logf("cop write failed: unsupported cpu\n"); | ||
663 | return -1; | ||
664 | #endif | ||
665 | } | ||
666 | |||
667 | /* return size to return to host or <0 on error */ | ||
668 | int do_cop_op(struct hwstub_cop_req_t *cop, void *in_data, size_t in_sz, | ||
669 | void *out_data, size_t out_max_sz) | ||
670 | { | ||
671 | int ret = -2; /* -2 means full debug dump */ | ||
672 | /* handle operations */ | ||
673 | if(cop->bOp == HWSTUB_COP_READ) | ||
674 | { | ||
675 | /* read cannot have extra data */ | ||
676 | if(in_sz > 0) | ||
677 | goto Lerr; | ||
678 | ret = cop_read(cop->bArgs, out_data, out_max_sz); | ||
679 | } | ||
680 | else if(cop->bOp == HWSTUB_COP_WRITE) | ||
681 | { | ||
682 | ret = cop_write(cop->bArgs, in_data, in_sz); | ||
683 | } | ||
684 | |||
685 | Lerr: | ||
686 | if(ret == -2) | ||
687 | { | ||
688 | /* debug output */ | ||
689 | logf("invalid cop op: %d, ", cop->bOp); | ||
690 | for(int i = 0; i < HWSTUB_COP_ARGS; i++) | ||
691 | logf("%c0x%x", i == 0 ? '[' : ',', cop->bArgs[i]); | ||
692 | logf("] in:%d\n", in_sz); | ||
693 | } | ||
694 | return ret; | ||
695 | } | ||
696 | |||
697 | static void handle_cop(struct usb_ctrlrequest *req) | ||
698 | { | ||
699 | int size = usb_drv_recv(EP_CONTROL, usb_buffer, req->wLength); | ||
700 | int hdr_sz = sizeof(struct hwstub_cop_req_t); | ||
701 | asm volatile("nop" : : : "memory"); | ||
702 | struct hwstub_cop_req_t *cop = (void *)usb_buffer; | ||
703 | /* request should at least contain the header */ | ||
704 | if(size < hdr_sz) | ||
705 | return usb_drv_stall(EP_CONTROL, true, true); | ||
706 | /* perform coprocessor operation: put output buffer after the input one, | ||
707 | * limit output buffer size to maximum buffer size */ | ||
708 | uint8_t *in_buf = usb_buffer + hdr_sz; | ||
709 | size_t in_sz = req->wLength - hdr_sz; | ||
710 | uint8_t *out_buf = in_buf + in_sz; | ||
711 | size_t out_max_sz = usb_buffer_size - req->wLength; | ||
712 | int ret = do_cop_op(cop, in_buf, in_sz, out_buf, out_max_sz); | ||
713 | /* STALL on error */ | ||
714 | if(ret < 0) | ||
715 | return usb_drv_stall(EP_CONTROL, true, true); | ||
716 | /* acknowledge */ | ||
717 | usb_drv_send(EP_CONTROL, NULL, 0); | ||
718 | /* if there is a read stage, prepare everything for the READ2 */ | ||
719 | if(ret > 0) | ||
720 | { | ||
721 | last_read_id = req->wValue; | ||
722 | last_read_addr = out_buf; | ||
723 | last_read_max_size = ret; | ||
724 | } | ||
725 | } | ||
726 | |||
565 | static void handle_class_intf_req(struct usb_ctrlrequest *req) | 727 | static void handle_class_intf_req(struct usb_ctrlrequest *req) |
566 | { | 728 | { |
567 | unsigned intf = req->wIndex & 0xff; | 729 | unsigned intf = req->wIndex & 0xff; |
@@ -581,6 +743,8 @@ static void handle_class_intf_req(struct usb_ctrlrequest *req) | |||
581 | return handle_write(req); | 743 | return handle_write(req); |
582 | case HWSTUB_EXEC: | 744 | case HWSTUB_EXEC: |
583 | return handle_exec(req); | 745 | return handle_exec(req); |
746 | case HWSTUB_COPROCESSOR_OP: | ||
747 | return handle_cop(req); | ||
584 | default: | 748 | default: |
585 | usb_drv_stall(EP_CONTROL, true, true); | 749 | usb_drv_stall(EP_CONTROL, true, true); |
586 | } | 750 | } |
diff --git a/utils/hwstub/tools/hwstub_shell.cpp b/utils/hwstub/tools/hwstub_shell.cpp index b2cf2cec60..336b7fed6b 100644 --- a/utils/hwstub/tools/hwstub_shell.cpp +++ b/utils/hwstub/tools/hwstub_shell.cpp | |||
@@ -310,9 +310,11 @@ int my_lua_call(lua_State *state) | |||
310 | { | 310 | { |
311 | int n = lua_gettop(state); | 311 | int n = lua_gettop(state); |
312 | if(n != 1) | 312 | if(n != 1) |
313 | luaL_error(state, "call takes target address argument"); | 313 | luaL_error(state, "call takes one argument: target address"); |
314 | 314 | ||
315 | g_hwdev->exec(mylua_checkunsigned(state, 1), HWSTUB_EXEC_CALL); | 315 | error ret = g_hwdev->exec(luaL_checkunsigned(state, 1), HWSTUB_EXEC_CALL); |
316 | if(ret != error::SUCCESS) | ||
317 | luaL_error(state, "call failed"); | ||
316 | return 0; | 318 | return 0; |
317 | } | 319 | } |
318 | 320 | ||
@@ -320,9 +322,67 @@ int my_lua_jump(lua_State *state) | |||
320 | { | 322 | { |
321 | int n = lua_gettop(state); | 323 | int n = lua_gettop(state); |
322 | if(n != 1) | 324 | if(n != 1) |
323 | luaL_error(state, "jump takes target address argument"); | 325 | luaL_error(state, "jump takes one argument: target address"); |
326 | |||
327 | error ret = g_hwdev->exec(luaL_checkunsigned(state, 1), HWSTUB_EXEC_JUMP); | ||
328 | if(ret != error::SUCCESS) | ||
329 | luaL_error(state, "jump failed"); | ||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | int my_lua_read32_cop(lua_State *state) | ||
334 | { | ||
335 | int n = lua_gettop(state); | ||
336 | if(n != 1) | ||
337 | luaL_error(state, "read32_cop takes one argument: arguments (array)"); | ||
338 | uint8_t args[HWSTUB_COP_ARGS] = {0}; | ||
339 | /* parse array */ | ||
340 | luaL_checktype(state, 1, LUA_TTABLE); | ||
341 | size_t sz = lua_rawlen(state, 1); | ||
342 | if(sz > HWSTUB_COP_ARGS) | ||
343 | luaL_error(state, "coprocessor operation take at most %d arguments", HWSTUB_COP_ARGS); | ||
344 | for(size_t i = 0; i < sz; i++) | ||
345 | { | ||
346 | lua_pushinteger(state, i + 1); /* index start at 1 */ | ||
347 | lua_gettable(state, 1); | ||
348 | /* check it is an integer */ | ||
349 | args[i] = luaL_checkunsigned(state, -1); | ||
350 | /* pop it */ | ||
351 | lua_pop(state, 1); | ||
352 | } | ||
324 | 353 | ||
325 | g_hwdev->exec(mylua_checkunsigned(state, 1), HWSTUB_EXEC_JUMP); | 354 | uint32_t val; |
355 | error ret = g_hwdev->read32_cop(args, val); | ||
356 | if(ret != error::SUCCESS) | ||
357 | luaL_error(state, "coprocessor read failed"); | ||
358 | lua_pushunsigned(state, val); | ||
359 | return 1; | ||
360 | } | ||
361 | |||
362 | int my_lua_write32_cop(lua_State *state) | ||
363 | { | ||
364 | int n = lua_gettop(state); | ||
365 | if(n != 2) | ||
366 | luaL_error(state, "write32_cop takes two arguments: arguments (array) and value"); | ||
367 | uint8_t args[HWSTUB_COP_ARGS] = {0}; | ||
368 | /* parse array */ | ||
369 | luaL_checktype(state, 1, LUA_TTABLE); | ||
370 | size_t sz = lua_rawlen(state, 1); | ||
371 | if(sz > HWSTUB_COP_ARGS) | ||
372 | luaL_error(state, "coprocessor operation take at most %d arguments", HWSTUB_COP_ARGS); | ||
373 | for(size_t i = 0; i < sz; i++) | ||
374 | { | ||
375 | lua_pushinteger(state, i + 1); /* index start at 1 */ | ||
376 | lua_gettable(state, 1); | ||
377 | /* check it is an integer */ | ||
378 | args[i] = luaL_checkunsigned(state, -1); | ||
379 | /* pop it */ | ||
380 | lua_pop(state, 1); | ||
381 | } | ||
382 | |||
383 | error ret = g_hwdev->write32_cop(args, luaL_checkunsigned(state, 2)); | ||
384 | if(ret != error::SUCCESS) | ||
385 | luaL_error(state, "coprocessor write failed"); | ||
326 | return 0; | 386 | return 0; |
327 | } | 387 | } |
328 | 388 | ||
@@ -753,6 +813,10 @@ bool my_lua_import_hwstub() | |||
753 | lua_setfield(g_lua, -2, "call"); | 813 | lua_setfield(g_lua, -2, "call"); |
754 | lua_pushcclosure(g_lua, my_lua_jump, 0); | 814 | lua_pushcclosure(g_lua, my_lua_jump, 0); |
755 | lua_setfield(g_lua, -2, "jump"); | 815 | lua_setfield(g_lua, -2, "jump"); |
816 | lua_pushcclosure(g_lua, my_lua_read32_cop, 0); | ||
817 | lua_setfield(g_lua, -2, "read32_cop"); | ||
818 | lua_pushcclosure(g_lua, my_lua_write32_cop, 0); | ||
819 | lua_setfield(g_lua, -2, "write32_cop"); | ||
756 | 820 | ||
757 | lua_setfield(g_lua, -2, "dev"); | 821 | lua_setfield(g_lua, -2, "dev"); |
758 | 822 | ||