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

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Немогу перейти в кодовый сегмент в большим DPL.  (Прочитано 33349 раз)
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
Молодой специалист

no
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
Молодой специалист

no
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
Молодой специалист

no
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