Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Немогу перейти в кодовый сегмент в большим DPL.  (Прочитано 40584 раз)
0 Пользователей и 1 Гость смотрят эту тему.
zloi7777
Постоялец

ru
Offline Offline

« : 01-07-2010 08:03 » 

Пишу простую прогу для защищенного режима. Пока нахожусь в нулевом кольце - все работает отлично, как только меняю DPL у целевого сегмента с 0 на 3, выскакивает общее нарушение зашиты  Жаль

Вот так все работает:


тут таблица дескрипторов:
Код:
STRUC	desc_struc		; структура дескриптора
limit dw 0 ; предел
base_l dw 0 ; мл. слово физического адреса
base_h db 0 ; ст. байт физического адреса
access db 0 ; байт доступа
acces32 db 0
base_b db 0
ENDS desc_struc
ACC_PRESENT EQU 10000000b ; сегмент есть в памЯти
[hr]ACC_CSEG EQU 00011000b ; сегмент кода
ACC_DSEG EQU 00010000b ; сегмент данных
ACC_TSSSEG EQU 00001001b ; TSS
ACC_EXPDOWN EQU 00000100b ; сегмент расширЯетсЯ вниз
ACC_CONFORM EQU 00000100b ; согласованный сегмент
ACC_DATAWR EQU 00000010b ; разрешена запись

ACC_INTR_GATE EQU 00001110b ; вентиль прерываниЯ
ACC_TRAP_GATE EQU 00001111b ; вентиль исключениЯ
ACC_CALL_GATE EQU 00001100b
ACC_TASK_GATE EQU 00000101b

ACC_RING0 EQU 00000000b
ACC_RING3 EQU 01100000b

; ------------------------------------------------------------
; Типы сегментов
DATA_ACC  = ACC_PRESENT OR ACC_DSEG OR ACC_DATAWR ; сегмент даннх
CODE_ACC  = ACC_PRESENT OR ACC_CSEG ; сегмент кода
STACK_ACC = ACC_PRESENT OR ACC_DSEG OR ACC_DATAWR OR ACC_EXPDOWN ; сегмент стека
TSS_ACC   = ACC_PRESENT OR ACC_TSSSEG ; TSS

IDT_ACC   = DATA_ACC ; байт доступа сегмента таблицы IDT
INTR_ACC  = ACC_PRESENT OR ACC_INTR_GATE ; байт доступа вентилЯ прерываниЯ
TRAP_ACC  = ACC_PRESENT OR ACC_TRAP_GATE ; байт доступа вентилЯ исключениЯ
CALL_ACC  = ACC_PRESENT OR ACC_CALL_GATE
TASK_ACC  = ACC_PRESENT OR ACC_TASK_GATE



ACS_GRANUL EQU 10000000b
ACS_DIMENS EQU 01000000b
DATASEG
DSEG_BEG = THIS WORD

GDT_BEG = $
LABEL gdtr WORD

gdt_0 desc_struc <0,0,0,0,0,0>
gdt_gdt desc_struc <GDT_SIZE-1,,,DATA_ACC,0,0>
gdt_idt desc_struc <IDT_SIZE-1,,,IDT_ACC,0,0>
gdt_ds desc_struc <03fffh,,,DATA_ACC,0,0>
gdt_cs desc_struc <03fffh,,,CODE_ACC OR ACC_DATAWR,0,0>
gdt_ss desc_struc <03fffh,00000h,000h,DATA_ACC,ACS_DIMENS,0>

gdt_ds32 desc_struc <0ffffh,00000h,000h,DATA_ACC               OR ACC_RING0,ACS_GRANUL OR 00Fh              ,000h>
gdt_cs32 desc_struc <0ffffh,00000h,000h,CODE_ACC OR ACC_DATAWR OR ACC_RING0,ACS_GRANUL OR ACS_DIMENS OR 00Fh,000h>
gdt_ss32 desc_struc <0ffffh,00000h,000h,DATA_ACC               OR ACC_RING0,ACS_GRANUL OR ACS_DIMENS OR 00Fh,000h>

gdt_dsdebug desc_struc <0ffffh,00000h,000h,DATA_ACC   OR ACC_RING0,ACS_GRANUL OR 00Fh              ,000h>
gdt_csdebug desc_struc <0ffffh,00000h,000h,CODE_ACC OR ACC_DATAWR OR ACC_RING0,ACS_GRANUL OR ACS_DIMENS OR 00Fh,000h>

gdt_all desc_struc <0ffffh,00000h,000h,DATA_ACC               OR ACC_RING0,ACS_GRANUL OR 00Fh              ,000h>

gdt_tds32 desc_struc <0ffffh,00000h,000h,DATA_ACC               OR ACC_RING0,ACS_GRANUL OR 00Fh              ,000h>
gdt_tcs32 desc_struc <0ffffh,00000h,000h,CODE_ACC OR ACC_DATAWR OR ACC_RING0,ACS_GRANUL OR ACS_DIMENS OR 00Fh,000h>
gdt_tss32 desc_struc <0ffffh,00000h,000h,DATA_ACC               OR ACC_RING0,ACS_GRANUL OR ACS_DIMENS OR 00Fh,000h>
gdt_ssdebug desc_struc <0ffffh,00000h,000h,DATA_ACC               OR ACC_RING0,ACS_GRANUL OR ACS_DIMENS OR 00Fh,000h>

gdt_pzu desc_struc <007ffh,00000h,080h,DATA_ACC               OR ACC_RING0,ACS_GRANUL OR 000h              ,0ffh>
gdt_cpzu desc_struc <007ffh,00000h,000h,DATA_ACC               OR ACC_RING0,ACS_GRANUL OR 000h              ,0ffh>

gdt_ramq desc_struc <003ffh,00000h,000h,DATA_ACC               OR ACC_RING0,ACS_GRANUL OR 000h              ,080h>
gdt_ramx desc_struc <003ffh,00000h,080h,DATA_ACC               OR ACC_RING0,ACS_GRANUL OR 000h              ,080h>
gdt_ramy desc_struc <003ffh,00000h,000h,DATA_ACC               OR ACC_RING0,ACS_GRANUL OR 000h              ,081h>
gdt_ramz desc_struc <003ffh,00000h,080h,DATA_ACC               OR ACC_RING0,ACS_GRANUL OR 000h              ,081h>

gdt_tss_main desc_struc <06bh,,,TSS_ACC,0,0>
gdt_tss_task1 desc_struc <06bh,,,TSS_ACC,0,0>
gdt_tss_task2 desc_struc <06bh,,,TSS_ACC,0,0>
gdt_tss_task3 desc_struc <06bh,,,TSS_ACC,0,0>
gdt_tss_task4 desc_struc <06bh,,,TSS_ACC,0,0>

gdt_ds1 desc_struc <00001h,00000h,000h,DATA_ACC               OR ACC_RING3,ACS_GRANUL OR 000h              ,000h>
gdt_cs1 desc_struc <00002h,00000h,000h,CODE_ACC OR ACC_DATAWR OR ACC_RING0,ACS_GRANUL OR ACS_DIMENS OR 000h,000h>;селектор 0e0h
gdt_ss1 desc_struc <00003h,00000h,000h,DATA_ACC               OR ACC_RING3,ACS_GRANUL OR ACS_DIMENS OR 000h,000h>
далее, перехожу в защищённый режим, инициализирую gdt_cs1 (адрес в памяти, предел), в нём по адресу 01010h размещаю код:
Код:
stlbpnt	PROC	far
  mov   dx, 1070h
  mov   eax,055h
  out   dx, eax
  hlt
  ret
stlbpnt ENDP
порт 1070h - введён специально для отладки (это нестандартная ПК).
далее перехожу непосредственно на этот код:
Код:
	DB		09ah
DD 01010h
DW 0e0h
по завершению из порта 1070h читаю 55h. Здесь все работает на ура.

А так нет:

ввожу изменения:
Код:
...

gdt_cs1 desc_struc <00002h,00000h,000h,CODE_ACC OR ACC_DATAWR OR ACC_RING3,ACS_GRANUL OR ACS_DIMENS OR 000h,000h>;селектор 0e3h

...


DB 09ah
DD 01010h
DW 0e3h
После чего сразу выскакивает исключение 13. Порт 1070h остается без изменений.

Что здесь нетак?
« Последнее редактирование: 01-07-2010 16:36 от Алексей1153++ » Записан
darkelf
Молодой специалист

Offline Offline

« Ответ #1 : 01-07-2010 10:08 » 

Я не уверен, но у Вас в tss указано разрешение для работы с портами? насколько я помню там должна быть битовая маска разрешенных портов после основных полей tss.
Записан
zloi7777
Постоялец

ru
Offline Offline

« Ответ #2 : 01-07-2010 10:21 » 

Я в программе не использую пока мультизадачность ( просто дескрипторы зарезервировал)
« Последнее редактирование: 01-07-2010 16:37 от Алексей1153++ » Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #3 : 01-07-2010 10:34 » 

Если мне не изменяет память, защита портов опциональна - можно ее совсем отключить.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
darkelf
Молодой специалист

Offline Offline

« Ответ #4 : 01-07-2010 11:47 » 

У меня получалось переключаться на более низкий уровень привилегий только через tss.
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #5 : 01-07-2010 12:44 » 

У x86 полно механизмов движения по привилегиям. Например, шлюз вызова и шлюз прерывания.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 01-07-2010 16:25 » 

Привожу кусок условного код OUT из справочника:


IF (PE = 1) AND ((VM = 1) OR (CPL > IOPL)) THEN
  (* Тут делается проверка по битовой карте разрешений портов (указатель на нее находится в TSS) *)
FI


Поле IOPL находится в EFLAGS (биты 12 и 13).
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
zloi7777
Постоялец

ru
Offline Offline

« Ответ #7 : 01-07-2010 18:29 » 

Шлюз вызова, вроде  для уменьшения DPL(повышения привелегий) используется....
(но навсяк случай через шлюз я тоже проверил, эфект тотже, при DPL=0 все работает, при DPL=3 при вызове шлюза(DPL=0) дает исключение 13).

А поп поводу портов, я переключения задач неделаю и tss непрогружаю, и недолжно запись в порт вызывать исключение 13..... Жаль
Записан
zloi7777
Постоялец

ru
Offline Offline

« Ответ #8 : 01-07-2010 18:36 » 

а переключится через TSS, надо попробовать будет.......спасибо.......

но негде в литературе про это даже неупоминается ...  Жаль
P.S.  про перегрузку стека для DPL=3 я в курсе, но я спецально после записи в порт поставил hlt/
Записан
Serg79
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #9 : 01-07-2010 19:37 » 

zloi7777, честно говоря из приведенного тобой кода ничего не понятно, набор инструкций препроцессора и не более, да куски машинных байт - это то что ты показал.

Как ты пытаешься понизить уровень привилегии исполняемого кода? Как ты диагностируешь, что у тебя кидается исключение "общей защиты"?

Для начала приведи маленький кусок ассемблерного кода переключения в защищенный режим с минимальным набором необходимых дескрипторов собранных в таблицу глобальных дескрипторов. Так же покажи код на ассемблере который понижает привилегию исполняемого кода до третьего уровня, и все дескрипторы в таблице глобальных дескрипторов которые будут описывать все необходимые сегменты для работы кода на третьем уровне защиты.
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #10 : 01-07-2010 19:38 » 

Шлюзы как раз задуманы именно для согласования кода разного уровня.


Мда. Освежил память: только через дескриптор задачи можно понизить.
« Последнее редактирование: 01-07-2010 19:59 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
zloi7777
Постоялец

ru
Offline Offline

« Ответ #11 : 01-07-2010 19:53 » 

Эфект от использования шлюза точно такойже - при DPL=0 все работает, при DPL=3 нет( на всяки случай уже проверил).
Записан
Serg79
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #12 : 01-07-2010 20:04 » 

Вообще RXL правильно заметил:
Привожу кусок условного код OUT из справочника:


IF (PE = 1) AND ((VM = 1) OR (CPL > IOPL)) THEN
  (* Тут делается проверка по битовой карте разрешений портов (указатель на нее находится в TSS) *)
FI


Поле IOPL находится в EFLAGS (биты 12 и 13).
В регистре флагов EFLAGS есть двухбитовое поле IOPL - I/O Privilege Level (уровень привилегий для ввода/вывода). Это поле определяет максимальный уровень привилегий, который может иметь код задачи при операциях ввода/вывода.

Карта разрешения ввода/вывода используется, когда процессор работает в режиме виртуального 8086, либо когда в задаче CPL > IOPL. Если процессор находится в защищённом режиме и выполняет задачу, CPL которой не больше её IOPL, то операции ввода/вывода разрешены по всем адресам.
Записан
zloi7777
Постоялец

ru
Offline Offline

« Ответ #13 : 01-07-2010 20:09 » 

в первом коде показана начальная часть таблици дискрипторов включая gdt_cs1 описывающий сегмент куда я перехожу.
во втором код записанный в целевом сегмене расположеный по адресу 1010h
в третьем сам переход на сегмент описанный в gdt_cs1 (правдо машинным кодом)
и в таком виде при DPL=0 все работает.

далее привожу изменения для DPL=3.

Опускаю перевод в защищенный режим, инициализацию таблици прерываний,  выделение памяти для gdt_cs1, загрузку программы из флэш памяти в выделенную память.....
Записан
Serg79
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #14 : 01-07-2010 20:11 » 

Кстати zloi7777, если мне не изменяет память ты уже спрашивал что то про дальние вызовы в защищенном режиме: https://forum.shelek.ru/index.php/topic,24193.0.html .

Чем закончились твои эксперименты? Тебе удалось найти С-ый компилятор который бы мог генерировать код для многосегментных приложений защищенного режима?
Записан
zloi7777
Постоялец

ru
Offline Offline

« Ответ #15 : 01-07-2010 20:12 » 

про IOPL в EFLAGS, незнал ,спасибо, проверю. заменю на ячейку памяти вместо порта, коль одна и таже прерва.

Записан
Serg79
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #16 : 01-07-2010 20:14 » 

zloi7777, если не секрет, как ты выделяешь память для gdt_cs1?
...
выделение памяти для gdt_cs1,
...
Не флейма ради, просто интересно.
Записан
zloi7777
Постоялец

ru
Offline Offline

« Ответ #17 : 01-07-2010 20:16 » 

Сделал доп библиотеку на асемблере с вызовами шлюзов, и использую с обычными компиляторами Си.
Записан
Serg79
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #18 : 01-07-2010 20:17 » 

про IOPL в EFLAGS, незнал ,спасибо, проверю. заменю на ячейку памяти вместо порта, коль одна и таже прерва.
Только обрати внимание на следующую фразу:
...
Карта разрешения ввода/вывода используется
...
Это говорит о том, что тебе необходимо так же будет подготовить карту ввода/вывода в TSS.
Записан
zloi7777
Постоялец

ru
Offline Offline

« Ответ #19 : 01-07-2010 20:21 » 

Памяти в устройстве мало, страничную делать смысла небыло, сделал примитивный свой диспечер.
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #20 : 01-07-2010 20:23 » 

Или сделать IOPL = 3 и карта будет не нужна.


Страничный механизм упрощает работу с динамической памятью.
Совсем забыл, что ты выбрал многосегментую модель...
« Последнее редактирование: 01-07-2010 20:25 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
zloi7777
Постоялец

ru
Offline Offline

« Ответ #21 : 01-07-2010 20:26 » 

я порт уберу будет память для проверки если выполница процедура stlbpnt.
хотя при отсутствии там записи в порт тоже 13-е исключение было, ща тока припомнил, проверял я это...((
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #22 : 01-07-2010 20:27 » 

zloi7777, не торопись писать - сперва прочти, что уже написали. После того как напишешь, прочти, что написал - очень рваный и малопонятный текст у тебя.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
zloi7777
Постоялец

ru
Offline Offline

« Ответ #23 : 01-07-2010 20:34 » 

Для динамической памяти - да  удобно.
К страничной может и вернусь, потом, если целесобразно будет,
а пока мне на пользовательский уровень неперейти.))


За рванасть извеняйте, я и так над сабой нечеловечиские усилия делаю, честно.
Записан
zloi7777
Постоялец

ru
Offline Offline

« Ответ #24 : 05-07-2010 04:20 » 

Попытался сделать через TSS, все тоже, на без понижения привилегии(ring0), все работает, при понижении до ring3 - все умирает, теперь даже без исключения   Жаль
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #25 : 05-07-2010 05:24 » 

zloi7777, ты IOPL=3 выставил в TSS?
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
zloi7777
Постоялец

ru
Offline Offline

« Ответ #26 : 05-07-2010 06:02 » 

да,
и заодно еще от out отказался, просто в доступную ячейку памяти пишу.
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #27 : 05-07-2010 06:17 » 

Значит дело не в бобине... Ищи ошибку. Почитай еще раз доки. Проверь все поля TSS на корректность.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
darkelf
Молодой специалист

Offline Offline

« Ответ #28 : 05-07-2010 07:40 » 

У меня следующий TSS работал (tasm):
Код:
;TSS для переключения на уровень привилегий 3
tss segment para use32
        dd 0 ; 00h обратная связь
        dd 0fffffffch; 04h esp0
        dd STACK32SEG0 ; 08h ss0
        dd 0 ; 0ch esp1
        dd 0 ; 10h ss1
        dd 0 ; 14h esp2
        dd 0 ; 18h ss2
        dd 0 ; 1ch cr3
        dd 0 ; 20h eip
        dd 00000000000000000011001000000010b ; 24h efl iopl=3 if=1
        dd 0 ; 28h eax
        dd 0 ; 2ch ecx
        dd 0 ; 30h edx
        dd 0 ; 34h ebx
        dd 0fffffffch ; 38h esp
        dd 0 ; 3ch ebp
        dd 0 ; 40h esi
        dd 0 ; 44h edi
        dd DATA32SEG3 ; 48h es
        dd CODE32SEG3 ; 4ch cs
        dd STACK32SEG3 ; 50h ss
        dd DATA32SEG3 ; 54h ds
        dd 0 ; 58h fs
        dd 0 ; 5ch gs
        dd 0 ; 60h ldtr
        dd 0 ; 64h trap flag
tss ends

В коде 0-го уровня привилегий выполнялся переход на данный TSS:

Код:
;переключаем задачу
        db 0eah
        dd 0
        dw TSSSEG

Таблица дескрипторов имела следующий вид:

Код:
; Глобальная дескрипторная таблица (GDT)
gdt     equ $
NULLSEG equ 0 ; первый пустой дескриптор
        dw 0,0,0,0
CODE32SEG3 equ 8+3 ; селектор кода с уровнем привилегий 3
        desc_seg_make 0, 0, CODEXO, 3, 1, 0, 1
DATA32SEG3 equ 16+3 ; селектор данных с уровнем привилегий 3
        desc_seg_make 0, 0, DATARW, 3, 1, 0, 1
STACK32SEG3 equ 24+3 ; селектор стека с уровнем привилегий 3
;стек объявляется как сегмент данных
;        desc_seg_make 0, 0, DATARW, 0, 1, 0
;стек объявляется как сегмент стека
        desc_seg_make 0, 0, STACKRW, 3, 1, 1, 1
CODE32SEG0 equ 32 ; селектор кода с уровнем привилегий 0
        desc_seg_make 0, 0, CODEXO, 0, 1, 0, 1
STACK32SEG0 equ 40 ; селектор стека с уровнем привилегий 0
;стек объявляется как сегмент данных
;        desc_seg_make 0, 0, DATARW, 0, 1, 0
;стек объявляется как сегмент стека
        desc_seg_make 0, 0, STACKRW, 0, 1, 1, 1
VIDEO32SEG equ 48 + 3 ; селектор сегмента видео памяти для отладки
        desc_seg_make 0b8000h, 4096, DATARW, 3, 1, 0, 1
TSSSEG equ 56 ; селектор сегмента TSS
        desc_tss_make 0, 0, 3, 1

Макросы создания дескрипторов для сегментов/TSS:
Код:
;коды типов сегментов
DATARO  equ 000b ;сегмент данных только для чтения
DATARW  equ 001b ;сегмент данных для чтения/записи
STACKRO equ 010b ;сегмент стека только для чтения (не используется)
STACKRW equ 011b ;сегмент стека для чтения/записи
CODEXO  equ 100b ;сегмент кода, разрешено только выполнение
CODERX  equ 101b ;сегмент кода, разрешено чтение/выполнение

;создать описатель дескриптора сегмента
desc_seg_make macro base,limit,type,dpl,d,g,pr
        dw limit AND 0ffffh ; предел 15-0
        dw base AND 0ffffh ; база 23-16
        db (base SHR 16) AND 0ffh ; база, биты 23-16
        ; тип сегмента, бит присутствия, признак сегмента, уровень привилегий
        db ((type AND 7) SHL 1) OR ((pr AND 1) SHL 7) OR (1 SHL 4) OR ((dpl AND 3) SHL 5)
        ; предел 19-16, бит используется пользователем, бит размера по умолчанию
        ; 0 - 16 бит, 1 - 32
        db ((limit SHR 16) AND 0fh) OR ((d AND 1) SHL 6) OR ((g AND 1) SHL 7)
        db (base SHR 24) AND 0ffh ;база 31-24
        endm

;создать описатель дескриптора TSS
desc_tss_make macro base,limit,dpl,pr
        dw limit AND 0ffffh ; предел 15-0
        dw base AND 0ffffh ; база 23-16
        db (base SHR 16) AND 0ffh ; база, биты 23-16
        ; тип сегмента, бит присутствия, признак сегмента, уровень привилегий
        db 1001b OR ((pr AND 1) SHL 7) OR ((dpl AND 3) SHL 5)
        db ((limit SHR 16) AND 0fh) ; предел 19-16
        db (base SHR 24) AND 0ffh ;база 31-24
        endm

Именно с портами не работал, но в видеопамять писалось нормально.

Сорри за оверквотинг.
Записан
zloi7777
Постоялец

ru
Offline Offline

« Ответ #29 : 05-07-2010 12:01 » new

darkelf,  Спасибо, через TSS заработало. У меня было почти как и в твоем коде, тока в TSS es был прогружен селектором с DPL=0, поменял и все запахало.
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines