
Este artigo é a conclusão lógica de uma
série de artigos sobre
"escalada Elbrus" sobre a introdução da
arquitetura de processador Embox à
Elbrus (E2K) . Por que uma conclusão lógica, porque, como resultado, foi possível executar através do telnet um aplicativo que exibe a imagem na tela, ou seja, para obter a operação completa da Embox nessa arquitetura. Pesquisas posteriores dificilmente podem ser chamadas de introdução, embora, é claro, muito permaneça incerto. E a arquitetura em si tem muitos recursos interessantes, que também não são atualmente compreendidos. Neste artigo, focaremos na organização da memória virtual, abordaremos o PCI, falaremos um pouco sobre uma placa de rede e abordaremos uma placa de vídeo em um hardware específico que possuímos.
Para quem tem preguiça de ler o artigo, darei imediatamente um pequeno vídeo com os resultados.
E agora, para quem estiver interessado, revelaremos os detalhes técnicos que pudemos entender no processo.
Memória virtual
Nosso erro de pilha
Vamos começar com a memória virtual. Na verdade, foi nisso que decidimos no
artigo anterior
da série. Vale lembrar imediatamente por que precisávamos de memória virtual, porque a Embox pode funcionar sem ela. É simples: a coisa é fazer cache. Vidyaha funcionou, mas eu tive que gravar a mesma coisa duas vezes na memória de vídeo para obter uma recuperação de imagem confiável. Obviamente, era possível lidar com o cache, mas nossos exemplos envolvem o uso de memória de vídeo diretamente de um aplicativo do usuário, sem nenhum material nuclear, como gerenciamento de cache, por isso foi correto aprender como mapear a memória como não armazenável em cache. O mesmo pode ser feito no Linux, mapeando fb (
exemplo ).
Vale ressaltar que, embora não escrevamos sobre Elbrus por um longo tempo e possa parecer que o MMU nessa arquitetura seja algum tipo de super complicado, mas a coisa é diferente. De fato, adicionamos apoio no verão, simplesmente não chegamos a nossas mãos para escrever sobre isso. Passamos muito tempo (vários meses) devido ao nosso erro estúpido. Este erro foi cometido no título do artigo ("Ou nunca esqueça o que você obteve durante a inteligência"). Estamos falando de pilhas com as quais lidamos muito bem e descrevemos isso no artigo Climbing
Elbrus - Reconnaissance. Parte técnica 1. Registros, pilhas e outros detalhes técnicos .
" Sofremos por muito tempo, perdendo de vista estupidamente o fato de pegar a pilha inicial (na qual o sistema é inicializado) de algum lugar externo e mapear tudo. do que precisamos para a Embox funcionar, não mapeamos esses dados.
Abaixo do gato, darei uma nova função e2k_entry, descrita no segundo artigo do
artigo da série .
Se desejar, você pode comparar.
__attribute__ ((__section__(".e2k_entry"))) void e2k_entry(struct pt_regs *regs) { if (entries_count >= CPU_COUNT) { e2k_trap_handler(regs); RESTORE_COMMON_REGS(regs); E2K_DONE; } e2k_wait_all(); entries_count = __e2k_atomic32_add(1, &entries_count); if (entries_count > 1) { while(!sync_count); context_init(&cpu_ctx[0], CONTEXT_PRIVELEGED | CONTEXT_IRQDISABLE, cpu_idle, idle_stack, sizeof(idle_stack)); context_switch(&cpu_ctx_prev[0], &cpu_ctx[0]); } context_init(&cpu_ctx[1], CONTEXT_PRIVELEGED | CONTEXT_IRQDISABLE, e2k_kernel_start, &_stack_top, KERNEL_STACK_SZ); sync_count = __e2k_atomic32_add(1, &sync_count); context_switch(&cpu_ctx_prev[1], &cpu_ctx[1]); }
Vou apenas explicar que agora usamos as funções context_init () e context_switch () apenas para mudar a pilha para a memória no espaço Embox. E fazemos isso para todos os núcleos, incluindo aqueles que não são usados.
Organização da MMU
Agora vou falar um pouco sobre a organização da MMU na arquitetura E2k.
Em geral, a arquitetura MMU é bastante comum e possui tabelas de quatro níveis (ou três ao usar uma página de 4 MB).
Existem vários registros de serviço na arquitetura E2k, que são acessados usando comandos de acesso a espaços alternativos, bem como ao espaço de E / S descrito brevemente no artigo
"Embox começa a escalar o Elbrus" .
Vamos precisar desses registros:
#define MMU_REG_CR 0x00 #define MMU_REG_CONT 0x10 #define MMU_REG_CR3_RG 0x20 #define MMU_REG_ELB_PTB 0x30 #define MMU_REG_ROOT_PTB 0x40 /
Na verdade, este é um registro de controle, um registro de número de contexto, um registro raiz de tabelas e um pouco obscuro MMU_REG_ELB_PTB. Vamos começar: esse registro deve ser definido com algum valor, os próximos 512 GB serão usados pelo processador para as necessidades do equipamento e esses endereços não estarão disponíveis para o programador. Fornecerei explicações da carta do especialista em ICST, mal posso explicar melhor:
No Linux, configuramos MMU_ELB_PTB para 0xff1 << 39 e, em seguida,
área superior da memória virtual (0xff8000000000 - 0xffffffffffff)
reservado para as necessidades de equipamentos, ou seja, TLB. Cada página
A tabela de páginas (TS) obtém seu endereço exclusivo nessa área,
além disso, esses endereços são facilmente obtidos no endereço em que o programa
apelou para a memória. E desde TLB armazena mapeamentos de endereços virtuais
físico, isso permite armazenar em cache no mesmo buffer TLB
transmite não apenas para endereços de usuários, mas também para o próprio veículo.
Nos processadores / arquiteturas em que TLBs separados são feitos para diferentes
níveis da tabela de páginas, esse truque se torna desnecessário.
Assim, quando você perde o TLB, torna-se possível não iniciar a pesquisa
do nível zero (pgd *) e verifique imediatamente o último nível do veículo (pte *).
Não há nenhum requisito de hardware para mapear essa área em si, Wirth. endereços de
é necessário apenas como índices para pesquisa TLB. No entanto, no cerne da
o último pgd no nível zero da tabela de páginas é escrito nat. o endereço deste
nível mais zero. Como resultado, apenas
os últimos 4 KB da área ff80'0000'0000 - ffff'ffff'ffff - ou seja, apenas certo
nível zero do veículo. Isso permite que o pgd * seja acessado por
ler / escrever instruções trabalhando em endereços virtuais.
Como resultado, decidiu-se simplesmente colocar neste registro um grande valor, o que não nos incomodará. Afinal, a solução proposta nos permite otimizar a pesquisa de páginas, mas ainda não estamos envolvidos na otimização. Entregue o mesmo que no Linux.
Agora, o registro de controle. Você precisa habilitar o MMU através dele. Os bits conhecidos são assim:
#define _MMU_CR_TLB_EN 0x0000000000000001 #define _MMU_CR_CD_MASK 0x0000000000000006 #define _MMU_CR_SET1 0x0000000000000008 #define _MMU_CR_SET2 0x0000000000000010 #define _MMU_CR_SET3 0x0000000000000020 #define _MMU_CR_CR0_PG 0x0000000000000040 #define _MMU_CR_CR4_PSE 0x0000000000000080 #define _MMU_CR_CR0_CD 0x0000000000000100 #define _MMU_CR_TLU2_EN 0x0000000000000200 #define _MMU_CR_LD_MPT 0x0000000000000400 #define _MMU_CR_IPD_MASK 0x0000000000000800 #define _MMU_CR_UPT_EN 0x0000000000001000
Estamos interessados no primeiro bit, que inclui a tradução de endereços.
Também definimos _MMU_CR_SET3, mas ainda não descobrimos em quais casos específicos isso deve ser feito.
Registo no concurso Bem, se simples, esse é o PID do processo ou espaço de endereço. Mais tecnicamente, essa é uma extensão de endereço de 11 bits. No nosso caso, tornamos todas as páginas nucleares, definindo o pouco de globalidade em todas as nossas páginas, usamos o mesmo espaço de endereço e, portanto, podemos usar zero nesse registro.
No registro da tabela raiz, há um ponteiro para o endereço físico do início da tabela de conversão. Você pode simplesmente fazer uma fraude mapeando a tabela também para o endereço especificado no registro MMU_REG_ELB_PTB, mas como eu disse, não estávamos focados na otimização.
O que mais posso dizer, a estrutura das tabelas é bastante comum, os sinalizadores são os seguintes:
#define E2K_MMU_PAGE_P 0x0000000000000001ULL #define E2K_MMU_PAGE_W 0x0000000000000002ULL #define E2K_MMU_PAGE_UU2 0x0000000000000004ULL #define E2K_MMU_PAGE_PWT 0x0000000000000008ULL #define E2K_MMU_PAGE_CD1 0x0000000000000010ULL #define E2K_MMU_PAGE_A 0x0000000000000020ULL #define E2K_MMU_PAGE_D 0x0000000000000040ULL #define E2K_MMU_PAGE_HUGE 0x0000000000000080ULL #define E2K_MMU_PAGE_G 0x0000000000000100ULL #define E2K_MMU_PAGE_CD2 0x0000000000000200ULL #define E2K_MMU_PAGE_NWA 0x0000000000000400ULL #define E2K_MMU_PAGE_AVAIL 0x0000000000000800ULL #define E2K_MMU_PAGE_PFN 0x000000fffffff000ULL #define E2K_MMU_PAGE_VALID 0x0000010000000000ULL #define E2K_MMU_PAGE_PV 0x0000020000000000ULL #define E2K_MMU_PAGE_INT_PR 0x0000040000000000ULL #define E2K_MMU_PAGE_NON_EX 0x0000080000000000ULL #define E2K_MMU_PAGE_RES 0x0000f00000000000ULL #define E2K_MMU_PAGE_C_UNIT 0xffff000000000000ULL
Para a tabela de quatro níveis, as mudanças de endereço são as seguintes:
#define __MMU_PGD_SHIFT (PAGE_SHIFT + 3 * (PAGE_SHIFT-3)) #define __MMU_PUD_SHIFT (PAGE_SHIFT + 2 * (PAGE_SHIFT-3)) #define __MMU_PMD_SHIFT (PAGE_SHIFT + 1 * (PAGE_SHIFT-3))
Um pouco sobre PCI
Comunicação através de espaços de endereço alternativos
Antes de passar para o vidyaha e a placa de rede, voltemos brevemente ao PCI. Já conversamos um pouco sobre isso na primeira parte de
"A Embox começa a escalar o Monte Elbrus" . Ele mostrou macros para comunicação com espaços de endereço alternativos:
#define _E2K_READ_MAS(addr, mas, type, size_letter, chan_letter) \ ({ \ register type res; \ asm volatile ("ld" #size_letter "," #chan_letter " \t0x0, [%1] %2, %0" \ : "=r" (res) \ : "r" ((__e2k_ptr_t) (addr)), \ "i" (mas)); \ res; \ }) #define _E2K_WRITE_MAS(addr, val, mas, type, size_letter, chan_letter) \ ({ \ asm volatile ("st" #size_letter "," #chan_letter " \t0x0, [%0] %2, %1" \ : \ : "r" ((__e2k_ptr_t) (addr)), \ "r" ((type) (val)), \ "i" (mas) \ : "memory"); \ })
E havia uma referência ao princípio dos espaços de endereço. Diferentes espaços de endereço são definidos usando o MAS (especificador de endereço de memória). E, por exemplo, para acessar o IO, através do qual o PCI é acessado, você precisa usar 6 e o MMU 7.
Mas com um estudo mais aprofundado da macro, você pode notar algum tipo de carta_de_ chan. E se você olhar para a descrição dos comandos e2k, encontramos
LDD ddd leitura de duas palavras
ldd [address] mas, dst
Ou seja, à primeira vista, não há canais. Mas, se você seguir os links, o código para a operação ldd especificada é 67. Mas 67 é o código para ldd apenas para os canais AL0 / AL3 e AL2 / AL5, e para os canais AL1 / AL4, esse código corresponde à operação POPCNTd.
Portanto, não foi possível entender completamente quais canais estão na terminologia de Elbrus. Atrevo-me a sugerir que isso já está conectado ao princípio vliw, quando você pode especificar qual alu é usada, porque nesse tipo de arquitetura um dos recursos é a presença de vários dispositivos de computação independentes. É claro que posso estar enganado, mas o fato é que, para acessar o PCI ou MMU, é necessário usar o segundo ou o quinto canal. Assim, o comando será algo como isto:
ldd, 2 0x0, [addr_in_mas] mas_id,% reg
lspci
Agora vou dar o resultado da saída do comando lspci no dispositivo que temos:
root @ embox: (nulo) #lspci
00: 0.0 (PCI dev E3E3: ABCD) [6 4]
Ponte PCI-to-PCI: (nula) ponte Elbrus PCIe (rev 01)
00: 1.0 (PCI dev 8086: E3E3) [6 4]
Ponte PCI-to-PCI: ponte Intel Corporation Elbrus Virt PCI (rev 01)
01: 0.0 (PCI dev 1FFF: 8000) [6 4]
Ponte PCI-para-PCI: (nula) Ponte PCI Elbrus (rev 05)
01: 1.0 (PCI dev 8086: 4D45) [2 0]
Controlador Ethernet: Intel Corporation MCST ETH1000 Gigabit Ethernet (rev 01)
01: 2.0 (PCI dev 8086: 4D49) [1 1]
Controlador IDE: Intel Corporation MCST IDE (rev 128)
01: 2.1 (PCI dev 8086: 0002) [7 2]
Comunicação simples. controlador: Intel Corporation (nulo) (rev 05)
01: 2.2 (PCI dev 8086: 8000) [7128]
Comunicação simples. controlador: ponte PCI Intel Corporation Elbrus (rev 00)
01: 2.3 (PCI dev 1013: 6005) [4 1]
Dispositivo multimídia: Cirrus Logic Crystal CS4281 PCI Audio (rev 01)
01: 3.0 (dev 8086: 4748 do PCI) [1 6]
Controlador de armazenamento em massa: Intel Corporation MCST SATA (rev 00)
01: 4.0 (PCI dev 8086: 554F) [12 3]
Dispositivo USB: Intel Corporation OHCI para Elbrus (rev 00)
01: 4.1 (PCI dev 8086: 5545) [12 3]
Dispositivo USB: Intel Corporation EHCI para Elbrus (rev 00)
02: 1.0 (PCI dev 126F: 0718) [3 0]
Controlador compatível com VGA: Silicon Motion, Inc. SM718 LynxSE + (rev 160)
root @ embox: (nulo) #
Nota
01: 2.2 (PCI dev 8086: 8000) [7128]
Comunicação simples. controlador: ponte PCI Intel Corporation Elbrus (rev 00)
De fato, é uma porta serial do MCST semelhante à am85c30, pelo menos através deste dispositivo que nos comunicamos através do minicom.
Placa de rede
Estrutura geral
Agora vamos para a placa de rede.
Se bem entendi, essa é a placa de rede original, um pouco semelhante em operação ao e1000, mas apenas em operação (como a presença de descritores nas filas de recebimento e transmissão).
Agora, mais sobre os pontos importantes que encontramos.
Placa de rede PCI VID: PID 0x8086: 0x4D45. Não se surpreenda que o VID seja o mesmo da Intel, o MCST geralmente usa esse VID em particular; veja pelo menos o dispositivo da porta serial mencionado acima.
BAR0 contém uma base de registro. Os registros são os seguintes:
#define L_E1000_E_CSR 0x00 #define L_E1000_MGIO_CSR 0x04 #define L_E1000_MGIO_DATA 0x08 #define L_E1000_E_BASE_ADDR 0x0c #define L_E1000_DMA_BASE_ADDR 0x10 #define L_E1000_PSF_CSR 0x14 #define L_E1000_PSF_DATA 0x18 #define L_E1000_INT_DELAY 0x1c
Os três últimos (L_E1000_PSF_CSR, L_E1000_PSF_DATA, L_E1000_INT_DELAY) que não usamos, portanto não falaremos sobre eles. Vamos começar com o MGIO, tudo é simples: leitura e gravação usando o protocolo MII, ou seja, comunicação com o chip PHY. Especificamente, temos um chip DP83865.
Os procedimentos não são particularmente notáveis, simplesmente os listarei.
Leitura:
static int e1000_mii_readreg(struct net_device *dev, int phy_id, int reg_num) { struct l_e1000_priv *ep = netdev_priv(dev); uint32_t rd; uint16_t val_out = 0; int i = 0; rd = 0; rd |= 0x2 << MGIO_CS_OFF; rd |= 0x1 << MGIO_ST_OF_F_OFF; rd |= 0x2 << MGIO_OP_CODE_OFF; rd |= (phy_id & 0x1f) << MGIO_PHY_AD_OFF; rd |= (reg_num & 0x1f) << MGIO_REG_AD_OFF; e1000_write_mgio_data(ep, rd); rd = 0; for (i = 0; i != 1000; i++) { if (e1000_read_mgio_csr(ep) & MGIO_CSR_RRDY) { rd = (uint16_t)e1000_read_mgio_data(ep); val_out = rd & 0xffff; log_debug("reg 0x%x >>> 0x%x", reg_num, val_out); return val_out; } usleep(100); } log_error("mdio_read: Unable to read from MGIO_DATA reg\n"); return val_out; }
Registro:
static void e1000_mii_writereg(struct net_device *dev, int phy_id, int reg_num, int val) { struct l_e1000_priv *ep = netdev_priv(dev); uint32_t wr; int i = 0; wr = 0; wr |= 0x2 << MGIO_CS_OFF; wr |= 0x1 << MGIO_ST_OF_F_OFF; wr |= 0x1 << MGIO_OP_CODE_OFF; wr |= (phy_id & 0x1f) << MGIO_PHY_AD_OFF; wr |= (reg_num & 0x1f) << MGIO_REG_AD_OFF; wr |= val & 0xffff; log_debug("reg 0x%x <<< 0x%x", reg_num, val); e1000_write_mgio_data(ep, wr); for (i = 0; i != 1000; i++) { if (e1000_read_mgio_csr(ep) & MGIO_CSR_RRDY) { return; } usleep(100); } log_error("Unable to write MGIO_DATA reg: val = 0x%x", wr); return; }
Agora L_E1000_DMA_BASE_ADDR e L_E1000_E_BASE_ADDR, na verdade, eles descrevem um parâmetro, o endereço do bloco de descrição da placa de rede. Ou seja, o endereço no Elbrus é de 64 bits e os registros são de 32 bits.
Na verdade codifique:
init_block_addr_part = (uint32_t)((uintptr_t)ep->init_block & 0xffffffff); e1000_write_e_base_addr(ep, init_block_addr_part); log_debug("Init Block Low DMA addr: 0x%x", init_block_addr_part); init_block_addr_part = (uint32_t)(((uintptr_t)(ep->init_block) >> 32) & 0xffffffff); e1000_write_dma_base_addr(ep, init_block_addr_part); log_debug("Init Block High DMA addr: 0x%x", init_block_addr_part);
A partir do qual é possível ver que L_E1000_DMA_BASE_ADDR é a parte superior e L_E1000_DMA_BASE_ADDR é a parte inferior do endereço de um determinado bloco de inicialização (na verdade, um bloco de descrição de cartões).
A estrutura da descrição é a seguinte:
struct l_e1000_init_block { uint16_t mode; uint8_t paddr[6]; uint64_t laddrf; uint32_t rdra; uint32_t tdra; } __attribute__((packed));
C laddrf - não entendi, por algum motivo, é colocado a zero, fizemos o mesmo.
paddr - como você pode imaginar, mac é o endereço da placa de rede.
rdra e tdra contêm os endereços dos anéis dos descritores de memória, os 4 bits inferiores são alocados ao tamanho do anel e esse é o logaritmo do tamanho. Ou seja, se houver 8, o número de descritores no anel será 2 ^ 8 (1 << 8 == 256).
mode é o modo de operação do cartão, os bits são os seguintes:
#define DRX (1 << 0) #define DTX (1 << 1) #define LOOP (1 << 2) #define DTCR (1 << 3) #define COLL (1 << 4) #define DRTY (1 << 5) #define INTL (1 << 6) #define EMBA (1 << 7) #define EJMF (1 << 8) #define EPSF (1 << 9) #define FULL (1 << 10) #define PROM (1 << 15)
Ou seja, quando tudo estiver configurado, você precisará definir o bit 10. Se desejar um modo promíscuo, também 15.
Descritores de pacotes
Agora, sobre o formato dos descritores de pacotes.
Na recepção:
struct l_e1000_rx_desc { uint32_t base; int16_t buf_length; int16_t status; int16_t msg_length; uint16_t reserved1; uint32_t etmr; } __attribute__((packed));
base - provavelmente entenda que este é o endereço do buffer para o pacote
buf_length - tamanho do buffer
msg_length - depois de receber, contém o comprimento do pacote recebido
status - status do descritor. Quando o pacote é preparado e entregue ao DMA (cartão), você precisa definir o bit 15 (RD_OWN). Se tudo estiver bem, depois de receber o pacote neste descritor, esse bit será redefinido e 9 (RD_STP) e 8 (RD_ENP) serão configurados.
Todos os bits de status são os seguintes:
#define RD_OWN (1 << 15) #define RD_ERR (1 << 14) #define RD_FRAM (1 << 13) #define RD_OFLO (1 << 12) #define RD_CRC (1 << 11) #define RD_BUFF (1 << 10) #define RD_STP (1 << 9) #define RD_ENP (1 << 8) #define RD_PAM (1 << 6) #define RD_LAFM (1 << 4) #define RD_BAM (1 << 3)
Na transferência:
struct l_e1000_tx_desc { uint32_t base; int16_t buf_length; int16_t status; uint32_t misc; uint32_t etmr; } __attribute__((packed));
Quase o mesmo que receber, os bits de status são os seguintes:
#define TD_OWN (1 << 15) #define TD_ERR (1 << 14) #define TD_AFCS (1 << 13) #define TD_NOINTR (1 << 13) #define TD_MORE (1 << 12) #define TD_ONE (1 << 11) #define TD_DEF (1 << 10) #define TD_STP (1 << 9) #define TD_ENP (1 << 8)
Quando um pacote é enviado, é necessário definir 15 (TD_OWN), 9 (TD_STP) e 8 (TD_ENP) de acordo. O bit 8 significa que este é o último pacote a ser processado; portanto, se um pacote for enviado, você precisará instalar apenas o último.
Também esqueci um recurso importante, o tamanho do buffer nos descritores é escrito com um sinal de menos, provavelmente em código adicional. Mesmo em little-endian, mas como a Elbrus tem a mesma ordem de bytes, isso provavelmente não é importante.
Registo de gestão
Agora descrevemos o último registro desmontado L_E1000_E_CSR:
#define E_CSR_ATME (1 << 24) #define E_CSR_TMCE (1 << 23) #define E_CSR_DRIN (1 << 22) #define E_CSR_DTIN (1 << 21) #define E_CSR_ESLE (1 << 20) #define E_CSR_SLVE (1 << 19) #define E_CSR_PSFI (1 << 18) #define E_CSR_SINT (1 << 16) #define E_CSR_ERR (1 << 15) #define E_CSR_BABL (1 << 14) #define E_CSR_CERR (1 << 13) #define E_CSR_MISS (1 << 12) #define E_CSR_MERR (1 << 11) #define E_CSR_RINT (1 << 10) #define E_CSR_TINT (1 << 9) #define E_CSR_IDON (1 << 8) #define E_CSR_INTR (1 << 7) #define E_CSR_INEA (1 << 6) #define E_CSR_RXON (1 << 5) #define E_CSR_TXON (1 << 4) #define E_CSR_TDMD (1 << 3) #define E_CSR_STOP (1 << 2) #define E_CSR_STRT (1 << 1) #define E_CSR_INIT (1 << 0)
Inicialização
Há uma sequência de inicialização um tanto incomum:
PARAR-> INIT-> IDON-> STRT
Nesse caso, os bits RXON e TXON aumentam independentemente.
Mais detalhes podem ser encontrados em nosso driver.
Placa de vídeo
Como já observado, nosso dispositivo usa um vidyah da Silicon Motion chamado SM718 LynxSE +. Portanto, tudo é simples, existem
fontes de driver no Linux e não há nada para descrever realmente.
Bem, exceto pelo fato de o vídeo mostrar que ficou muito baixo em fps, parece um acesso lento à memória. Mas isso não tem otimização do compilador e, em geral, talvez esse seja o nosso problema associado ao uso incorreto da arquitetura e2k.
Bem, o que mais dizer sobre Sakhalin Elbrus?
Em princípio, o tempo está normal :)
Aparentemente, Elbrus existe, trabalha. Pessoalmente, vejo o principal problema do desenvolvimento dessa arquitetura interessante como sua proximidade. É difícil acreditar que uma empresa relativamente pequena possa criar um processador, um compilador, fornecer suporte e tudo mais. Sim, começaram a aparecer desenvolvedores de software de terceiros, o mesmo Basalt-SPO suporta o
Alt-Linux, que pode ser instalado no Elbrus .
Sim, houve relatos de que desenvolvedores de terceiros estão fabricando hardware baseado no processador Elbrus, por exemplo,
Fastwel . Mas todos esses são apenas pequenos avanços em direção à abertura. Um exemplo muito simples, para reproduzir o que dissemos e mostramos aqui, precisamos de um compilador, e somente o
MCST o possui , as informações fornecidas no artigo são um complemento às informações recebidas do
MCST , e ainda não digo que é improvável que um pedaço de ferro seja encontrado mesmo no MCST. É bastante antigo e o
ICST oferece modelos mais recentes.
PS Naturalmente, você pode ver tudo no
repositório Embox .
PPS Venha para o canal de telegrama russo via Embox (
https://t.me/embox_chat ).
O PPS Embox atualizou o segundo componente da versão, agora o atual
0.4.0