Fix crash with the UBC handler + Add VBR space

This commit is contained in:
Yann MAGNIN 2019-12-09 15:57:06 +01:00
parent 44ace2e56a
commit 659f631469
20 changed files with 706 additions and 372 deletions

View File

@ -81,6 +81,9 @@ check:
map:
@ $(OBJDUMP) -D $(DEBUG)/$(NAME).elf | less
bin:
@ cat $(MEMORY_MAP) | less
##---
## Automated rules

View File

@ -21,7 +21,8 @@ typedef struct ubc_session_s
uint32_t cursor;
} context;
struct {
uint32_t cursor;
int32_t vcursor;
int32_t hcursor;
} disassembly;
} menu;
struct ubc_context_s *context;

View File

@ -15,7 +15,10 @@ struct opcode_s
uint16_t mask;
uint16_t code;
uint16_t arg_mask[ARGUMENTS_MAX];
void (*special)(char *buffer, struct opcode_s);
void (*special)(char *buffer, uint16_t *area);
};
// Opcode list.
extern const struct opcode_s opcode_list[];
#endif /*__OPCODE_H__*/

View File

@ -26,6 +26,7 @@
extern void dclear(void);
extern void dprint(int x, int y, char const *str, ...);
extern void dascii(int x, int y, char const c);
extern void dreverse(int x, int y, int width, int height);
extern void dscroll(int line);
extern void dupdate(void);

View File

@ -4,6 +4,7 @@
#include <kernel/devices/ubc.h>
#include <kernel/syscall.h>
#include <kernel/types.h>
#include <kernel/extra.h>
#include <lib/display.h>
#include <lib/string.h>
@ -19,18 +20,18 @@ extern uint32_t sdata;
extern uint32_t bvhex_ram;
extern uint32_t bvhex_rom;
extern uint32_t svhex;
extern uint32_t btest_ram;
extern uint32_t btest_rom;
extern uint32_t stest;
extern uint32_t bubc_ram;
extern uint32_t bubc_rom;
extern uint32_t subc;
// Internal functions.
extern void section_wipe(uint32_t *section, size_t size);
extern void section_load(uint32_t *dest, uint32_t *src, size_t size);
extern void ubc_handler(void);
extern mpu_t mpu_get(void);
extern void ubc_handler_pre(void);
extern void test(void);
extern int main(void);
__attribute__((section(".pretext")))
int start(void)
{
@ -39,13 +40,32 @@ int start(void)
// Wipe .bss section and dump .data / Vhex sections
memset(&bbss, 0x00, (size_t)&sbss);
memcpy(&bubc_ram, &bubc_rom, (size_t)&subc);
memcpy(&bdata_ram, &bdata_rom, (size_t)&sdata);
//memcpy(&bvhex_ram, &bvhex_rom, (size_t)&svhex);
//memcpy(&btest_ram, &btest_rom, (size_t)&stest);
memcpy(&bvhex_ram, &bvhex_rom, (size_t)&svhex);
// Get Casio's VRAM address.
// Get Casio's VRAM
display_open();
/* volatile uint32_t *bite = (void*)0xe5200000;
dclear();
dprint(0, 0, "UBC = %p", &ubc_handler_pre);
dprint(0, 1, "TEST = %#x", *bite);
dprint(0, 2, "TEST = %#x", *(uint32_t*)&bubc_rom);
dprint(0, 3, "TEST = %#x", (uint32_t)&bubc_ram);
dprint(0, 4, "TEST = %#x", (uint32_t)&bubc_rom);
dprint(0, 5, "TEST = %#x", (size_t)&subc);
dupdate();
while (1);*/
// Check MPU hardware.
current_mpu = mpu_get();
if (current_mpu != MPU_SH7305)
{
return (0);
}
// Open User Break Controller.
// @note:
// This function is hardcoded to follow syscall

View File

@ -10,6 +10,16 @@
extern void menu_context(ubc_session_t *session);
extern void menu_disassembly(ubc_session_t *session);
void ubc_module_handler(int action)
{
/* dclear();
dprint(0, 0, "spc = %p", action);
dupdate();
for (int i = 0 ; i < 4000000 ; i = i + 1);
*/ //TODO stop all clock / timer.
}
void ubc_handler(struct ubc_context_s *context, int channel)
{
void (*menu)(ubc_session_t *session);
@ -20,7 +30,8 @@ void ubc_handler(struct ubc_context_s *context, int channel)
session.channel = channel;
session.context = context;
session.menu.context.cursor = 0;
session.menu.disassembly.cursor = 0;
session.menu.disassembly.vcursor = -(DISPLAY_VLINES_MAX >> 1);
session.menu.disassembly.hcursor = 0;
// Initialize menu function.
menu = &menu_disassembly;
@ -36,4 +47,11 @@ void ubc_handler(struct ubc_context_s *context, int channel)
case KEY_CTRL_F2: menu = &menu_context; break;
}
}
// Update UBC
SH7305_UBC.CBR0.CE = 0; // Disable channel.
SH7305_UBC.CAR0 = context->spc; // Update break address.
SH7305_UBC.CAMR0 = 0x00000000; // Update break address.
SH7305_UBC.CBR0.CE = 1; // Disable channel.
icbi((void*)0xa0000000);
}

View File

@ -1,34 +1,32 @@
.section ".ubc.handler", "awx", @progbits
.global _ubc_handler_pre
.type _ubc_handler_pre, @function
.extern _ubc_handler
.extern _ubc_module_handler
.text
.align 2
_ubc_handler_pre:
! Stack management.
stc.l spc, @-r15 ! Save SPC regsiter.
stc.l ssr, @-r15 ! Save SSR regsiter.
.word 0b0100111100110010 ! Save SGR register (save r15 address befor break) "stc.l sgr, @-r15"
mov.l r8, @-r15 ! Save r8 register.
mov.l r9, @-r15 ! Save r9 register.
mov.l r8, @-r15 ! Save r8 register.
sts.l pr, @-r15 ! Save pr regsiter.
mov r15, r0 ! Save stack address. (used for UBC context)
! Generate UBC context
! Generate "programe" context (used by the UBC handler)
stc.l spc, @-r15 ! Get SPC register.
stc.l ssr, @-r15 ! Get SSR register.
sts.l mach, @-r15 ! Get MACH register.
sts.l macl, @-r15 ! Get MACL register.
stc.l gbr, @-r15 ! Get GBR register.
.word 0b0100111100110010 ! Get SGR register (save r15 address befor break) "stc.l sgr, @-r15"
mov.l r14, @-r15 ! Get r14 register.
mov.l r13, @-r15 ! Get r13 register.
mov.l r12, @-r15 ! Get r12 register.
mov.l r10, @-r15 ! Get r10 register.
mov.l r11, @-r15 ! Get r11 register.
mov.l r9, @-r15 ! Get r9 register.
mov.l r14, @-r15 ! Get "program" r14 register.
mov.l r13, @-r15 ! Get "program" r13 register.
mov.l r12, @-r15 ! Get "program" r12 register.
mov.l r11, @-r15 ! Get "program" r11 register.
mov.l r10, @-r15 ! Get "program" r10 register.
mov.l r9, @-r15 ! Get "program" r9 register.
mov.l r8, @-r15 ! Get "program" r8 register.
stc.l R7_BANK, @-r15 ! Get "program" r7 regsiter.
stc.l R6_BANK, @-r15 ! Get "program" r6 regsiter.
@ -39,49 +37,79 @@ _ubc_handler_pre:
stc.l R1_BANK, @-r15 ! Get "program" r1 regsiter.
stc.l R0_BANK, @-r15 ! Get "program" r0 regsiter.
! Save stack address.
mov r0, r8 ! Save "original" stack address.
! We need to stop (and save) all clocks / timers
! before do any job.
mov.l .ubc_module_handler, r0 ! Get high level abstraction for handle hardware module.
jsr @r0 ! call ubc_module(MODULE_STOP)
mov #0, r4 ! (db) r4 = MODULE_STOP
! Get which channel is trigger and clear interrupt Flags.
mov.l .ubc_ccmfr, r0 ! Get UBC.CCMFR register
mov.l @r0, r10 ! r10 = UBC.CCMFR. (save register)
mov.l @r0, r9 ! r5 = UBC.CCMFR. (save register)
mov #0, r1 ! r2 = 0x00000000 (clear flags)
mov.l r1, @r0 ! Clear UBC.CCMFR.MF1 = 0 and UBC.CCMFR.MF1 = 0
mov.l .icbi_addr, r2 ! Get P2 area for ICBI instruction.
.word 0b0000001011100011 ! SH4 instruction "icbi @r2"
! Allow / unblock interrupt and switch register bank !
stc sr, r9 ! Save SR register.
mov r9, r1 ! Get SR register.
stc sr, r8 ! Save SR register.
mov r8, r1 ! Get SR register.
mov.l .sr_mask, r0 ! Get SR mask for SR.BL, SR.IMASK and SR.RB
and r0, r1 ! SR.BL = 0, SR.IMASK = 0b0000 and SR.RB = 0
ldc r1, sr ! Update SR register.
! Call high-level abstraction
mov r15, r4 ! Send UBC context object to the abstraction.
mov r10, r5 ! Send which channel is trigger.
mov r9, r5 ! Send which channel is trigger.
mov.l .ubc_handler, r0 ! Get high-level abstraction address
jsr @r0 ! Jump into it.
nop ! (db) nop.
! Block interrupt
ldc r9, sr ! Restore SR register (with SR.BL = 1 and SR.IMASK = 0b1111)
! Block interrupt and switch
! register bank
ldc r8, sr ! Restore SR register (with SR.BL = 1, SR.IMASK = 0b1111, SR.RB = 1)
! Clean exit.
mov r8, r15 ! Restore stack space.
lds.l @r15+, pr ! Restor PR register.
mov.l @r15+, r9 ! Restore r9 register.
mov.l @r15+, r8 ! Restore r8 register.
! Restore "program" context.
ldc.l @r15+, R0_BANK ! Restore "program" r0 regsiter.
ldc.l @r15+, R1_BANK ! Restore "program" r1 regsiter.
ldc.l @r15+, R2_BANK ! Restore "program" r2 regsiter.
ldc.l @r15+, R3_BANK ! Restore "program" r3 regsiter.
ldc.l @r15+, R4_BANK ! Restore "program" r4 regsiter.
ldc.l @r15+, R5_BANK ! Restore "program" r5 regsiter.
ldc.l @r15+, R6_BANK ! Restore "program" r6 regsiter.
ldc.l @r15+, R7_BANK ! Restore "program" r7 regsiter.
mov.l @r15+, r8 ! Restore "program" r8 regsiter.
mov.l @r15+, r9 ! Restore "program" r9 regsiter.
mov.l @r15+, r10 ! Restore "program" r10 regsiter.
mov.l @r15+, r11 ! Restore "program" r11 regsiter.
mov.l @r15+, r12 ! Restore "program" r12 regsiter.
mov.l @r15+, r13 ! Restore "program" r13 regsiter.
mov.l @r15+, r14 ! Restore "program" r14 regsiter.
.word 0b0100111100110110 ! Restore SGR regsiter. "ldc.l @r15+, sgr"
ldc.l @r15+, gbr ! Get GBR register.
lds.l @r15+, macl ! Get MACL register.
lds.l @r15+, mach ! Get MACH register.
ldc.l @r15+, ssr ! Restore SSR regsiter.
ldc.l @r15+, spc ! Restore SPC regsiter.
! Retore and restart clock / timers
stc spc, r4 ! (db) r4 = MODULE_START
mov.l .ubc_module_handler, r0 ! Get high level abstraction for handle hardware module.
jsr @r0 ! call ubc_module(MODULE_START)
nop
! Clean exit.
lds.l @r15+, pr ! Restor PR register.
mov.l @r15+, r8 ! Restore r8 register.
mov.l @r15+, r9 ! Restore r8 register.
rte ! Interrupt Exit.
nop ! (db) Safety first.
.align 4
.ubc_handler: .long _ubc_handler
.ubc_ccmfr: .long 0xff200600
.icbi_addr: .long 0xa0000000
.sr_mask: .long ~(0x300000f0)
.ubc_handler: .long _ubc_handler
.ubc_module_handler: .long _ubc_module_handler
.ubc_ccmfr: .long 0xff200600
.icbi_addr: .long 0xa0000000
.sr_mask: .long ~(0x300000f0)
.end

View File

@ -1,10 +1,110 @@
#include <kernel/devices/ubc.h>
#include <kernel/opcode.h>
#include <kernel/keybios.h>
#include <lib/display.h>
#include <lib/stdio.h>
static uint32_t get_arg(uint16_t code, const struct opcode_s *opcode, int id)
{
int shift;
// Check arg mask
if (opcode->arg_mask[id] == 0x0000)
return (0);
// Get arg shift.
shift = -1;
while (++shift < 16 && !(opcode->arg_mask[id] & (0x01 << shift)));
// Get argument.
return ((code & opcode->arg_mask[id]) >> shift);
}
static void display_mnemonic(ubc_session_t *session)
{
char line[128];
uint16_t *area;
int i;
int j;
// Get starting area.
// TODO: update me !!
area = (void *)(session->context->spc + (session->menu.disassembly.vcursor << 1));
// Main Loop.
i = -1;
while (++i < DISPLAY_VLINES_MAX)
{
// Generate first part.
sprintf(line, "%8x %4x ", &area[i], area[i]);
// Try to find opcode.
j = -1;
while (opcode_list[++j].name != NULL)
{
// Check opcode.
if ((area[i] & opcode_list[j].mask) != opcode_list[j].code)
continue;
// Generate line via special function.
if (opcode_list[j].special != NULL)
{
opcode_list[j].special(&line[14], &area[i]);
break;
}
// Generate common line.
sprintf(&line[14],
opcode_list[j].name,
get_arg(area[i], &opcode_list[j], 0),
get_arg(area[i], &opcode_list[j], 1),
get_arg(area[i], &opcode_list[j], 2)
);
break;
}
// If no opcode are found, generate "empty" line.
if (opcode_list[j].name == NULL)
sprintf(&line[14], ".word 0x%4x", area[i]);
// Display line !
dprint(session->menu.disassembly.hcursor, i, line);
// Highlight break line if needed.
if ((uint32_t)&area[i] == session->context->spc)
{
dreverse(
0,
i * (KERNEL_FONT_REAL_HEIGHT + 1),
DISPLAY_SCREEN_WIDTH,
KERNEL_FONT_REAL_HEIGHT + 1
);
}
}
}
static void cursor_update(ubc_session_t *session)
{
// Horizontal update.
if (session->key == KEY_CTRL_LEFT)
session->menu.disassembly.hcursor += 1;
if (session->key == KEY_CTRL_RIGHT)
session->menu.disassembly.hcursor -= 1;
// Vertical update.
if (session->key == KEY_CTRL_UP)
session->menu.disassembly.vcursor -= 1;
if (session->key == KEY_CTRL_DOWN)
session->menu.disassembly.vcursor += 1;
}
void menu_disassembly(ubc_session_t *session)
{
// test
// Update cursor position.
cursor_update(session);
// display ASM.
dclear();
dprint(0, 0, "Disassembly Menu !!");
display_mnemonic(session);
dupdate();
}

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@ int ubc_open(void)
casio_dbr = dbr_set(&ubc_handler_pre);
// Setup Channel 0.
SH7305_UBC.CRR0.PCB = 0; // Set PC break before instruction break.
SH7305_UBC.CRR0.PCB = 1; // Set PC break adter instruction break.
SH7305_UBC.CRR0.BIE = 1; // Request a Break.
SH7305_UBC.CBR0.MFE = 0; // Enable Match Flag.

View File

@ -0,0 +1,38 @@
#include <lib/display.h>
__attribute__((section(".vhex.exception"), interrupt_handler))
void exception_handler(void)
{
uint32_t spc;
uint32_t ssr;
uint32_t sr;
// Get some registers's data.
__asm__ volatile (
"stc spc, %0;"
"stc ssr, %1;"
"stc sr, %2"
: "=r"(spc), "=r"(ssr), "=r"(sr)
:
:
);
// Write exception informations.
dclear();
dprint(0, 0,
"Ho crap ! Exception !\n"
"tra: %#x\n"
"expevt: %#x\n"
"spc: %#x\n"
"ssr: %#x\n"
"sr: %#x",
*((uint32_t *)0xff000020),
*((uint32_t *)0xff000024),
spc,
ssr,
sr
);
dupdate();
while (1);
}

View File

@ -0,0 +1,10 @@
#include <lib/display.h>
__attribute__((section(".vhex.interrupt"), interrupt_handler))
void interrupt_handler(void)
{
dclear();
dprint(0, 0, "Interrupt handler (%#x)\n", *(uint32_t*)0xff000028);
dupdate();
while (1);
}

38
src/kernel/vbr/tlb.c Normal file
View File

@ -0,0 +1,38 @@
#include <lib/display.h>
__attribute__((section(".glados.tlb"), interrupt_handler))
void tlb_handler(void)
{
uint32_t spc;
uint32_t ssr;
uint32_t sr;
// Get some registers's data.
__asm__ volatile (
"stc spc, %0;"
"stc ssr, %1;"
"stc sr, %2"
: "=r"(spc), "=r"(ssr), "=r"(sr)
:
:
);
// Write exception informations.
dclear();
dprint(0, 0,
"Ho crap ! Exception !\n"
"tra: %#x\n"
"expevt: %#x\n"
"spc: %#x\n"
"ssr: %#x\n"
"sr: %#x",
*((uint32_t *)0xff000020),
*((uint32_t *)0xff000024),
spc,
ssr,
sr
);
dupdate();
while (1);
}

View File

@ -6,6 +6,7 @@ void dprint(int x, int y, char const *str, ...)
{
char buffer[512];
int default_pos_x;
int starting_x;
va_list ap;
int i;
@ -16,6 +17,7 @@ void dprint(int x, int y, char const *str, ...)
va_end(ap);
i = -1;
starting_x = x;
x = x * (KERNEL_FONT_REAL_WIDTH + 1);
y = y * (KERNEL_FONT_REAL_HEIGHT + 1);
default_pos_x = x;
@ -27,6 +29,12 @@ void dprint(int x, int y, char const *str, ...)
x = default_pos_x;
continue;
}
if (buffer[i] == '\t')
{
x = x / (KERNEL_FONT_REAL_WIDTH + 1);
x = (x + (4 - ((x - starting_x) & 3))) * (KERNEL_FONT_REAL_WIDTH + 1);
continue;
}
dascii(x, y, buffer[i]);
x = x + KERNEL_FONT_REAL_WIDTH + 1;
}

View File

@ -0,0 +1,50 @@
#include <lib/display.h>
void dreverse(int x, int y, int width, int height)
{
int vram_offset_y;
int j;
// Check error.
if (width < 0 || height < 0)
return;
// Get "real" X position and area width.
if (x < 0)
{
width = width + x;
x = 0;
} else {
if (x + width >= DISPLAY_SCREEN_WIDTH)
width = DISPLAY_SCREEN_WIDTH - x;
}
// Get "real" Y position and area height.
if (y < 0)
{
height = height + x;
y = 0;
} else {
if (y + height >= DISPLAY_SCREEN_HEIGHT)
height = DISPLAY_SCREEN_HEIGHT - x;
}
// Check potential error.
// @note we do not check height because the while()
// while do the job for us.
if (width < 0)
return;
// Generate VRAM offset for Y axis.
vram_offset_y = (y + height - 1) << 4;
while (--height >= 0)
{
j = width + x;
while (--j >= x)
{
vram[(j >> 3) + vram_offset_y] ^= 0x80 >> (j & 7);
}
vram_offset_y = vram_offset_y - 16;
}
}

View File

@ -1,38 +1,36 @@
//#include <lib/stdio.h>
//#include <lib/display.h>
//#include <kernel/tty.h>
/*#include <lib/stdio.h>
#include <lib/display.h>
/*int main(void)
// TODO: remove me !!
extern void test(void);
int main(void)
{
char *line;
// Open TTY and display entry message
tty_open();
tty_write("Welcome to Vhex !\n");
dclear();
dprint(0, 0, "Boot complete !");
dupdate();
while (1);
// UBC test !
// Open User Break Controller.
// @note:
// This function is hardcoded to follow syscall
// execution based on userland vitural address
// 0x08100000, reserved for add-in RAM.
// The tested programe is staticaly linked by the
// linker script.
// This module is only on SH7305 - SH4 based MPU.
// THis function SHOULD not be called !
ubc_open();
// Main loop
while (1)
{
// Write input indications.
// TODO: write current working directory ?
tty_write(">");
// Jump into the tested function.
// @note:
// Thus USC should be start after the jump.
//
//((void(*)(void))0x08100000)();
//((void(*)(void))&vhex_dbr)();
test();
// Get each line writen by the user.
nbread = getline(&line, &size, 0);
if (nbread == EOF)
{
// TODO: check jobs ?
break;
}
// TODO: do some job.
}
// Close TTY and return.
tty_close();
return (0);
// Power OFF UBC module.
ubc_close();
}*/

View File

@ -1,8 +1,8 @@
#include <kernel/syscall.h>
/*#include <kernel/syscall.h>
void test(void)
{
uint8_t *vram;
vram = casio_Bdisp_GetVRAM();
}
}*/

23
src/user/test_asm.s Normal file
View File

@ -0,0 +1,23 @@
.global _test
.type _test, @function
.align 2
_test:
sts.l pr, @-r15
mov.l .syscall_vram, r0
mov.l .syscall_entry, r1
jsr @r1
nop
tst r0, r0
bf _test
nop
lds.l @r15+, pr
rts
nop
.align 4
.syscall_entry: .long 0xa0010070
.syscall_vram: .long 0x00000135
.end

BIN
vhex.g1a

Binary file not shown.

89
vhex.ld
View File

@ -10,10 +10,18 @@ MEMORY
** This location is realy important because no Casio's OS data
** should be overwrite (specially TLB information stored by Casio).
*/
bootram (rwx) : o = 0x88040000, l = 252k
dbrram (rwx) : o = 0x8807f000, l = 4k
rom (rx) : o = 0x00300200, l = 512k
testram (rwx) : o = 0x08100000, l = 8k
ram (WX) : o = 0x88040000, l = 252k
rom (RX) : o = 0x00300200, l = 512k
ilram (WX) : o = 0xe5200000, l = 4k
}
PHDRS
{
text PT_LOAD ;
data PT_LOAD ;
bss PT_NULL ;
ubc PT_LOAD ;
vbr PT_LOAD ;
}
SECTIONS
@ -29,13 +37,15 @@ SECTIONS
. = ALIGN(4);
*(.rodata)
*(.rodata.*)
} > rom
} > rom : text
/*
** RAM sctions
*/
. = ORIGIN(bootram);
. = ORIGIN(ram);
/* BootStrap Stack: should be wiped later ! */
/* (we force the section to be non loadable when the program is run) */
@ -44,59 +54,25 @@ SECTIONS
*(.bss)
*(COMMON)
. = ALIGN(4);
} > bootram :NONE
} > ram : bss
_sbss = SIZEOF(.bss);
/* Read-write data going to RAM */
.data : ALIGN(4) {
.data ALIGN(4) : ALIGN(4) {
_bdata_rom = LOADADDR(.data) ;
_bdata_ram = . ;
*(.data)
. = ALIGN(4);
} > bootram AT> rom
} > ram AT> rom : data
_sdata = SIZEOF(.data);
/*
** TEST space !
*/
/*. = ORIGIN(testram);
.test : ALIGN(4) {
_btest_rom = LOADADDR(.test) ;
_btest_ram = . ;
*(.test.prgm)
. = ALIGN(4);
} > testram AT> rom
_stest = SIZEOF(.test);
*/
/*
** DBR space !
*/
/*. = ORIGIN(testram);
.vhex : ALIGN(4) {
_bvhex_rom = LOADADDR(.vhex) ;
_bvhex_ram = . ;
_vhex_dbr = . ;
*(.vhex.handler)
. = ALIGN(4);
} > osram AT> rom
_svhex = SIZEOF(.vhex);
*/
/*
** VBR space !
*/
/* Interrupt / exception handlers */
/*.vhex : SUBALIGN(4) {
.vhex ALIGN(4) : SUBALIGN(4) {
_bvhex_rom = LOADADDR(.vhex) ;
_bvhex_ram = . ;
_vhex_vbr = . - 0x100;
@ -106,8 +82,29 @@ SECTIONS
. = _vhex_vbr + 0x600 ;
*(.vhex.interrupt) ;
. = ALIGN(4);
} > osram AT> rom
_svhex = SIZEOF(.vhex);*/
} > ram AT> rom : vbr
_svhex = SIZEOF(.vhex);
/*
** DBR space !
*/
. = ORIGIN(ilram);
.ubc ALIGN(4) : ALIGN(4) {
_bubc_rom = LOADADDR(.ubc) ;
_bubc_ram = . ;
*(.ubc.handler)
*(.ubc)
. = ALIGN(4);
} > ilram AT> rom : ubc
_subc = SIZEOF(.ubc);
/* unwanted section */
/DISCARD/ : {