Tôi thấy một số vấn đề tiềm năng với những phần quan trọng. Có những cảnh báo và giải pháp cho tất cả những điều này, nhưng như một bản tóm tắt:
- Không có gì ngăn trình biên dịch di chuyển mã trên các macro này, để tối ưu hóa hoặc ngẫu nhiên các lý do khác.
- Họ lưu và khôi phục một số phần của bộ xử lý trạng thái trình biên dịch dự kiến lắp ráp nội tuyến để riêng (trừ khi nó được nói khác).
- Không có gì ngăn cản một sự gián đoạn xảy ra ở giữa chuỗi và thay đổi trạng thái giữa khi nó được đọc và khi nó được viết.
Trước hết, bạn chắc chắn cần một số rào cản bộ nhớ trình biên dịch . GCC thực hiện những điều này như là clobbers . Về cơ bản, đây là một cách để nói với trình biên dịch "Không, bạn không thể di chuyển các truy cập bộ nhớ qua phần lắp ráp nội tuyến này vì nó có thể ảnh hưởng đến kết quả của việc truy cập bộ nhớ." Cụ thể, bạn cần cả hai "memory"
và trình "cc"
chặn, trên cả macro bắt đầu và kết thúc. Những điều này sẽ ngăn những thứ khác (như các lệnh gọi hàm) được sắp xếp lại so với cụm nội tuyến, bởi vì trình biên dịch biết rằng chúng có thể có quyền truy cập bộ nhớ. Tôi đã thấy trạng thái giữ GCC cho ARM trong các thanh ghi mã điều kiện trong quá trình lắp ráp nội tuyến với "memory"
các trình "cc"
thu thập dữ liệu , vì vậy bạn chắc chắn cần trình ghi đè.
Thứ hai, các phần quan trọng này đang tiết kiệm và khôi phục rất nhiều hơn là chỉ cho phép ngắt. Cụ thể, họ đang lưu và khôi phục hầu hết CPSR (Đăng ký trạng thái chương trình hiện tại) (liên kết dành cho Cortex-R4 vì tôi không thể tìm thấy một sơ đồ đẹp cho A9, nhưng nó phải giống hệt nhau). Có những hạn chế tinh tế xung quanh những phần trạng thái thực sự có thể được sửa đổi, nhưng nó cần thiết hơn ở đây.
Trong số những thứ khác, điều này bao gồm các mã điều kiện (trong đó kết quả của các hướng dẫn như cmp
được lưu trữ để các hướng dẫn có điều kiện tiếp theo có thể tác động đến kết quả). Trình biên dịch chắc chắn sẽ bị nhầm lẫn bởi điều này. Điều này có thể dễ dàng giải quyết bằng cách sử dụng "cc"
clobber như đã đề cập ở trên. Tuy nhiên, điều này sẽ khiến mã bị lỗi mỗi lần, vì vậy nó không giống như những gì bạn đang gặp vấn đề. Tuy nhiên, phần nào đó của một quả bom hẹn giờ, trong đó việc sửa đổi mã ngẫu nhiên khác có thể khiến trình biên dịch làm một cái gì đó hơi khác một chút sẽ bị phá vỡ bởi điều này.
Điều này cũng sẽ cố gắng lưu / khôi phục các bit IT, được sử dụng để thực hiện thực thi có điều kiện Thumb . Lưu ý rằng nếu bạn không bao giờ thực thi mã Thumb, điều này không thành vấn đề. Tôi chưa bao giờ tìm ra cách lắp ráp nội tuyến của GCC đối phó với các bit CNTT, ngoài việc kết luận là không, có nghĩa là trình biên dịch không bao giờ đặt lắp ráp nội tuyến trong một khối CNTT và luôn mong muốn việc lắp ráp kết thúc bên ngoài khối CNTT. Tôi chưa bao giờ thấy GCC tạo mã vi phạm các giả định này và tôi đã thực hiện một số lắp ráp nội tuyến khá phức tạp với tối ưu hóa nặng, vì vậy tôi chắc chắn rằng họ nắm giữ một cách hợp lý. Điều này có nghĩa là nó có thể sẽ không thực sự cố gắng thay đổi các bit IT, trong trường hợp đó mọi thứ đều ổn. Cố gắng sửa đổi các bit này được phân loại là "không thể đoán trước về mặt kiến trúc", vì vậy nó có thể làm tất cả những điều tồi tệ, nhưng có lẽ sẽ không làm gì cả.
Loại bit cuối cùng sẽ được lưu / khôi phục (bên cạnh các bit thực sự vô hiệu hóa ngắt) là các bit chế độ. Chúng có thể sẽ không thay đổi, vì vậy nó có thể không thành vấn đề, nhưng nếu bạn có bất kỳ mã nào cố tình thay đổi chế độ thì các phần ngắt này có thể gây ra sự cố. Thay đổi giữa chế độ đặc quyền và người dùng là trường hợp duy nhất để làm điều này tôi mong đợi.
Thứ ba, không có gì ngăn cản sự thay đổi các phần khác của CPSR giữa MRS
và MSR
trong ARM_INT_LOCK
. Bất kỳ thay đổi như vậy có thể được ghi đè. Trong hầu hết các hệ thống hợp lý, các ngắt không đồng bộ không làm thay đổi trạng thái của mã mà chúng bị gián đoạn (bao gồm cả CPSR). Nếu họ làm như vậy, sẽ rất khó để suy luận về những gì mã sẽ làm. Tuy nhiên, điều đó là có thể (thay đổi bit vô hiệu hóa FIQ dường như rất có thể với tôi), vì vậy bạn nên xem xét nếu hệ thống của bạn thực hiện việc này.
Đây là cách tôi sẽ thực hiện những điều này theo cách giải quyết tất cả các vấn đề tiềm ẩn mà tôi đã chỉ ra:
#define ARM_INT_KEY_TYPE unsigned int
#define ARM_INT_LOCK(key_) \
asm volatile(\
"mrs %[key], cpsr\n\t"\
"ands %[key], %[key], #0xC0\n\t"\
"cpsid if\n\t" : [key]"=r"(key_) :: "memory", "cc" );
#define ARM_INT_UNLOCK(key_) asm volatile (\
"tst %[key], #0x40\n\t"\
"beq 0f\n\t"\
"cpsie f\n\t"\
"0: tst %[key], #0x80\n\t"\
"beq 1f\n\t"\
"cpsie i\n\t"
"1:\n\t" :: [key]"r" (key_) : "memory", "cc")
Đảm bảo biên dịch với -mcpu=cortex-a9
vì ít nhất một số phiên bản GCC (như của tôi) mặc định là CPU ARM cũ không hỗ trợ cpsie
và cpsid
.
Tôi đã sử dụng ands
thay vì chỉ and
trong ARM_INT_LOCK
một hướng dẫn 16 bit nếu điều này được sử dụng trong mã Thumb. Các "cc"
clobber là cần thiết dù sao, do đó, nó hoàn toàn là một lợi ích kích thước hiệu suất / code.
0
và 1
là nhãn địa phương , để tham khảo.
Chúng nên được sử dụng theo tất cả các cách giống như các phiên bản của bạn. Nó ARM_INT_LOCK
chỉ nhanh / nhỏ như bản gốc của bạn. Thật không may, tôi không thể đưa ra một cách để làm ARM_INT_UNLOCK
một cách an toàn ở bất cứ nơi nào gần như một vài hướng dẫn.
Nếu hệ thống của bạn có các ràng buộc về thời điểm IRQ và FIQ bị vô hiệu hóa, điều này có thể được đơn giản hóa. Ví dụ: nếu chúng luôn bị vô hiệu hóa cùng nhau, bạn có thể kết hợp thành một cbz
+ cpsie if
như thế này:
#define ARM_INT_UNLOCK(key_) asm volatile (\
"cbz %[key], 0f\n\t"\
"cpsie if\n\t"\
"0:\n\t" :: [key]"r" (key_) : "memory", "cc")
Ngoài ra, nếu bạn không quan tâm đến FIQ thì nó cũng tương tự như việc tắt bật / tắt hoàn toàn chúng.
Nếu bạn biết rằng không có gì khác thay đổi bất kỳ bit trạng thái nào khác trong CPSR giữa khóa và mở khóa, thì bạn cũng có thể sử dụng tiếp tục với một cái gì đó rất giống với mã gốc của bạn, ngoại trừ cả hai "memory"
và mã bị "cc"
chặn trong cả hai ARM_INT_LOCK
vàARM_INT_UNLOCK