مرجع API قابل اعتماد

Trusty API هایی را برای توسعه دو دسته از برنامه ها و خدمات ارائه می دهد:

  • برنامه ها و سرویس های قابل اعتمادی که روی پردازنده TEE اجرا می شوند
  • برنامه های معمولی و نامعتبر که روی پردازنده اصلی اجرا می شوند و از خدمات ارائه شده توسط برنامه های مورد اعتماد استفاده می کنند

Trusty API به طور کلی سیستم ارتباطات بین فرآیندی Trusty (IPC) را توصیف می کند، از جمله ارتباطات با دنیای غیر ایمن. نرم‌افزاری که روی پردازنده اصلی اجرا می‌شود می‌تواند از Trusty API برای اتصال به برنامه‌ها و سرویس‌های قابل اعتماد و تبادل پیام‌های دلخواه با آن‌ها درست مانند یک سرویس شبکه از طریق IP استفاده کند. این به برنامه بستگی دارد که قالب داده و معنایی این پیام ها را با استفاده از یک پروتکل در سطح برنامه تعیین کند. تحویل قابل اطمینان پیام ها توسط زیرساخت Trusty (به شکل درایورهایی که روی پردازنده اصلی اجرا می شوند) تضمین می شود و ارتباط کاملاً ناهمزمان است.

پورت ها و کانال ها

پورت ها توسط برنامه های Trusty استفاده می شوند تا نقاط پایانی سرویس را در قالب یک مسیر نامگذاری شده که مشتریان به آن متصل می شوند، نشان دهند. این یک شناسه سرویس ساده و مبتنی بر رشته را برای مشتریان ارائه می دهد. قرارداد نامگذاری، نامگذاری به سبک DNS معکوس است، به عنوان مثال com.google.servicename .

هنگامی که یک کلاینت به یک پورت متصل می شود، مشتری یک کانال برای تعامل با یک سرویس دریافت می کند. سرویس باید یک اتصال ورودی را بپذیرد، و زمانی که قبول کرد، یک کانال نیز دریافت می کند. در اصل، پورت ها برای جستجوی سرویس ها استفاده می شوند و سپس ارتباط از طریق یک جفت کانال متصل (یعنی نمونه های اتصال در یک پورت) رخ می دهد. هنگامی که یک کلاینت به یک پورت متصل می شود، یک اتصال متقارن و دو جهته برقرار می شود. با استفاده از این مسیر تمام دوبلکس، کلاینت ها و سرورها می توانند پیام های دلخواه مبادله کنند تا زمانی که هر یک از طرفین تصمیم به قطع اتصال بگیرند.

فقط برنامه‌های مورد اعتماد سمت امن یا ماژول‌های هسته Trusty می‌توانند پورت ایجاد کنند. برنامه های در حال اجرا در سمت غیر ایمن (در دنیای عادی) فقط می توانند به خدمات منتشر شده توسط سمت امن متصل شوند.

بسته به نیازها، یک برنامه قابل اعتماد می تواند همزمان مشتری و سرور باشد. یک برنامه قابل اعتماد که سرویسی را منتشر می کند (به عنوان سرور) ممکن است نیاز به اتصال به سرویس های دیگر (به عنوان مشتری) داشته باشد.

Handle API

دسته‌ها اعداد صحیح بدون علامت هستند که منابعی مانند پورت‌ها و کانال‌ها را نشان می‌دهند، مشابه توصیفگرهای فایل در یونیکس. پس از ایجاد دستگیره ها، آنها در یک جدول دسته مخصوص برنامه قرار می گیرند و می توانند بعداً به آنها ارجاع دهند.

تماس‌گیرنده می‌تواند داده‌های خصوصی را با استفاده از روش set_cookie() با یک دسته مرتبط کند.

روش‌ها در Handle API

دسته‌ها فقط در زمینه یک برنامه معتبر هستند. یک برنامه نباید مقدار یک دسته را به برنامه های دیگر منتقل کند مگر اینکه به صراحت مشخص شده باشد. فقط یک مقدار handle باید با مقایسه آن با INVALID_IPC_HANDLE #define, که یک برنامه می‌تواند به عنوان نشانه‌ای از نامعتبر بودن یا تنظیم نشده بودن یک دسته استفاده کند.

داده های خصوصی ارائه شده توسط تماس گیرنده را با یک دسته مشخص مرتبط می کند.

long set_cookie(uint32_t handle, void *cookie)

[in] handle : هر دسته ای که توسط یکی از فراخوانی های API برگردانده می شود

cookie [in]: اشاره‌گر به داده‌های فضای کاربر دلخواه در برنامه Trusty

[reval]: NO_ERROR در صورت موفقیت، < 0 کد خطا در غیر این صورت

این فراخوانی برای رسیدگی به رویدادها زمانی مفید است که در زمان دیگری پس از ایجاد دسته رخ دهند. مکانیزم مدیریت رویداد دسته و کوکی آن را به کنترل کننده رویداد برمی گرداند.

با استفاده از فراخوان wait() می‌توان برای رویدادها منتظر دستگیره‌ها بود.

صبر کن()

منتظر می ماند تا یک رویداد در یک دسته معین برای مدت زمان مشخصی رخ دهد.

long wait(uint32_t handle_id, uevent_t *event, unsigned long timeout_msecs)

[in] handle_id : هر دسته ای که توسط یکی از فراخوانی های API برگردانده می شود

event [out] : اشاره گر به ساختار که نشان دهنده رویدادی است که روی این دسته رخ داده است

[in] timeout_msecs : مقدار وقفه در میلی ثانیه. مقدار -1 یک مهلت نامحدود است

[reval]: NO_ERROR اگر یک رویداد معتبر در یک بازه زمانی مشخص رخ داده باشد. ERR_TIMED_OUT اگر مهلت زمانی مشخصی سپری شده باشد اما هیچ رویدادی رخ نداده باشد. < 0 برای سایر خطاها

پس از موفقیت ( retval == NO_ERROR )، فراخوانی wait() یک ساختار مشخص شده uevent_t را با اطلاعات مربوط به رویدادی که رخ داده است پر می کند.

typedef struct uevent {
    uint32_t handle; /* handle this event is related to */
    uint32_t event;  /* combination of IPC_HANDLE_POLL_XXX flags */
    void    *cookie; /* cookie associated with this handle */
} uevent_t;

فیلد event حاوی ترکیبی از مقادیر زیر است:

enum {
  IPC_HANDLE_POLL_NONE    = 0x0,
  IPC_HANDLE_POLL_READY   = 0x1,
  IPC_HANDLE_POLL_ERROR   = 0x2,
  IPC_HANDLE_POLL_HUP     = 0x4,
  IPC_HANDLE_POLL_MSG     = 0x8,
  IPC_HANDLE_POLL_SEND_UNBLOCKED = 0x10,
  … more values[TBD]
};

IPC_HANDLE_POLL_NONE - هیچ رویدادی واقعاً معلق نیست، تماس‌گیرنده باید انتظار را دوباره راه‌اندازی کند

IPC_HANDLE_POLL_ERROR - یک خطای داخلی نامشخص رخ داده است

IPC_HANDLE_POLL_READY - به نوع دسته بستگی دارد، به شرح زیر:

  • برای پورت ها، این مقدار نشان می دهد که یک اتصال معلق وجود دارد
  • برای کانال ها، این مقدار نشان می دهد که یک اتصال ناهمزمان برقرار شده است (به connect() ) مراجعه کنید

رویدادهای زیر فقط مربوط به کانال‌ها هستند:

  • IPC_HANDLE_POLL_HUP - نشان می دهد که یک کانال توسط یک همتا بسته شده است
  • IPC_HANDLE_POLL_MSG - نشان می دهد که یک پیام در حال انتظار برای این کانال وجود دارد
  • IPC_HANDLE_POLL_SEND_UNBLOCKED - نشان می‌دهد که تماس‌گیرنده‌ای که قبلاً از طریق ارسال مسدود شده است ممکن است دوباره تلاش کند پیامی ارسال کند (برای جزئیات به توضیح send_msg() مراجعه کنید)

یک کنترل کننده رویداد باید برای مدیریت ترکیبی از رویدادهای مشخص شده آماده باشد، زیرا ممکن است چندین بیت به طور همزمان تنظیم شوند. به عنوان مثال، برای یک کانال، ممکن است همزمان پیام های معلق و اتصال توسط یک همتا بسته شود.

بیشتر رویدادها چسبنده هستند. آنها تا زمانی که شرایط اساسی ادامه دارد باقی می مانند (به عنوان مثال همه پیام های معلق دریافت می شوند و درخواست های اتصال در انتظار رسیدگی می شوند). استثنا مورد رویداد IPC_HANDLE_POLL_SEND_UNBLOCKED است که پس از خواندن پاک می‌شود و برنامه فقط یک فرصت برای مدیریت آن دارد.

دسته ها را می توان با فراخوانی متد close() از بین برد.

بستن ()

منبع مرتبط با دسته مشخص شده را از بین می برد و آن را از جدول دسته حذف می کند.

long close(uint32_t handle_id);

[in] handle_id : دسته برای از بین بردن

[بازیابی]: 0 در صورت موفقیت؛ در غیر این صورت یک خطای منفی

API سرور

یک سرور با ایجاد یک یا چند پورت با نام که نشان دهنده نقاط پایانی سرویس آن است شروع می شود. هر پورت با یک دسته نمایش داده می شود.

روش‌ها در API سرور

port_create()

یک پورت سرویس با نام ایجاد می کند.

long port_create (const char *path, uint num_recv_bufs, size_t recv_buf_size,
uint32_t flags)

[in] path : نام رشته پورت (همانطور که در بالا توضیح داده شد). این نام باید در سراسر سیستم منحصر به فرد باشد. تلاش برای ایجاد یک نسخه تکراری با شکست مواجه شد.

[in] num_recv_bufs : حداکثر تعداد بافرهایی که یک کانال در این پورت می تواند از قبل برای تسهیل تبادل داده با مشتری اختصاص دهد. بافرها به طور جداگانه برای داده هایی که در هر دو جهت حرکت می کنند شمارش می شوند، بنابراین مشخص کردن 1 در اینجا به این معنی است که 1 ارسال و 1 بافر دریافت از قبل تخصیص داده شده است. به طور کلی، تعداد بافرهای مورد نیاز به توافق پروتکل سطح بالاتر بین مشتری و سرور بستگی دارد. در صورت وجود یک پروتکل بسیار همگام، این عدد می تواند به 1 برسد (پیام ارسال کنید، قبل از ارسال دیگری پاسخ دریافت کنید). اما اگر مشتری انتظار داشته باشد که قبل از نمایش پاسخ، بیش از یک پیام ارسال کند (مثلاً یک پیام به عنوان مقدمه و دیگری به عنوان دستور واقعی) این عدد می تواند بیشتر باشد. مجموعه‌های بافر اختصاص‌یافته در هر کانال هستند، بنابراین دو اتصال (کانال) مجزا دارای مجموعه‌های بافر جداگانه خواهند بود.

[in] recv_buf_size : حداکثر اندازه هر بافر مجزا در مجموعه بافر بالا. این مقدار وابسته به پروتکل است و به طور موثر حداکثر اندازه پیامی را که می توانید با همتایان مبادله کنید محدود می کند

[in] flags : ترکیبی از پرچم ها که رفتار پورت اضافی را مشخص می کند

این مقدار باید ترکیبی از مقادیر زیر باشد:

IPC_PORT_ALLOW_TA_CONNECT - امکان اتصال از سایر برنامه های امن را فراهم می کند

IPC_PORT_ALLOW_NS_CONNECT - امکان اتصال از دنیای غیر ایمن را فراهم می کند

[retval]: در صورت غیر منفی بودن به پورت ایجاد شده یا در صورت منفی بودن یک خطای خاص رسیدگی کنید

سپس سرور لیستی از دستگیره‌های پورت را برای اتصالات ورودی با استفاده از فراخوان wait() نظرسنجی می‌کند. به محض دریافت یک درخواست اتصال که توسط بیت IPC_HANDLE_POLL_READY در فیلد event ساختار uevent_t نشان داده شده است، سرور باید برای پایان دادن به برقراری اتصال و ایجاد یک کانال (که توسط دسته دیگری ارائه می‌شود) که می‌تواند برای پیام‌های دریافتی نظرسنجی شود، accept() کند. .

قبول ()

اتصال ورودی را می پذیرد و به یک کانال دسترسی پیدا می کند.

long accept(uint32_t handle_id, uuid_t *peer_uuid);

[in] handle_id : دسته ای که نشان دهنده پورتی است که کلاینت به آن متصل شده است

[out] peer_uuid : اشاره‌گر به ساختار uuid_t است که باید با UUID برنامه مشتری متصل پر شود. اگر اتصال از دنیای ناامن منشا گرفته باشد، روی تمام صفرها تنظیم می شود

[reval]: به ​​کانالی (اگر غیرمنفی) است که در آن سرور می‌تواند پیام‌ها را با مشتری مبادله کند (یا کد خطا در غیر این صورت)

Client API

این بخش شامل متدهای موجود در Client API است.

روش‌ها در Client API

اتصال ()

اتصال به پورت مشخص شده با نام را آغاز می کند.

long connect(const char *path, uint flags);

[in] path : نام پورتی که توسط یک برنامه Trusty منتشر شده است

[in] flags : رفتار اضافی و اختیاری را مشخص می کند

[retval]: انتقال به کانالی که می‌توان از طریق آن پیام‌ها را با سرور رد و بدل کرد. خطا در صورت منفی

اگر هیچ flags مشخص نشده باشد (پارامتر flags روی 0 تنظیم شده است)، فراخوانی connect() یک اتصال همزمان به یک پورت مشخص را آغاز می کند که اگر پورت وجود نداشته باشد بلافاصله یک خطا را برمی گرداند و یک بلوک ایجاد می کند تا زمانی که سرور در غیر این صورت یک مورد را بپذیرد. اتصال

این رفتار را می توان با تعیین ترکیبی از دو مقدار که در زیر توضیح داده شده است تغییر داد:

enum {
IPC_CONNECT_WAIT_FOR_PORT = 0x1,
IPC_CONNECT_ASYNC = 0x2,
};

IPC_CONNECT_WAIT_FOR_PORT - اگر پورت مشخص شده بلافاصله در هنگام اجرا وجود نداشته باشد، به جای اینکه فوراً از کار بیفتد، یک فراخوان connect() را وادار می کند که منتظر بماند.

IPC_CONNECT_ASYNC - اگر تنظیم شود، یک اتصال ناهمزمان را آغاز می کند. قبل از شروع عملیات عادی، یک برنامه باید با فراخوانی wait() برای یک رویداد تکمیل اتصال که توسط بیت IPC_HANDLE_POLL_READY تنظیم شده در قسمت رویداد ساختار uevent_t تنظیم شده است، نظرسنجی کند.

API پیام

تماس‌های API پیام‌رسانی، ارسال و خواندن پیام‌ها را از طریق یک اتصال (کانال) از قبل ایجاد شده امکان‌پذیر می‌سازد. تماس‌های API Messaging برای سرورها و کلاینت‌ها یکسان است.

یک کلاینت با صدور یک تماس connect() یک handle به یک کانال دریافت می کند و یک سرور از یک call accept() که در بالا توضیح داده شد، یک handle کانال دریافت می کند.

ساختار یک پیام مطمئن

همانطور که در زیر نشان داده شده است، پیام‌های رد و بدل شده توسط Trusty API ساختاری حداقلی دارند و این امر را به سرور و کلاینت واگذار می‌کند تا درباره معنایی محتوای واقعی توافق کنند:

/*
 *  IPC message
 */
typedef struct iovec {
        void   *base;
        size_t  len;
} iovec_t;

typedef struct ipc_msg {
        uint     num_iov; /* number of iovs in this message */
        iovec_t  *iov;    /* pointer to iov array */

        uint     num_handles; /* reserved, currently not supported */
        handle_t *handles;    /* reserved, currently not supported */
} ipc_msg_t;

یک پیام می تواند از یک یا چند بافر غیر پیوسته تشکیل شده باشد که توسط آرایه ای از ساختارهای iovec_t نمایش داده می شود. Trusty با استفاده از آرایه iov خواندن و نوشتن scatter-gather را در این بلوک ها انجام می دهد. محتوای بافرهایی که می توان با آرایه iov توصیف کرد کاملا دلخواه است.

روش‌ها در API پیام‌رسانی

send_msg()

پیامی را از طریق یک کانال مشخص ارسال می کند.

long send_msg(uint32_t handle, ipc_msg_t *msg);

[in] handle : به کانالی که پیام را از طریق آن ارسال می‌کنید

[in] msg : اشاره گر به ipc_msg_t structure که پیام را توصیف می کند

[reval]: تعداد کل بایت های ارسال شده در زمان موفقیت. در غیر این صورت یک خطای منفی

اگر کلاینت (یا سرور) بخواهد پیامی را از طریق کانال ارسال کند و فضایی در صف پیام همتای مقصد وجود نداشته باشد، ممکن است کانال وارد حالت مسدود شده ارسال شود (این هرگز نباید برای یک پروتکل درخواست/پاسخ همزمان ساده اتفاق بیفتد. اما ممکن است در موارد پیچیده‌تر اتفاق بیفتد) که با بازگرداندن کد خطای ERR_NOT_ENOUGH_BUFFER نشان داده می‌شود. در چنین حالتی، تماس‌گیرنده باید منتظر بماند تا همتا با بازیابی پیام‌های مدیریت و بازنشستگی، که با بیت IPC_HANDLE_POLL_SEND_UNBLOCKED در فیلد event ساختار uevent_t بازگردانده شده توسط wait() برگردانده شده است، مقداری فضای در صف دریافت خود آزاد کند.

get_msg()

متا اطلاعات را در مورد پیام بعدی در صف پیام ورودی دریافت می کند

از یک کانال مشخص

long get_msg(uint32_t handle, ipc_msg_info_t *msg_info);

[in] handle : دسته کانالی که باید پیام جدیدی در آن بازیابی شود

[out] msg_info : ساختار اطلاعات پیام به شرح زیر است:

typedef struct ipc_msg_info {
        size_t    len;  /* total message length */
        uint32_t  id;   /* message id */
} ipc_msg_info_t;

به هر پیام یک شناسه منحصربه‌فرد در میان مجموعه پیام‌های برجسته اختصاص داده می‌شود و طول کل هر پیام پر می‌شود. در صورت پیکربندی و اجازه توسط پروتکل، می‌توان چندین پیام برجسته (بازشده) را به طور همزمان برای یک کانال خاص وجود داشته باشد.

[reval]: NO_ERROR در موفقیت؛ در غیر این صورت یک خطای منفی

read_msg()

محتوای پیام را با شناسه مشخص شده با شروع از افست مشخص شده می خواند.

long read_msg(uint32_t handle, uint32_t msg_id, uint32_t offset, ipc_msg_t
*msg);

[in] handle : دسته کانالی که از آن پیام را می‌خوانید

[in] msg_id : شناسه پیام برای خواندن

[in] offset : در پیامی که از آن شروع به خواندن می شود جابجا می شود

[out] msg : اشاره‌گر به ساختار ipc_msg_t مجموعه‌ای از بافرها را توصیف می‌کند که داده‌های پیام ورودی را در آن ذخیره می‌کند.

[retval]: تعداد کل بایت های ذخیره شده در بافرهای msg در هنگام موفقیت. در غیر این صورت یک خطای منفی

روش read_msg را می‌توان چندین بار فراخوانی کرد و در صورت نیاز با یک افست متفاوت (نه لزوماً ترتیبی) شروع می‌شود.

put_msg()

پیامی را با شناسه مشخص بازنشانی می کند.

long put_msg(uint32_t handle, uint32_t msg_id);

[in] handle : دسته کانالی که پیام به آن رسیده است

[in] msg_id : شناسه پیام در حال بازنشستگی

[reval]: NO_ERROR در موفقیت؛ در غیر این صورت یک خطای منفی

پس از بازنشستگی پیام و بافری که آن را اشغال کرده آزاد شد، نمی توان به محتوای پیام دسترسی داشت.

File Descriptor API

File Descriptor API شامل فراخوانی های read() ، write() و ioctl() می باشد. همه این فراخوان ها می توانند بر روی یک مجموعه از پیش تعریف شده (ایستا) از توصیفگرهای فایل که به طور سنتی با اعداد کوچک نمایش داده می شوند، عمل کنند. در پیاده سازی فعلی، فضای توصیفگر فایل جدا از فضای دسته IPC است. File Descriptor API در Trusty شبیه یک API سنتی مبتنی بر توصیفگر فایل است.

به طور پیش فرض، 3 توصیف کننده فایل از پیش تعریف شده (استاندارد و شناخته شده) وجود دارد:

  • 0 - ورودی استاندارد. اجرای پیش‌فرض ورودی استاندارد fd بدون عملیات است (چون انتظار نمی‌رود برنامه‌های مورد اعتماد کنسول تعاملی داشته باشند) بنابراین خواندن، نوشتن یا فراخوانی ioctl() در fd 0 باید یک خطای ERR_NOT_SUPPORTED را برگرداند.
  • 1 - خروجی استاندارد. بسته به پلتفرم و پیکربندی، داده های نوشته شده در خروجی استاندارد را می توان (بسته به سطح اشکال زدایی LK) به UART و/یا گزارش حافظه موجود در سمت غیر ایمن هدایت کرد. گزارش‌ها و پیام‌های اشکال‌زدایی غیر مهم باید در خروجی استاندارد قرار بگیرند. متدهای read() و ioctl() no-ops هستند و باید یک خطای ERR_NOT_SUPPORTED را برگردانند.
  • 2- خطای استاندارد. بسته به پلت فرم و پیکربندی، داده های نوشته شده با خطای استاندارد باید به UART یا گزارش حافظه موجود در سمت غیر ایمن هدایت شوند. توصیه می‌شود فقط پیام‌های مهم را با خطای استاندارد بنویسید، زیرا این جریان به احتمال زیاد از بین می‌رود. متدهای read() و ioctl() no-ops هستند و باید یک خطای ERR_NOT_SUPPORTED را برگردانند.

حتی اگر این مجموعه از توصیف‌گرهای فایل را می‌توان برای پیاده‌سازی fds بیشتر (برای اجرای پسوندهای خاص پلتفرم) گسترش داد، گسترش توصیف‌گرهای فایل باید با احتیاط اعمال شود. گسترش توصیف کننده فایل مستعد ایجاد تضاد است و به طور کلی توصیه نمی شود.

روش‌ها در File Descriptor API

خواندن ()

تلاش برای خواندن تا count بایت داده از یک توصیفگر فایل مشخص.

long read(uint32_t fd, void *buf, uint32_t count);

[in] fd : توصیف کننده فایل که از آن می توان خواند

[out] buf : اشاره گر به بافری است که داده ها را در آن ذخیره می کند

[in] count : حداکثر تعداد بایت برای خواندن

[retval]: تعداد بایت های برگردانده شده خوانده شده. در غیر این صورت یک خطای منفی

نوشتن ()

می‌نویسد تا count بایت‌های داده در توصیف‌گر فایل مشخص شده.

long write(uint32_t fd, void *buf, uint32_t count);

[in] fd : توصیف کننده فایلی که باید روی آن نوشته شود

[out] buf : اشاره گر به داده برای نوشتن

[in] count : حداکثر تعداد بایت برای نوشتن

[retval]: تعداد بایت های برگردانده شده نوشته شده. در غیر این صورت یک خطای منفی

ioctl()

یک دستور ioctl مشخص را برای یک توصیفگر فایل معین فراخوانی می کند.

long ioctl(uint32_t fd, uint32_t cmd, void *args);

[in] fd : توصیف کننده فایلی که در آن ioctl() فراخوانی شود.

[in] cmd : دستور ioctl

[in/out] args : اشاره گر به آرگومان های ioctl()

API متفرقه

روش‌ها در API متفرقه

gettime()

زمان فعلی سیستم (بر حسب نانوثانیه) را برمی‌گرداند.

long gettime(uint32_t clock_id, uint32_t flags, int64_t *time);

[in] clock_id : وابسته به پلتفرم؛ برای پیش فرض صفر را پاس کنید

[in] flags : رزرو شده، باید صفر باشد

[out] time : اشاره‌گر به یک مقدار int64_t است که زمان فعلی را روی آن ذخیره می‌کند

[reval]: NO_ERROR در موفقیت؛ در غیر این صورت یک خطای منفی

nanosleep()

اجرای برنامه تماس گیرنده را برای مدت زمان مشخصی به حالت تعلیق در می آورد و پس از آن مدت آن را از سر می گیرد.

long nanosleep(uint32_t clock_id, uint32_t flags, uint64_t sleep_time)

[in] clock_id : رزرو شده، باید صفر باشد

[in] flags : رزرو شده، باید صفر باشد

[in] sleep_time : زمان خواب در نانوثانیه

[reval]: NO_ERROR در موفقیت؛ در غیر این صورت یک خطای منفی

نمونه ای از سرور برنامه قابل اعتماد

برنامه نمونه زیر استفاده از APIهای بالا را نشان می دهد. نمونه یک سرویس «اکو» ایجاد می‌کند که چندین اتصال ورودی را مدیریت می‌کند و همه پیام‌هایی را که از مشتریان دریافت می‌کند از سمت امن یا غیرایمن دریافت می‌کند، به تماس‌گیرنده منعکس می‌کند.

#include <uapi/err.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <trusty_ipc.h>
#define LOG_TAG "echo_srv"
#define TLOGE(fmt, ...) \
    fprintf(stderr, "%s: %d: " fmt, LOG_TAG, __LINE__, ##__VA_ARGS__)

# define MAX_ECHO_MSG_SIZE 64

static
const char * srv_name = "com.android.echo.srv.echo";

static uint8_t msg_buf[MAX_ECHO_MSG_SIZE];

/*
 *  Message handler
 */
static int handle_msg(handle_t chan) {
  int rc;
  struct iovec iov;
  ipc_msg_t msg;
  ipc_msg_info_t msg_inf;

  iov.iov_base = msg_buf;
  iov.iov_len = sizeof(msg_buf);

  msg.num_iov = 1;
  msg.iov = &iov;
  msg.num_handles = 0;
  msg.handles = NULL;

  /* get message info */
  rc = get_msg(chan, &msg_inf);
  if (rc == ERR_NO_MSG)
    return NO_ERROR; /* no new messages */

  if (rc != NO_ERROR) {
    TLOGE("failed (%d) to get_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  /* read msg content */
  rc = read_msg(chan, msg_inf.id, 0, &msg);
  if (rc < 0) {
    TLOGE("failed (%d) to read_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  /* update number of bytes received */
  iov.iov_len = (size_t) rc;

  /* send message back to the caller */
  rc = send_msg(chan, &msg);
  if (rc < 0) {
    TLOGE("failed (%d) to send_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  /* retire message */
  rc = put_msg(chan, msg_inf.id);
  if (rc != NO_ERROR) {
    TLOGE("failed (%d) to put_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  return NO_ERROR;
}

/*
 *  Channel event handler
 */
static void handle_channel_event(const uevent_t * ev) {
  int rc;

  if (ev->event & IPC_HANDLE_POLL_MSG) {
    rc = handle_msg(ev->handle);
    if (rc != NO_ERROR) {
      /* report an error and close channel */
      TLOGE("failed (%d) to handle event on channel %d\n",
        rc, ev->handle);
      close(ev->handle);
    }
    return;
  }
  if (ev->event & IPC_HANDLE_POLL_HUP) {
    /* closed by peer. */
    close(ev->handle);
    return;
  }
}

/*
 *  Port event handler
 */
static void handle_port_event(const uevent_t * ev) {
  uuid_t peer_uuid;

  if ((ev->event & IPC_HANDLE_POLL_ERROR) ||
    (ev->event & IPC_HANDLE_POLL_HUP) ||
    (ev->event & IPC_HANDLE_POLL_MSG) ||
    (ev->event & IPC_HANDLE_POLL_SEND_UNBLOCKED)) {
    /* should never happen with port handles */
    TLOGE("error event (0x%x) for port (%d)\n",
      ev->event, ev->handle);
    abort();
  }
  if (ev->event & IPC_HANDLE_POLL_READY) {
    /* incoming connection: accept it */
    int rc = accept(ev->handle, &peer_uuid);
    if (rc < 0) {
      TLOGE("failed (%d) to accept on port %d\n",
        rc, ev->handle);
      return;
    }
    handle_t chan = rc;
    while (true){
      struct uevent cev;

      rc = wait(chan, &cev, INFINITE_TIME);
      if (rc < 0) {
        TLOGE("wait returned (%d)\n", rc);
        abort();
      }
      handle_channel_event(&cev);
      if (cev.event & IPC_HANDLE_POLL_HUP) {
        return;
      }
    }
  }
}


/*
 *  Main app entry point
 */
int main(void) {
  int rc;
  handle_t port;

  /* Initialize service */
  rc = port_create(srv_name, 1, MAX_ECHO_MSG_SIZE,
    IPC_PORT_ALLOW_NS_CONNECT |
    IPC_PORT_ALLOW_TA_CONNECT);
  if (rc < 0) {
    TLOGE("Failed (%d) to create port %s\n",
      rc, srv_name);
    abort();
  }
  port = (handle_t) rc;

  /* enter main event loop */
  while (true) {
    uevent_t ev;

    ev.handle = INVALID_IPC_HANDLE;
    ev.event = 0;
    ev.cookie = NULL;

    /* wait forever */
    rc = wait(port, &ev, INFINITE_TIME);
    if (rc == NO_ERROR) {
      /* got an event */
      handle_port_event(&ev);
    } else {
      TLOGE("wait returned (%d)\n", rc);
      abort();
    }
  }
  return 0;
}

متد run_end_to_end_msg_test() 10000 پیام را به صورت ناهمزمان به سرویس "echo" ارسال می کند و پاسخ ها را مدیریت می کند.

static int run_echo_test(void)
{
  int rc;
  handle_t chan;
  uevent_t uevt;
  uint8_t tx_buf[64];
  uint8_t rx_buf[64];
  ipc_msg_info_t inf;
  ipc_msg_t   tx_msg;
  iovec_t     tx_iov;
  ipc_msg_t   rx_msg;
  iovec_t     rx_iov;

  /* prepare tx message buffer */
  tx_iov.base = tx_buf;
  tx_iov.len  = sizeof(tx_buf);
  tx_msg.num_iov = 1;
  tx_msg.iov     = &tx_iov;
  tx_msg.num_handles = 0;
  tx_msg.handles = NULL;

  memset (tx_buf, 0x55, sizeof(tx_buf));

  /* prepare rx message buffer */
  rx_iov.base = rx_buf;
  rx_iov.len  = sizeof(rx_buf);
  rx_msg.num_iov = 1;
  rx_msg.iov     = &rx_iov;
  rx_msg.num_handles = 0;
  rx_msg.handles = NULL;

  /* open connection to echo service */
  rc = sync_connect(srv_name, 1000);
  if(rc < 0)
    return rc;

  /* got channel */
  chan = (handle_t)rc;

  /* send/receive 10000 messages asynchronously. */
  uint tx_cnt = 10000;
  uint rx_cnt = 10000;

  while (tx_cnt || rx_cnt) {
    /* send messages until all buffers are full */
while (tx_cnt) {
    rc = send_msg(chan, &tx_msg);
      if (rc == ERR_NOT_ENOUGH_BUFFER)
      break;  /* no more space */
    if (rc != 64) {
      if (rc > 0) {
        /* incomplete send */
        rc = ERR_NOT_VALID;
}
      goto abort_test;
}
    tx_cnt--;
  }

  /* wait for reply msg or room */
  rc = wait(chan, &uevt, 1000);
  if (rc != NO_ERROR)
    goto abort_test;

  /* drain all messages */
  while (rx_cnt) {
    /* get a reply */
      rc = get_msg(chan, &inf);
    if (rc == ERR_NO_MSG)
        break;  /* no more messages  */
  if (rc != NO_ERROR)
goto abort_test;

  /* read reply data */
    rc = read_msg(chan, inf.id, 0, &rx_msg);
  if (rc != 64) {
    /* unexpected reply length */
    rc = ERR_NOT_VALID;
    goto abort_test;
}

  /* discard reply */
  rc = put_msg(chan, inf.id);
  if (rc != NO_ERROR)
    goto abort_test;
  rx_cnt--;
  }
}

abort_test:
  close(chan);
  return rc;
}

APIها و برنامه‌های جهانی غیرایمن

مجموعه‌ای از سرویس‌های Trusty که از سمت امن منتشر شده و با ویژگی IPC_PORT_ALLOW_NS_CONNECT مشخص شده‌اند، برای برنامه‌های فضایی هسته و کاربر که در سمت غیر ایمن اجرا می‌شوند، قابل دسترسی هستند.

محیط اجرا در سمت غیر امن (هسته و فضای کاربر) به شدت با محیط اجرا در سمت امن متفاوت است. بنابراین، به جای یک کتابخانه واحد برای هر دو محیط، دو مجموعه متفاوت از API وجود دارد. در هسته، Client API توسط درایور کرنل trusty-ipc ارائه می‌شود و یک گره دستگاه کاراکتری را ثبت می‌کند که می‌تواند توسط فرآیندهای فضای کاربر برای ارتباط با سرویس‌های در حال اجرا در سمت امن استفاده شود.

فضای کاربری Trusty IPC Client API

فضای کاربر کتابخانه Trusty IPC Client API یک لایه نازک در بالای گره دستگاه fd است.

یک برنامه فضایی کاربر یک جلسه ارتباطی را با فراخوانی tipc_connect() شروع می‌کند و اتصال به یک سرویس Trusty مشخص شده را راه‌اندازی می‌کند. در داخل، فراخوانی tipc_connect() یک گره دستگاه مشخص را برای به دست آوردن یک توصیفگر فایل باز می کند و یک فراخوانی TIPC_IOC_CONNECT ioctl() را با پارامتر argp به رشته ای اشاره می کند که حاوی نام سرویسی است که باید به آن متصل شود.

#define TIPC_IOC_MAGIC  'r'
#define TIPC_IOC_CONNECT  _IOW(TIPC_IOC_MAGIC, 0x80, char *)

توصیفگر فایل حاصل تنها می تواند برای برقراری ارتباط با سرویسی که برای آن ایجاد شده است استفاده شود. وقتی دیگر نیازی به اتصال نیست، توصیفگر فایل باید با فراخوانی tipc_close() بسته شود.

توصیفگر فایل بدست آمده توسط فراخوانی tipc_connect() مانند یک گره دستگاه کاراکتر معمولی رفتار می کند. توصیف کننده فایل:

  • در صورت نیاز می توان آن را به حالت غیر مسدود کننده تغییر داد
  • می توان با استفاده از یک فراخوان استاندارد write() برای ارسال پیام به طرف دیگر نوشت
  • می توان برای در دسترس بودن پیام های دریافتی به عنوان یک توصیفگر فایل معمولی، نظرسنجی (با استفاده از تماس های poll() یا تماس های select() )
  • برای بازیابی پیام های دریافتی قابل خواندن است

تماس گیرنده با اجرای یک تماس نوشتن برای fd مشخص شده، پیامی به سرویس Trusty ارسال می کند. تمام داده های ارسال شده به write() write بالا توسط درایور trusty-ipc به پیام تبدیل می شود. پیام به سمت امنی تحویل داده می شود که در آن داده ها توسط زیرسیستم IPC در هسته Trusty مدیریت می شود و به مقصد مناسب هدایت می شود و به عنوان یک رویداد IPC_HANDLE_POLL_MSG در یک دسته کانال خاص به حلقه رویداد برنامه تحویل می شود. بسته به پروتکل خاص و ویژه سرویس، سرویس Trusty ممکن است یک یا چند پیام پاسخ ارسال کند که به سمت غیر ایمن تحویل داده می‌شوند و در صف پیام توصیف‌کننده فایل کانال مناسب قرار می‌گیرند تا توسط اپلیکیشن فضای کاربر read() تماس بگیرید.

tipc_connect()

یک گره دستگاه tipc مشخص شده را باز می کند و اتصال به یک سرویس Trusty مشخص را آغاز می کند.

int tipc_connect(const char *dev_name, const char *srv_name);

[in] dev_name : مسیری برای باز کردن گره دستگاه Trusty IPC

[in] srv_name : نام یک سرویس Trusty منتشر شده برای اتصال به آن

[retval]: توصیف کننده فایل معتبر در مورد موفقیت، -1 در غیر این صورت.

tipc_close()

اتصال به سرویس Trusty مشخص شده توسط یک توصیفگر فایل را می بندد.

int tipc_close(int fd);

[in] fd : توصیف کننده فایلی که قبلاً با یک فراخوانی tipc_connect() باز شده است

Kernel Trusty IPC Client API

هسته Trusty IPC Client API برای درایورهای هسته در دسترس است. فضای کاربری Trusty IPC API در بالای این API پیاده سازی شده است.

به طور کلی، استفاده معمولی از این API شامل ایجاد یک شی struct tipc_chan توسط تماس گیرنده با استفاده از تابع tipc_create_channel() و سپس استفاده از فراخوانی tipc_chan_connect() برای شروع اتصال به سرویس Trusty IPC است که در سمت امن اجرا می‌شود. اتصال به سمت راه دور را می توان با فراخوانی tipc_chan_shutdown() و سپس tipc_chan_destroy() برای پاکسازی منابع قطع کرد.

پس از دریافت یک اعلان (از طریق callback handle_event() ) مبنی بر اینکه یک اتصال با موفقیت برقرار شده است، تماس گیرنده کارهای زیر را انجام می دهد:

  • با استفاده از تماس tipc_chan_get_txbuf_timeout() یک بافر پیام به دست می آورد
  • یک پیام می نویسد، و
  • پیام را با استفاده از روش tipc_chan_queue_msg() برای تحویل به یک سرویس Trusty (در سمت امن)، که کانال به آن متصل است، در صف قرار می دهد.

پس از موفقیت آمیز بودن صف، تماس گیرنده باید بافر پیام را فراموش کند زیرا بافر پیام در نهایت پس از پردازش توسط سمت راه دور (برای استفاده مجدد در آینده، برای پیام های دیگر) به مخزن بافر آزاد باز می گردد. کاربر فقط باید tipc_chan_put_txbuf() فراخوانی کند اگر نتواند چنین بافری را در صف قرار دهد یا دیگر نیازی به آن نباشد.

یک کاربر API پیام‌ها را از سمت راه دور دریافت می‌کند و با مدیریت یک تماس اعلان handle_msg() (که در چارچوب صف کاری trusty-ipc rx فراخوانی می‌شود) که یک اشاره‌گر به یک بافر rx حاوی یک پیام دریافتی برای رسیدگی ارائه می‌کند.

انتظار می‌رود پیاده‌سازی callback handle_msg() یک اشاره‌گر را به یک struct tipc_msg_buf برگرداند. اگر به صورت محلی مدیریت شود و دیگر مورد نیاز نباشد، می تواند مانند بافر پیام ورودی باشد. از طرف دیگر، اگر بافر ورودی برای پردازش بیشتر در صف قرار گیرد، می‌تواند یک بافر جدید باشد که توسط یک فراخوانی tipc_chan_get_rxbuf() بدست می‌آید. یک بافر rx جدا شده باید ردیابی شود و در نهایت با استفاده از یک فراخوانی tipc_chan_put_rxbuf() زمانی که دیگر مورد نیاز نیست آزاد شود.

روش‌ها در Kernel Trusty IPC Client API

tipc_create_channel()

نمونه ای از کانال IPC Trusty را برای یک دستگاه مطمئن-ipc خاص ایجاد و پیکربندی می کند.

struct tipc_chan *tipc_create_channel(struct device *dev,
                          const struct tipc_chan_ops *ops,
                              void *cb_arg);

[in] dev : اشاره گر به ipc قابل اعتمادی که کانال دستگاه برای آن ایجاد شده است

[in] ops : اشاره‌گر به struct tipc_chan_ops ، با تماس‌های تماس‌گیرنده خاص پر شده است

[in] cb_arg : اشاره‌گر به داده‌هایی که به تماس‌های tipc_chan_ops ارسال می‌شوند

[retval]: اشاره‌گر به یک نمونه جدید ایجاد شده از struct tipc_chan در مورد موفقیت، در غیر این صورت ERR_PTR(err)

به طور کلی، یک تماس گیرنده باید دو فراخوانی را ارائه دهد که به طور ناهمزمان در هنگام وقوع فعالیت مربوطه فراخوانی شوند.

رویداد void (*handle_event)(void *cb_arg, int event) برای اطلاع تماس گیرنده در مورد تغییر وضعیت کانال فراخوانی می شود.

[in] cb_arg : اشاره گر به داده ارسال شده به تماس tipc_create_channel()

[in] event : رویدادی که می تواند یکی از مقادیر زیر باشد:

  • TIPC_CHANNEL_CONNECTED - اتصال موفقیت آمیز به سمت راه دور را نشان می دهد
  • TIPC_CHANNEL_DISCONNECTED - نشان می دهد که طرف راه دور درخواست اتصال جدید را رد کرده یا درخواست قطع اتصال برای کانال قبلی را داده است.
  • TIPC_CHANNEL_SHUTDOWN - نشان می دهد که سمت راه دور در حال خاموش شدن است و به طور دائم تمام اتصالات قطع می شود

struct tipc_msg_buf *(*handle_msg)(void *cb_arg, struct tipc_msg_buf *mb) فراخوانی فراخوانی می شود تا اعلان دریافت پیام جدید از طریق یک کانال مشخص را ارائه دهد:

  • [in] cb_arg : اشاره گر به داده ارسال شده به تماس tipc_create_channel()
  • [in] mb : اشاره گر به struct tipc_msg_buf که پیام ورودی را توصیف می کند
  • [retval]: انتظار می‌رود اجرای callback یک اشاره‌گر را به struct tipc_msg_buf برگرداند که می‌تواند همان اشاره‌گر دریافت‌شده به عنوان پارامتر mb باشد، اگر پیام به صورت محلی مدیریت شود و دیگر مورد نیاز نباشد (یا می‌تواند یک بافر جدید باشد که توسط تماس tipc_chan_get_rxbuf() )

tipc_chan_connect()

اتصال به سرویس Trusty IPC مشخص شده را آغاز می کند.

int tipc_chan_connect(struct tipc_chan *chan, const char *port);

[in] chan : اشاره گر به کانالی است که با تماس tipc_create_chan() نشانگر است

[in] port : اشاره گر به رشته ای حاوی نام سرویسی است که باید به آن متصل شود

[reval]: 0 در موفقیت، یک خطای منفی در غیر این صورت

هنگامی که یک اتصال برقرار می شود با دریافت یک تماس handle_event به تماس گیرنده اطلاع داده می شود.

tipc_chan_shutdown()

اتصال به سرویس Trusty IPC را که قبلاً با تماس tipc_chan_connect() آغاز شده بود، خاتمه می دهد.

int tipc_chan_shutdown(struct tipc_chan *chan);

[in] chan : اشاره گر به کانالی است که با تماس tipc_create_chan() نشانگر است

tipc_chan_destroy()

یک کانال IPC معین Trusty را از بین می برد.

void tipc_chan_destroy(struct tipc_chan *chan);

[in] chan : اشاره گر به کانالی است که با تماس tipc_create_chan() نشانگر است

tipc_chan_get_txbuf_timeout()

یک بافر پیام را دریافت می کند که می تواند برای ارسال داده ها از طریق یک کانال مشخص استفاده شود. اگر بافر فوراً در دسترس نباشد، تماس گیرنده ممکن است برای مدت زمان تعیین شده (در میلی ثانیه) مسدود شود.

struct tipc_msg_buf *
tipc_chan_get_txbuf_timeout(struct tipc_chan *chan, long timeout);

[in] chan : اشاره‌گر به کانالی است که در آن یک پیام در صف قرار می‌گیرد

[in] chan : حداکثر زمان برای صبر کردن تا زمانی که بافر tx در دسترس قرار گیرد

[retval]: یک بافر پیام معتبر در صورت موفقیت، ERR_PTR(err) در صورت خطا

tipc_chan_queue_msg()

پیامی را در صف می‌گذارد تا از طریق کانال‌های Trusty IPC مشخص شده ارسال شود.

int tipc_chan_queue_msg(struct tipc_chan *chan, struct tipc_msg_buf *mb);

[in] chan : اشاره گر به کانالی است که پیام را در صف قرار می دهد

[in] mb: اشاره‌گر به پیام در صف (که با تماس tipc_chan_get_txbuf_timeout() بدست می‌آید)

[reval]: 0 در موفقیت، یک خطای منفی در غیر این صورت

tipc_chan_put_txbuf()

بافر پیام Tx مشخص شده را که قبلاً توسط تماس tipc_chan_get_txbuf_timeout() بدست آمده بود، آزاد می کند.

void tipc_chan_put_txbuf(struct tipc_chan *chan,
                         struct tipc_msg_buf *mb);

[in] chan : اشاره گر به کانالی که این بافر پیام به آن تعلق دارد

[in] mb : اشاره گر به بافر پیام برای انتشار

[reval]: هیچ

tipc_chan_get_rxbuf()

یک بافر پیام جدید دریافت می کند که می تواند برای دریافت پیام ها در کانال مشخص شده استفاده شود.

struct tipc_msg_buf *tipc_chan_get_rxbuf(struct tipc_chan *chan);

[in] chan : اشاره گر به کانالی که این بافر پیام به آن تعلق دارد

[retval]: یک بافر پیام معتبر در صورت موفقیت، ERR_PTR(err) در صورت خطا

tipc_chan_put_rxbuf()

بافر پیام مشخصی را که قبلاً توسط یک فراخوانی tipc_chan_get_rxbuf() بدست آمده بود، آزاد می کند.

void tipc_chan_put_rxbuf(struct tipc_chan *chan,
                         struct tipc_msg_buf *mb);

[in] chan : اشاره گر به کانالی که این بافر پیام به آن تعلق دارد

[in] mb : اشاره گر به یک بافر پیام برای انتشار

[reval]: هیچ

،

Trusty API هایی را برای توسعه دو دسته از برنامه ها و خدمات ارائه می دهد:

  • برنامه ها و سرویس های قابل اعتمادی که روی پردازنده TEE اجرا می شوند
  • برنامه های معمولی و نامعتبر که روی پردازنده اصلی اجرا می شوند و از خدمات ارائه شده توسط برنامه های مورد اعتماد استفاده می کنند

Trusty API به طور کلی سیستم ارتباطات بین فرآیندی Trusty (IPC) را توصیف می کند، از جمله ارتباطات با دنیای غیر ایمن. نرم‌افزاری که روی پردازنده اصلی اجرا می‌شود می‌تواند از Trusty API برای اتصال به برنامه‌ها و سرویس‌های قابل اعتماد و تبادل پیام‌های دلخواه با آن‌ها درست مانند یک سرویس شبکه از طریق IP استفاده کند. این به برنامه بستگی دارد که قالب داده و معنایی این پیام ها را با استفاده از یک پروتکل در سطح برنامه تعیین کند. تحویل قابل اطمینان پیام ها توسط زیرساخت Trusty (به شکل درایورهایی که روی پردازنده اصلی اجرا می شوند) تضمین می شود و ارتباط کاملاً ناهمزمان است.

پورت ها و کانال ها

پورت ها توسط برنامه های Trusty استفاده می شوند تا نقاط پایانی سرویس را در قالب یک مسیر نامگذاری شده که مشتریان به آن متصل می شوند، نشان دهند. این یک شناسه سرویس ساده و مبتنی بر رشته را برای مشتریان ارائه می دهد. قرارداد نامگذاری، نامگذاری به سبک DNS معکوس است، به عنوان مثال com.google.servicename .

هنگامی که یک کلاینت به یک پورت متصل می شود، مشتری یک کانال برای تعامل با یک سرویس دریافت می کند. سرویس باید یک اتصال ورودی را بپذیرد، و زمانی که قبول کرد، یک کانال نیز دریافت می کند. در اصل، پورت ها برای جستجوی سرویس ها استفاده می شوند و سپس ارتباط از طریق یک جفت کانال متصل (یعنی نمونه های اتصال در یک پورت) رخ می دهد. هنگامی که یک کلاینت به یک پورت متصل می شود، یک اتصال متقارن و دو جهته برقرار می شود. با استفاده از این مسیر تمام دوبلکس، کلاینت ها و سرورها می توانند پیام های دلخواه مبادله کنند تا زمانی که هر یک از طرفین تصمیم به قطع اتصال بگیرند.

فقط برنامه‌های مورد اعتماد سمت امن یا ماژول‌های هسته Trusty می‌توانند پورت ایجاد کنند. برنامه های در حال اجرا در سمت غیر ایمن (در دنیای عادی) فقط می توانند به خدمات منتشر شده توسط سمت امن متصل شوند.

بسته به نیازها، یک برنامه قابل اعتماد می تواند همزمان مشتری و سرور باشد. یک برنامه قابل اعتماد که سرویسی را منتشر می کند (به عنوان سرور) ممکن است نیاز به اتصال به سرویس های دیگر (به عنوان مشتری) داشته باشد.

Handle API

دسته‌ها اعداد صحیح بدون علامت هستند که منابعی مانند پورت‌ها و کانال‌ها را نشان می‌دهند، مشابه توصیفگرهای فایل در یونیکس. پس از ایجاد دستگیره ها، آنها در یک جدول دسته مخصوص برنامه قرار می گیرند و می توانند بعداً به آنها ارجاع دهند.

تماس‌گیرنده می‌تواند داده‌های خصوصی را با استفاده از روش set_cookie() با یک دسته مرتبط کند.

روش‌ها در Handle API

دسته‌ها فقط در زمینه یک برنامه معتبر هستند. یک برنامه نباید مقدار یک دسته را به برنامه های دیگر منتقل کند مگر اینکه به صراحت مشخص شده باشد. فقط یک مقدار handle باید با مقایسه آن با INVALID_IPC_HANDLE #define, که یک برنامه می‌تواند به عنوان نشانه‌ای از نامعتبر بودن یا تنظیم نشده بودن یک دسته استفاده کند.

داده های خصوصی ارائه شده توسط تماس گیرنده را با یک دسته مشخص مرتبط می کند.

long set_cookie(uint32_t handle, void *cookie)

[in] handle : هر دسته ای که توسط یکی از فراخوانی های API برگردانده می شود

cookie [in]: اشاره‌گر به داده‌های فضای کاربر دلخواه در برنامه Trusty

[reval]: NO_ERROR در صورت موفقیت، < 0 کد خطا در غیر این صورت

این فراخوانی برای رسیدگی به رویدادها زمانی مفید است که در زمان دیگری پس از ایجاد دسته رخ دهند. مکانیزم مدیریت رویداد دسته و کوکی آن را به کنترل کننده رویداد برمی گرداند.

با استفاده از فراخوان wait() می‌توان برای رویدادها منتظر دستگیره‌ها بود.

صبر کن()

منتظر می ماند تا یک رویداد در یک دسته معین برای مدت زمان مشخصی رخ دهد.

long wait(uint32_t handle_id, uevent_t *event, unsigned long timeout_msecs)

[in] handle_id : هر دسته ای که توسط یکی از فراخوانی های API برگردانده می شود

event [out] : اشاره گر به ساختار که نشان دهنده رویدادی است که روی این دسته رخ داده است

[in] timeout_msecs : مقدار وقفه در میلی ثانیه. مقدار -1 یک مهلت نامحدود است

[reval]: NO_ERROR اگر یک رویداد معتبر در یک بازه زمانی مشخص رخ داده باشد. ERR_TIMED_OUT اگر مهلت زمانی مشخصی سپری شده باشد اما هیچ رویدادی رخ نداده باشد. < 0 برای سایر خطاها

پس از موفقیت ( retval == NO_ERROR )، فراخوانی wait() یک ساختار مشخص شده uevent_t را با اطلاعات مربوط به رویدادی که رخ داده است پر می کند.

typedef struct uevent {
    uint32_t handle; /* handle this event is related to */
    uint32_t event;  /* combination of IPC_HANDLE_POLL_XXX flags */
    void    *cookie; /* cookie associated with this handle */
} uevent_t;

فیلد event حاوی ترکیبی از مقادیر زیر است:

enum {
  IPC_HANDLE_POLL_NONE    = 0x0,
  IPC_HANDLE_POLL_READY   = 0x1,
  IPC_HANDLE_POLL_ERROR   = 0x2,
  IPC_HANDLE_POLL_HUP     = 0x4,
  IPC_HANDLE_POLL_MSG     = 0x8,
  IPC_HANDLE_POLL_SEND_UNBLOCKED = 0x10,
  … more values[TBD]
};

IPC_HANDLE_POLL_NONE - هیچ رویدادی واقعاً معلق نیست، تماس‌گیرنده باید انتظار را دوباره راه‌اندازی کند

IPC_HANDLE_POLL_ERROR - یک خطای داخلی نامشخص رخ داده است

IPC_HANDLE_POLL_READY - به نوع دسته بستگی دارد، به شرح زیر:

  • برای پورت ها، این مقدار نشان می دهد که یک اتصال معلق وجود دارد
  • برای کانال ها، این مقدار نشان می دهد که یک اتصال ناهمزمان برقرار شده است (به connect() ) مراجعه کنید

رویدادهای زیر فقط مربوط به کانال‌ها هستند:

  • IPC_HANDLE_POLL_HUP - نشان می دهد که یک کانال توسط یک همتا بسته شده است
  • IPC_HANDLE_POLL_MSG - نشان می دهد که یک پیام در حال انتظار برای این کانال وجود دارد
  • IPC_HANDLE_POLL_SEND_UNBLOCKED - نشان می‌دهد که تماس‌گیرنده‌ای که قبلاً از طریق ارسال مسدود شده است ممکن است دوباره تلاش کند پیامی ارسال کند (برای جزئیات به توضیح send_msg() مراجعه کنید)

یک کنترل کننده رویداد باید برای مدیریت ترکیبی از رویدادهای مشخص شده آماده باشد، زیرا ممکن است چندین بیت به طور همزمان تنظیم شوند. به عنوان مثال، برای یک کانال، ممکن است همزمان پیام های معلق و اتصال توسط یک همتا بسته شود.

بیشتر رویدادها چسبنده هستند. آنها تا زمانی که شرایط اساسی ادامه دارد باقی می مانند (به عنوان مثال همه پیام های معلق دریافت می شوند و درخواست های اتصال در انتظار رسیدگی می شوند). استثنا مورد رویداد IPC_HANDLE_POLL_SEND_UNBLOCKED است که پس از خواندن پاک می‌شود و برنامه فقط یک فرصت برای مدیریت آن دارد.

دسته ها را می توان با فراخوانی متد close() از بین برد.

بستن ()

منبع مرتبط با دسته مشخص شده را از بین می برد و آن را از جدول دسته حذف می کند.

long close(uint32_t handle_id);

[in] handle_id : دسته برای از بین بردن

[بازیابی]: 0 در صورت موفقیت؛ در غیر این صورت یک خطای منفی

API سرور

یک سرور با ایجاد یک یا چند پورت با نام که نشان دهنده نقاط پایانی سرویس آن است شروع می شود. هر پورت با یک دسته نمایش داده می شود.

روش‌ها در API سرور

port_create()

یک پورت سرویس با نام ایجاد می کند.

long port_create (const char *path, uint num_recv_bufs, size_t recv_buf_size,
uint32_t flags)

[in] path : نام رشته پورت (همانطور که در بالا توضیح داده شد). این نام باید در سراسر سیستم منحصر به فرد باشد. تلاش برای ایجاد یک نسخه تکراری با شکست مواجه شد.

[in] num_recv_bufs : حداکثر تعداد بافرهایی که یک کانال در این پورت می تواند از قبل برای تسهیل تبادل داده با مشتری اختصاص دهد. بافر برای داده های مربوط به هر دو جهت به طور جداگانه شمارش می شود ، بنابراین مشخص کردن 1 در اینجا به معنای 1 ارسال و 1 بافر دریافت شده است. به طور کلی ، تعداد بافرهای مورد نیاز به توافق پروتکل سطح بالاتر بین مشتری و سرور بستگی دارد. در صورت پروتکل بسیار همزمان ، این تعداد می تواند به اندازه 1 باشد (پیام ارسال کنید ، قبل از ارسال دیگری پاسخ دهید). اما اگر مشتری انتظار داشته باشد بیش از یک پیام ارسال کند قبل از اینکه پاسخ باشد ، شماره بیشتر باشد (به عنوان مثال ، یک پیام به عنوان یک پیش نویس و دیگری به عنوان دستور واقعی). مجموعه های بافر اختصاص یافته در هر کانال هستند ، بنابراین دو اتصال جداگانه (کانال) دارای مجموعه بافر جداگانه هستند.

[در] recv_buf_size : حداکثر اندازه هر بافر فردی در مجموعه بافر فوق. این مقدار وابسته به پروتکل است و به طور موثری حداکثر اندازه پیام را که می توانید با همسالان مبادله کنید محدود می کند

[در] flags : ترکیبی از پرچم ها که رفتار پورت اضافی را مشخص می کند

این مقدار باید ترکیبی از مقادیر زیر باشد:

IPC_PORT_ALLOW_TA_CONNECT - امکان اتصال از سایر برنامه های امن را فراهم می کند

IPC_PORT_ALLOW_NS_CONNECT - امکان اتصال از دنیای غیر امن را فراهم می کند

[retval]: اگر غیر منفی یا یک خطای خاص در صورت منفی است ، به درگاه ایجاد شده رسیدگی کنید

سپس سرور لیست دستگیره های پورت را برای اتصالات ورودی با استفاده از تماس wait() انتخاب می کند. پس از دریافت درخواست اتصال که توسط IPC_HANDLE_POLL_READY SET در قسمت event ساختار uevent_t مشخص شده است ، سرور باید accept() تماس بگیرد تا ایجاد اتصال را به پایان برساند و یک کانال ایجاد کند (که توسط دسته دیگری نشان داده شده است) که می تواند برای پیام های ورودی نظرسنجی شود .

قبول ()

یک اتصال ورودی را می پذیرد و دسته ای را به یک کانال می گیرد.

long accept(uint32_t handle_id, uuid_t *peer_uuid);

[in] handle_id : دسته ای را نشان می دهد که در پورت ای که مشتری به آن وصل شده است

[out] peer_uuid : اشاره گر به یک ساختار uuid_t که با UUID برنامه مشتری اتصال پر شود. اگر این اتصال از دنیای غیر امنیت سرچشمه گرفته باشد ، روی همه صفرها تنظیم شده است

[retval]: به ​​یک کانال (در صورت غیر منفی) رسیدگی کنید که سرور بتواند پیام ها را با مشتری مبادله کند (یا در غیر این صورت کد خطا)

API مشتری

این بخش شامل روشهای موجود در API مشتری است.

روشهای موجود در API مشتری

اتصال ()

اتصال به پورت مشخص شده با نام را آغاز می کند.

long connect(const char *path, uint flags);

[در] path : نام پورت منتشر شده توسط یک برنامه قابل اعتماد

[در] flags : رفتار اضافی و اختیاری را مشخص می کند

[retval]: به ​​کانال که می توان پیام ها را با سرور رد و بدل کرد ، کنترل کنید. خطا اگر منفی باشد

اگر هیچ flags مشخص نشده است (پارامتر flags روی 0 تنظیم شده است) ، Calling connect() یک اتصال همزمان را به یک درگاه مشخص می کند که بلافاصله در صورت وجود درگاه خطایی را برمی گرداند و بلوک ایجاد می کند تا اینکه سرور در غیر این صورت یک را قبول کند اتصال

این رفتار را می توان با مشخص کردن ترکیبی از دو مقدار ، که در زیر شرح داده شده است ، تغییر داد:

enum {
IPC_CONNECT_WAIT_FOR_PORT = 0x1,
IPC_CONNECT_ASYNC = 0x2,
};

IPC_CONNECT_WAIT_FOR_PORT - یک تماس connect() را مجبور می کند تا منتظر بمانید اگر پورت مشخص شده بلافاصله در اجرای آن وجود ندارد ، به جای اینکه بلافاصله شکست بخورد.

IPC_CONNECT_ASYNC - در صورت تنظیم ، یک اتصال ناهمزمان را آغاز می کند. یک برنامه باید با فراخوانی wait() برای یک رویداد تکمیل اتصال که توسط IPC_HANDLE_POLL_READY SET در قسمت رویداد ساختار uevent_t قبل از شروع کار عادی نشان داده شده است ، نظرسنجی کند.

API پیام رسانی

تماس های API پیام رسانی ، ارسال و خواندن پیام ها را از طریق اتصال قبلی (کانال) که قبلاً ایجاد شده است ، امکان پذیر می کند. تماس های API پیام رسانی برای سرورها و مشتری ها یکسان است.

مشتری با صدور تماس connect() یک دسته را به یک کانال دریافت می کند و یک سرور از یک تماس accept() که در بالا توضیح داده شده است ، یک دسته کانال دریافت می کند.

ساختار یک پیام قابل اعتماد

همانطور که در زیر نشان داده شده است ، پیام های رد و بدل شده توسط API قابل اعتماد دارای ساختار حداقل هستند و آن را به سرور و مشتری واگذار می کنند تا در مورد معناشناسی مطالب واقعی توافق کنند:

/*
 *  IPC message
 */
typedef struct iovec {
        void   *base;
        size_t  len;
} iovec_t;

typedef struct ipc_msg {
        uint     num_iov; /* number of iovs in this message */
        iovec_t  *iov;    /* pointer to iov array */

        uint     num_handles; /* reserved, currently not supported */
        handle_t *handles;    /* reserved, currently not supported */
} ipc_msg_t;

یک پیام می تواند از یک یا چند بافر غیر مبهم تشکیل شود که توسط مجموعه ای از ساختارهای iovec_t نشان داده شده است. Trusty با استفاده از آرایه iov ، خوانش های پراکنده را انجام می دهد و به این بلوک ها می نویسد. محتوای بافرهایی که توسط آرایه iov قابل توصیف است کاملاً خودسرانه است.

روشهای موجود در API پیام رسانی

send_msg ()

پیام را از طریق یک کانال مشخص ارسال می کند.

long send_msg(uint32_t handle, ipc_msg_t *msg);

[در] handle : به کانال که برای ارسال پیام بر روی آن استفاده کنید

[در] msg : اشاره گر به ipc_msg_t structure که پیام را توصیف می کند

[retval]: تعداد کل بایت ارسال شده در موفقیت ؛ یک خطای منفی در غیر این صورت

اگر مشتری (یا سرور) سعی در ارسال پیام از طریق کانال دارد و هیچ فضایی در صف پیام همسالان مقصد وجود ندارد ، کانال ممکن است یک حالت مسدود شده را وارد کند (این هرگز نباید برای یک پروتکل درخواست/پاسخ همزمان ساده اتفاق بیفتد اما ممکن است در موارد پیچیده تر اتفاق بیفتد) که با بازگشت کد خطای ERR_NOT_ENOUGH_BUFFER نشان داده شده است. در چنین حالتی ، تماس گیرنده باید صبر کند تا همسالان با بازیابی پیام های دست زدن و بازنشستگی ، مقداری فضای موجود در صف خود را آزاد کند ، که توسط IPC_HANDLE_POLL_SEND_UNBLOCKED تنظیم شده در قسمت event ساختار uevent_t که توسط تماس wait() بازگردانده شده است.

get_msg ()

در مورد پیام بعدی در صف پیام ورودی ، اطلاعات متا را دریافت می کند

از یک کانال مشخص شده

long get_msg(uint32_t handle, ipc_msg_info_t *msg_info);

[در] handle : دسته کانال که باید پیام جدیدی بازیابی شود

[out] msg_info : ساختار اطلاعات پیام به شرح زیر شرح داده شده است:

typedef struct ipc_msg_info {
        size_t    len;  /* total message length */
        uint32_t  id;   /* message id */
} ipc_msg_info_t;

به هر پیام یک شناسه منحصر به فرد در مجموعه پیام های برجسته اختصاص داده می شود ، و طول کل هر پیام پر می شود.

[retval]: NO_ERROR در موفقیت ؛ یک خطای منفی در غیر این صورت

read_msg ()

محتوای پیام را با شناسه مشخص شده از افست مشخص شده می خواند.

long read_msg(uint32_t handle, uint32_t msg_id, uint32_t offset, ipc_msg_t
*msg);

[در] handle : دسته کانال که از آن پیام بخواند

[در] msg_id : شناسه پیام برای خواندن

[in] offset : جبران پیام برای شروع خواندن

[out] msg : اشاره گر به ساختار ipc_msg_t که مجموعه ای از بافر را توصیف می کند که در آن می توان داده های پیام ورودی را ذخیره کرد

[retval]: تعداد کل بایت های ذخیره شده در بافرهای msg در موفقیت. یک خطای منفی در غیر این صورت

روش read_msg را می توان چندین بار نامید که در صورت لزوم از یک جبران متفاوت (نه لزوماً پی در پی) استفاده می شود.

put_msg ()

پیام با شناسه مشخص بازنشسته می شود.

long put_msg(uint32_t handle, uint32_t msg_id);

[در] handle : دسته کانال که پیام به آن رسیده است

[در] msg_id : شناسه پیام بازنشسته می شود

[retval]: NO_ERROR در موفقیت ؛ یک خطای منفی در غیر این صورت

پس از بازنشستگی پیام و بافر که اشغال شده است ، نمی توان به محتوای پیام دسترسی پیدا کرد.

API توصیف کننده پرونده

API Descriptor Descripttor شامل تماس های read() ، write() و ioctl() است. همه این تماس ها می توانند بر روی مجموعه ای از پیش تعریف شده (استاتیک) از توصیف کننده های پرونده به طور سنتی که توسط تعداد کمی نشان داده می شوند ، کار کنند. در اجرای فعلی ، فضای توصیف کننده پرونده از فضای دسته IPC جداست. API توصیف کننده پرونده در Trusty مشابه API مبتنی بر توصیف کننده پرونده سنتی است.

به طور پیش فرض ، 3 توصیف کننده پرونده از پیش تعریف شده (استاندارد و شناخته شده) وجود دارد:

  • 0 - ورودی استاندارد. اجرای پیش فرض ورودی استاندارد fd یک NO-OP است (همانطور که انتظار نمی رود برنامه های معتبر یک کنسول تعاملی داشته باشند) بنابراین خواندن ، نوشتن یا فراخوانی ioctl() در fd 0 باید یک خطای ERR_NOT_SUPPORTED را برگرداند.
  • 1 - خروجی استاندارد. داده های نوشته شده به خروجی استاندارد می توانند (بسته به سطح اشکال زدایی LK) به UART و/یا ورود به حافظه موجود در سمت غیر امن ، بسته به بستر و پیکربندی ، مسیریابی شوند. گزارش ها و پیام های اشکال زدایی غیر بحرانی باید در خروجی استاندارد باشد. روشهای read() و ioctl() بدون استفاده هستند و باید خطای ERR_NOT_SUPPORTED را برگردانند.
  • 2 - خطای استاندارد. داده های نوشته شده به خطای استاندارد باید بسته به بستر و پیکربندی ، به ورود به سیستم UART یا حافظه موجود در قسمت غیر ایمن منتقل شوند. توصیه می شود فقط پیام های مهم را برای خطای استاندارد بنویسید ، زیرا این جریان به احتمال زیاد بدون استفاده نیست. روشهای read() و ioctl() بدون استفاده هستند و باید خطای ERR_NOT_SUPPORTED را برگردانند.

حتی اگر این مجموعه از توصیف کننده های پرونده برای اجرای fds بیشتر (برای اجرای برنامه های خاص پلت فرم) قابل گسترش باشد ، باید توصیف کننده های پرونده با احتیاط اعمال شوند. گسترش توصیف کننده های پرونده مستعد ایجاد درگیری است و به طور کلی توصیه نمی شود.

روشهای موجود در API توصیف کننده پرونده

بخوانید ()

تلاش برای خواندن برای count بایت داده ها از یک توصیف کننده فایل مشخص.

long read(uint32_t fd, void *buf, uint32_t count);

[در] fd : توصیف کننده پرونده ای که از آن خوانده شود

[out] buf : اشاره گر به بافر که در آن داده ها را ذخیره می کند

[در] count : حداکثر تعداد بایت برای خواندن

[retval]: تعداد برگشتی بایت خوانده شده ؛ یک خطای منفی در غیر این صورت

نوشتن ()

برای count بایت داده ها به توصیف کننده پرونده مشخص می نویسد.

long write(uint32_t fd, void *buf, uint32_t count);

[در] fd : توصیف کننده پرونده برای نوشتن

[out] buf : اشاره گر به داده ها برای نوشتن

[در] count : حداکثر تعداد بایت برای نوشتن

[retval]: تعداد برگشتی بایت نوشته شده ؛ یک خطای منفی در غیر این صورت

ioctl ()

یک دستور ioctl مشخص را برای یک توصیف کننده فایل خاص فراخوانی می کند.

long ioctl(uint32_t fd, uint32_t cmd, void *args);

[در] fd : توصیف کننده پرونده برای فراخوانی ioctl()

[در] cmd : دستور ioctl

[in/out] args : نشانگر استدلال های ioctl()

API متفرقه

روشهای موجود در API متفرقه

GetTime ()

زمان فعلی سیستم (در نانو ثانیه) را برمی گرداند.

long gettime(uint32_t clock_id, uint32_t flags, int64_t *time);

[در] clock_id : وابسته به پلتفرم ؛ صفر را برای پیش فرض عبور دهید

[در] flags : محفوظ است ، باید صفر باشد

[خارج] time : نشانگر به مقدار int64_t که می تواند زمان فعلی را ذخیره کند

[retval]: NO_ERROR در موفقیت ؛ یک خطای منفی در غیر این صورت

nanosleep ()

اجرای برنامه فراخوانی را برای مدت زمان مشخصی به حالت تعلیق در می آورد و پس از آن دوره آن را از سر می گیرد.

long nanosleep(uint32_t clock_id, uint32_t flags, uint64_t sleep_time)

[در] clock_id : محفوظ است ، باید صفر باشد

[در] flags : محفوظ است ، باید صفر باشد

[در] sleep_time : زمان خواب در نانو ثانیه

[retval]: NO_ERROR در موفقیت ؛ یک خطای منفی در غیر این صورت

نمونه ای از یک سرور برنامه قابل اعتماد

برنامه نمونه زیر استفاده از API های فوق را نشان می دهد. نمونه یک سرویس "اکو" ایجاد می کند که چندین اتصالات ورودی را به همراه دارد و تمام پیام هایی را که از مشتری دریافت می کند از طرف امن یا غیر ایمن به تماس گیرنده باز می گردد.

#include <uapi/err.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <trusty_ipc.h>
#define LOG_TAG "echo_srv"
#define TLOGE(fmt, ...) \
    fprintf(stderr, "%s: %d: " fmt, LOG_TAG, __LINE__, ##__VA_ARGS__)

# define MAX_ECHO_MSG_SIZE 64

static
const char * srv_name = "com.android.echo.srv.echo";

static uint8_t msg_buf[MAX_ECHO_MSG_SIZE];

/*
 *  Message handler
 */
static int handle_msg(handle_t chan) {
  int rc;
  struct iovec iov;
  ipc_msg_t msg;
  ipc_msg_info_t msg_inf;

  iov.iov_base = msg_buf;
  iov.iov_len = sizeof(msg_buf);

  msg.num_iov = 1;
  msg.iov = &iov;
  msg.num_handles = 0;
  msg.handles = NULL;

  /* get message info */
  rc = get_msg(chan, &msg_inf);
  if (rc == ERR_NO_MSG)
    return NO_ERROR; /* no new messages */

  if (rc != NO_ERROR) {
    TLOGE("failed (%d) to get_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  /* read msg content */
  rc = read_msg(chan, msg_inf.id, 0, &msg);
  if (rc < 0) {
    TLOGE("failed (%d) to read_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  /* update number of bytes received */
  iov.iov_len = (size_t) rc;

  /* send message back to the caller */
  rc = send_msg(chan, &msg);
  if (rc < 0) {
    TLOGE("failed (%d) to send_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  /* retire message */
  rc = put_msg(chan, msg_inf.id);
  if (rc != NO_ERROR) {
    TLOGE("failed (%d) to put_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  return NO_ERROR;
}

/*
 *  Channel event handler
 */
static void handle_channel_event(const uevent_t * ev) {
  int rc;

  if (ev->event & IPC_HANDLE_POLL_MSG) {
    rc = handle_msg(ev->handle);
    if (rc != NO_ERROR) {
      /* report an error and close channel */
      TLOGE("failed (%d) to handle event on channel %d\n",
        rc, ev->handle);
      close(ev->handle);
    }
    return;
  }
  if (ev->event & IPC_HANDLE_POLL_HUP) {
    /* closed by peer. */
    close(ev->handle);
    return;
  }
}

/*
 *  Port event handler
 */
static void handle_port_event(const uevent_t * ev) {
  uuid_t peer_uuid;

  if ((ev->event & IPC_HANDLE_POLL_ERROR) ||
    (ev->event & IPC_HANDLE_POLL_HUP) ||
    (ev->event & IPC_HANDLE_POLL_MSG) ||
    (ev->event & IPC_HANDLE_POLL_SEND_UNBLOCKED)) {
    /* should never happen with port handles */
    TLOGE("error event (0x%x) for port (%d)\n",
      ev->event, ev->handle);
    abort();
  }
  if (ev->event & IPC_HANDLE_POLL_READY) {
    /* incoming connection: accept it */
    int rc = accept(ev->handle, &peer_uuid);
    if (rc < 0) {
      TLOGE("failed (%d) to accept on port %d\n",
        rc, ev->handle);
      return;
    }
    handle_t chan = rc;
    while (true){
      struct uevent cev;

      rc = wait(chan, &cev, INFINITE_TIME);
      if (rc < 0) {
        TLOGE("wait returned (%d)\n", rc);
        abort();
      }
      handle_channel_event(&cev);
      if (cev.event & IPC_HANDLE_POLL_HUP) {
        return;
      }
    }
  }
}


/*
 *  Main app entry point
 */
int main(void) {
  int rc;
  handle_t port;

  /* Initialize service */
  rc = port_create(srv_name, 1, MAX_ECHO_MSG_SIZE,
    IPC_PORT_ALLOW_NS_CONNECT |
    IPC_PORT_ALLOW_TA_CONNECT);
  if (rc < 0) {
    TLOGE("Failed (%d) to create port %s\n",
      rc, srv_name);
    abort();
  }
  port = (handle_t) rc;

  /* enter main event loop */
  while (true) {
    uevent_t ev;

    ev.handle = INVALID_IPC_HANDLE;
    ev.event = 0;
    ev.cookie = NULL;

    /* wait forever */
    rc = wait(port, &ev, INFINITE_TIME);
    if (rc == NO_ERROR) {
      /* got an event */
      handle_port_event(&ev);
    } else {
      TLOGE("wait returned (%d)\n", rc);
      abort();
    }
  }
  return 0;
}

روش run_end_to_end_msg_test() 10،000 پیام را به صورت غیر همزمان به سرویس "echo" ارسال می کند و پاسخ ها را کنترل می کند.

static int run_echo_test(void)
{
  int rc;
  handle_t chan;
  uevent_t uevt;
  uint8_t tx_buf[64];
  uint8_t rx_buf[64];
  ipc_msg_info_t inf;
  ipc_msg_t   tx_msg;
  iovec_t     tx_iov;
  ipc_msg_t   rx_msg;
  iovec_t     rx_iov;

  /* prepare tx message buffer */
  tx_iov.base = tx_buf;
  tx_iov.len  = sizeof(tx_buf);
  tx_msg.num_iov = 1;
  tx_msg.iov     = &tx_iov;
  tx_msg.num_handles = 0;
  tx_msg.handles = NULL;

  memset (tx_buf, 0x55, sizeof(tx_buf));

  /* prepare rx message buffer */
  rx_iov.base = rx_buf;
  rx_iov.len  = sizeof(rx_buf);
  rx_msg.num_iov = 1;
  rx_msg.iov     = &rx_iov;
  rx_msg.num_handles = 0;
  rx_msg.handles = NULL;

  /* open connection to echo service */
  rc = sync_connect(srv_name, 1000);
  if(rc < 0)
    return rc;

  /* got channel */
  chan = (handle_t)rc;

  /* send/receive 10000 messages asynchronously. */
  uint tx_cnt = 10000;
  uint rx_cnt = 10000;

  while (tx_cnt || rx_cnt) {
    /* send messages until all buffers are full */
while (tx_cnt) {
    rc = send_msg(chan, &tx_msg);
      if (rc == ERR_NOT_ENOUGH_BUFFER)
      break;  /* no more space */
    if (rc != 64) {
      if (rc > 0) {
        /* incomplete send */
        rc = ERR_NOT_VALID;
}
      goto abort_test;
}
    tx_cnt--;
  }

  /* wait for reply msg or room */
  rc = wait(chan, &uevt, 1000);
  if (rc != NO_ERROR)
    goto abort_test;

  /* drain all messages */
  while (rx_cnt) {
    /* get a reply */
      rc = get_msg(chan, &inf);
    if (rc == ERR_NO_MSG)
        break;  /* no more messages  */
  if (rc != NO_ERROR)
goto abort_test;

  /* read reply data */
    rc = read_msg(chan, inf.id, 0, &rx_msg);
  if (rc != 64) {
    /* unexpected reply length */
    rc = ERR_NOT_VALID;
    goto abort_test;
}

  /* discard reply */
  rc = put_msg(chan, inf.id);
  if (rc != NO_ERROR)
    goto abort_test;
  rx_cnt--;
  }
}

abort_test:
  close(chan);
  return rc;
}

API ها و برنامه های جهانی غیر امن

مجموعه ای از خدمات قابل اعتماد ، که از سمت امن منتشر شده و با ویژگی IPC_PORT_ALLOW_NS_CONNECT مشخص شده است ، برای برنامه های فضایی هسته و کاربر در حال اجرا در سمت غیر امن است.

محیط اعدام در سمت غیر امن (هسته و فضای کاربر) به طرز چشمگیری با محیط اجرای در سمت امن متفاوت است. بنابراین ، به جای یک کتابخانه واحد برای هر دو محیط ، دو مجموعه مختلف API وجود دارد. در هسته ، API مشتری توسط درایور هسته Trusty-IPC ارائه می شود و یک گره دستگاه کاراکتر را ثبت می کند که می تواند توسط فرآیندهای فضایی کاربر برای برقراری ارتباط با خدمات اجرا شده در سمت امن استفاده شود.

فضای کاربر API مشتری IPC قابل اعتماد

کتابخانه API Client IPC Trusty Space Trusty یک لایه نازک در بالای گره دستگاه fd است.

یک برنامه فضایی کاربر با فراخوانی tipc_connect() ، یک جلسه ارتباطی را آغاز می کند ، و ارتباط خود را به یک سرویس قابل اعتماد مشخص می کند. در داخل ، تماس tipc_connect() یک گره دستگاه مشخص را برای به دست آوردن یک توصیف کننده پرونده باز می کند و از یک تماس TIPC_IOC_CONNECT ioctl() با پارامتر argp که به یک رشته حاوی نام سرویس اشاره می کند ، فراخوانی می کند.

#define TIPC_IOC_MAGIC  'r'
#define TIPC_IOC_CONNECT  _IOW(TIPC_IOC_MAGIC, 0x80, char *)

توصیف کننده پرونده حاصل فقط می تواند برای برقراری ارتباط با خدماتی که برای آن ایجاد شده است استفاده شود. توصیف کننده پرونده باید با تماس با tipc_close() بسته شود که اتصال دیگر لازم نباشد.

توصیف کننده پرونده به دست آمده توسط تماس tipc_connect() به عنوان یک گره دستگاه کاراکتر معمولی رفتار می کند. توصیف کننده پرونده:

  • در صورت لزوم می تواند به حالت غیر مسدود کننده تبدیل شود
  • برای ارسال پیام به طرف دیگر می توان برای استفاده از یک تماس write() استفاده کرد
  • برای در دسترس بودن پیام های دریافتی به عنوان یک توصیف کننده پرونده معمولی می توان نظرسنجی کرد (با استفاده از poll() یا تماس select() تماس ())
  • برای بازیابی پیام های ورودی قابل خواندن است

یک تماس گیرنده با اجرای یک تماس نوشتن برای fd مشخص شده ، پیامی را به سرویس قابل اعتماد ارسال می کند. تمام داده های منتقل شده به write() تماس () توسط درایور Trusty-IPC به پیام تبدیل می شوند. این پیام به سمت امن تحویل داده می شود که داده ها توسط زیر سیستم IPC در هسته قابل اعتماد اداره می شوند و به مقصد مناسب منتقل می شوند و به عنوان یک رویداد IPC_HANDLE_POLL_MSG در یک دسته کانال خاص به یک حلقه رویداد برنامه تحویل داده می شوند. بسته به پروتکل خاص و خاص خدمات ، سرویس قابل اعتماد ممکن است یک یا چند پیام پاسخ را ارسال کند که به طرف غیر امن تحویل داده می شوند و در صف پیام توصیف کننده پرونده کانال مناسب قرار می گیرند که توسط برنامه فضایی کاربر خوانده می read() تماس بگیرید.

tipc_connect ()

یک گره دستگاه tipc مشخص را باز می کند و اتصال به یک سرویس قابل اعتماد مشخص را آغاز می کند.

int tipc_connect(const char *dev_name, const char *srv_name);

[در] dev_name : مسیر به گره دستگاه IPC قابل اعتماد برای باز کردن

[در] srv_name : نام یک سرویس قابل اعتماد منتشر شده برای اتصال به آن

[retval]: توصیف کننده پرونده معتبر در مورد موفقیت ، -1 در غیر این صورت.

tipc_close ()

اتصال به سرویس قابل اعتماد مشخص شده توسط یک توصیف کننده پرونده را می بندد.

int tipc_close(int fd);

[در] fd : توصیف کننده پرونده قبلاً توسط یک تماس tipc_connect() باز شده است

API مشتری IPC قابل اعتماد هسته

API مشتری IPC Trusty IPC برای رانندگان هسته در دسترس است. فضای کاربر API قابل اعتماد IPC در بالای این API پیاده سازی شده است.

به طور کلی ، استفاده معمولی از این API شامل یک تماس گیرنده است که با استفاده از عملکرد tipc_create_channel() و سپس با استفاده از تماس tipc_chan_connect() برای ایجاد اتصال به سرویس IPC قابل اعتماد که در سمت امن اجرا می شود ، یک شیء struct tipc_chan را ایجاد می کند. اتصال به سمت از راه دور را می توان با تماس با tipc_chan_shutdown() و به دنبال آن tipc_chan_destroy() برای پاکسازی منابع خاتمه داد.

پس از دریافت اعلان (از طریق تماس با handle_event() ) مبنی بر برقراری ارتباط با موفقیت ، یک تماس گیرنده موارد زیر را انجام می دهد:

  • با استفاده از تماس tipc_chan_get_txbuf_timeout() بافر پیام به دست می آورد
  • یک پیام را تشکیل می دهد ، و
  • پیام را با استفاده از روش tipc_chan_queue_msg() برای تحویل به یک سرویس قابل اعتماد (از طرف امن) ، که کانال به آن متصل است ، صف کنید

پس از موفقیت در صف ، تماس گیرنده باید بافر پیام را فراموش کند زیرا بافر پیام در نهایت پس از پردازش توسط سمت از راه دور (برای استفاده مجدد ، برای سایر پیام ها) به استخر بافر رایگان باز می گردد. در صورت عدم موفقیت در صف چنین بافر ، کاربر فقط باید با tipc_chan_put_txbuf() تماس بگیرد یا دیگر لازم نیست.

یک کاربر API با استفاده از پاسخ به تماس handle_msg() از راه دور پیام هایی را دریافت می کند (که در زمینه کار قابل اعتماد-IPC rx نامیده می شود) که یک نشانگر برای یک بافر rx حاوی یک پیام ورودی را فراهم می کند.

انتظار می رود که اجرای پاسخ به تماس handle_msg() یک نشانگر را به یک struct tipc_msg_buf برگرداند. اگر به صورت محلی اداره شود و دیگر نیازی به آن نباشد ، می تواند همان بافر پیام ورودی باشد. از طرف دیگر ، این می تواند یک بافر جدید باشد که توسط یک تماس tipc_chan_get_rxbuf() به دست آمده است اگر بافر ورودی برای پردازش بیشتر صف شود. یک بافر rx جدا شده باید ردیابی شود و در نهایت با استفاده از تماس tipc_chan_put_rxbuf() در صورت نیاز دیگر منتشر شود.

روشهای موجود در API مشتری IPC قابل اعتماد هسته

TIPC_CREATE_CHANNEL ()

نمونه ای از یک کانال IPC قابل اعتماد را برای یک دستگاه قابل اعتماد-IPC خاص ایجاد و پیکربندی می کند.

struct tipc_chan *tipc_create_channel(struct device *dev,
                          const struct tipc_chan_ops *ops,
                              void *cb_arg);

[در] dev : اشاره گر به Trusty-IPC که کانال دستگاه برای آن ایجاد شده است

[در] ops : اشاره گر به struct tipc_chan_ops ، با تماس های خاص تماس گیرنده پر شده است

[در] cb_arg : اشاره گر به داده هایی که به تماس های tipc_chan_ops منتقل می شود

[retval]: اشاره گر به یک نمونه تازه ایجاد شده از struct tipc_chan در مورد موفقیت ، ERR_PTR(err) در غیر این صورت

به طور کلی ، یک تماس گیرنده باید هنگام وقوع فعالیت مربوطه ، دو تماس تلفنی را ارائه دهد که به صورت ناهمزمان فراخوانی می شوند.

رویداد void (*handle_event)(void *cb_arg, int event) برای اطلاع یک تماس گیرنده در مورد تغییر حالت کانال فراخوانی می شود.

[در] cb_arg : اشاره گر به داده های منتقل شده به یک تماس tipc_create_channel()

[در] event : رویدادی که می تواند یکی از مقادیر زیر باشد:

  • TIPC_CHANNEL_CONNECTED - نشان دهنده اتصال موفق به سمت از راه دور است
  • TIPC_CHANNEL_DISCONNECTED - نشان می دهد طرف از راه دور درخواست اتصال جدید یا قطع درخواست را برای کانال قبلاً متصل رد کرده است
  • TIPC_CHANNEL_SHUTDOWN - نشان می دهد که طرف از راه دور خاموش است و به طور دائم تمام اتصالات را خاتمه می دهد

struct tipc_msg_buf *(*handle_msg)(void *cb_arg, struct tipc_msg_buf *mb) تماس با پاسخ به شما ارائه می شود تا اعلان مبنی بر دریافت پیام جدید از طریق یک کانال مشخص شده را ارائه دهد:

  • [در] cb_arg : اشاره گر به داده های منتقل شده به تماس tipc_create_channel()
  • [در] mb : اشاره گر به struct tipc_msg_buf توصیف یک پیام ورودی
  • [retval]: انتظار می رود که اجرای پاسخ به تماس یک نشانگر را به یک struct tipc_msg_buf برگرداند که می تواند همان نشانگر دریافت شده به عنوان یک پارامتر mb باشد اگر پیام به صورت محلی انجام شود و دیگر لازم نیست (یا می تواند یک بافر جدیدی باشد که توسط tipc_chan_get_rxbuf() تماس)

tipc_chan_connect ()

اتصال به سرویس IPC قابل اعتماد مشخص را آغاز می کند.

int tipc_chan_connect(struct tipc_chan *chan, const char *port);

[در] chan : نشانگر به کانال بازگشت توسط تماس tipc_create_chan()

[در] port : اشاره گر به رشته ای که حاوی نام سرویس است که به آن وصل می شود

[retval]: 0 در مورد موفقیت ، یک خطای منفی در غیر این صورت

هنگام برقراری اتصال با دریافت یک تماس تلفنی handle_event تماس گیرنده به تماس گیرنده اطلاع داده می شود.

TIPC_CHAN_SHUTDOWN ()

اتصال به سرویس IPC قابل اعتماد که قبلاً توسط یک تماس tipc_chan_connect() آغاز شده بود ، خاتمه می دهد.

int tipc_chan_shutdown(struct tipc_chan *chan);

[in] chan : اشاره گر به یک کانال که توسط یک تماس tipc_create_chan() بازگشت

tipc_chan_destroy ()

یک کانال IPC قابل اعتماد مشخص را از بین می برد.

void tipc_chan_destroy(struct tipc_chan *chan);

[در] chan : نشانگر به کانال بازگشت توسط تماس tipc_create_chan()

tipc_chan_get_txbuf_timeout ()

یک بافر پیام را بدست می آورد که می تواند برای ارسال داده ها از طریق یک کانال مشخص استفاده شود. اگر بافر بلافاصله در دسترس نباشد ، ممکن است تماس گیرنده برای مدت زمان مشخص شده (در میلی ثانیه) مسدود شود.

struct tipc_msg_buf *
tipc_chan_get_txbuf_timeout(struct tipc_chan *chan, long timeout);

[در] chan : اشاره گر به کانال که می تواند یک پیام را به آن صف کند

[in] chan : حداکثر زمان برای صبر کردن تا در دسترس بودن بافر tx

[retval]: یک بافر پیام معتبر در موفقیت ، ERR_PTR(err) در خطا

tipc_chan_queue_msg ()

صف ارسال پیامی که باید از طریق کانال های IPC قابل اعتماد ارسال شود.

int tipc_chan_queue_msg(struct tipc_chan *chan, struct tipc_msg_buf *mb);

[in] chan : اشاره گر به کانال که می تواند پیام را صف کند

[در] mb: اشاره گر به پیام به صف (به دست آمده توسط یک tipc_chan_get_txbuf_timeout() تماس)

[retval]: 0 در مورد موفقیت ، یک خطای منفی در غیر این صورت

tipc_chan_put_txbuf ()

بافر پیام Tx مشخص شده را که قبلاً توسط یک تماس tipc_chan_get_txbuf_timeout() به دست آمده است ، منتشر می کند.

void tipc_chan_put_txbuf(struct tipc_chan *chan,
                         struct tipc_msg_buf *mb);

[در] chan : اشاره گر به کانال که این بافر پیام متعلق به آن است

[در] mb : اشاره گر به بافر پیام برای انتشار

[retval]: هیچکدام

tipc_chan_get_rxbuf ()

یک بافر پیام جدیدی را بدست می آورد که می تواند برای دریافت پیام از طریق کانال مشخص شده استفاده شود.

struct tipc_msg_buf *tipc_chan_get_rxbuf(struct tipc_chan *chan);

[در] chan : اشاره گر به کانال که این بافر پیام به آن تعلق دارد

[retval]: یک بافر پیام معتبر در موفقیت ، ERR_PTR(err) در خطا

tipc_chan_put_rxbuf ()

یک بافر پیام مشخص را که قبلاً توسط یک تماس tipc_chan_get_rxbuf() به دست آمده است ، منتشر می کند.

void tipc_chan_put_rxbuf(struct tipc_chan *chan,
                         struct tipc_msg_buf *mb);

[در] chan : اشاره گر به کانال که این بافر پیام به آن تعلق دارد

[در] mb : اشاره گر به یک بافر پیام برای انتشار

[retval]: هیچکدام